C++:征服C指针:指针(一)

news2024/11/26 8:40:59

关于指针

  • 1.看一个简单的程序,来接触下指针
  • 2. 常见疑问:指针就是地址,那么int的指针和double的指针有什么区别 了
  • 3. 常见疑问:指针运算
  • 4. 为什么存在奇怪的指针运算符
  • 5. 试图将数组作为函数的参数进行传递。
  • 6. 什么是空指针
    • 5.1 声明函数形参的方法
  • 6. 指向函数的指针
  • 7. 什么是指向数组的指针
    • 7.1 将数组类型解读成指针
  • 8. const 修饰指针(常量指针/指针常量)
  • 9. typedef 给指针定义别名
  • 10. 指针和字符串常量
  • 11. 关于指向函数指针引起的混乱
  • 附录:代码示例

本章节,我们重点分析C指针,首先我们从指针的概念谈起
💚💚💚

  1. 指针是一种保存变量地址的变量,并在C中频繁使用
  2. 在C语言标准中:最初出现指针,也有这样一段话
    🧡指针类型(pointer type)可由:函数类型,对象类型或不完全的类型派生,派生指针类型的类型称为引用类型。
    🧡指针类型,描述一个对象,该类对象的值提供对该引用类型实体的引用。由引用类型T 派生的指针类型有时称为 “指向T的指针”
    从引用类型构造指针类型的过程称为“指针类型的派生”。
    🧡 如由 int 类型派生的指针,称为 “指向int 类型的指针”。

下面,我们对指针的研究,主要从如下几个方面展开。
3. 指针类型
4. 指针类型变量
5. 指针类型的值(即内存地址)

1.看一个简单的程序,来接触下指针

#include<iostream>
using namespace std;
int main()
{
	int hoge = 5;
	int piyo = 10;
	int* hoge_p;
	
	// 输出每个变量的地址
	printf("&hoge...%p\n",&hoge);
	printf("&piyo...%p\n",&piyo);
	printf("&hoge_p...%p\n",&hoge_p);
	
	// 将 hoge的内存地址赋值给 hoge_p
	hoge_p = &hoge;
	// 打印指针变量的值
	printf("hoge_p...%p\n",hoge_p);
	
	// 通过hoge_p输出 hoge的内容
	printf("*hoge_p...%d\n",*hoge_p);
	
	// 通过 hoge_p修改 hoge的内容
	*hoge_p = 10;
	printf("*hoge...%d\n",hoge);
	return 0;
}

// 打印内容
&hoge...000000000061fe1c
&piyo...000000000061fe18
&hoge_p...000000000061fe10

hoge_p...000000000061fe1c
*hoge_p...5
*hoge...10

我们用一个图来说明下。
在这里插入图片描述

  1. 指针变量 hoge_p保存了另外一个变量 hoge地址,我们认为 “hoge_p” 指向 “hoge”。
  2. 对 hoge变量实施 &运算符得到 “hoge地址” 。 有时也称:"hoge的地址"的为 “指向 hoge的指针” (实际上这里的指针指:指针类型的值)
  3. 在 指针前面加上 * 可以表示指针指向的变量。(hoge_p 指向hoge), 所以 , *hoge_p 等同于 hoge , 那么一旦要求输出 *hoge_p 就会输出 hoge保存的值 。

🧡🧡🧡 要点

  1. 对变量使用 & 运算符,可以取得该变量的地址。 这个地址称为指向 该变量的指针。
  2. 指针变量 hoge_p 保存了指向其他变量的地址情况下(如保存 hoge 地址)可以说 “hoge_p 指向 hoge” 。
  3. 对指针变量运算 *运算符,就等同于它指向的变量。(如 hoge_p指向 hoge, *hoge_p就等同于 hoge)。

2. 常见疑问:指针就是地址,那么int的指针和double的指针有什么区别 了

  1. 如果从指针变量的角度说,指向这两个类型的指针没有区别,都保持相同的表现形式,与指针类型无关。
    💚(尽管C标准没有规定所有数据类型的长度,但通常是这样一种情况:整数类型指针长度是一样的, char 类型指针和 结构体指针 长度一致,函数指针长度与数据指针长度不同)
    💚 指针长度取决于使用的机器和编译器,通常在 现代 windows上,指针是32 位或64位长,对于DOS来说是 16位长。
  2. 从指针运算的角度来说,就需要关注指针类型了
    💚(如:对指针加 N 即 :指针前进 “当前指针指向的数据类型的长度 * N”)
  3. 指向任何类型的指针类型 ------ void* 类型
void test_voidPointer()
{
	int hoge = 5;
	void* hoge_p;
	// void 类型指针保存 hoge内存地址,这个是没问题的
	hoge_p = &hoge;
	
	// 但是由于编译器并不知道 hoge_p 指针类型,仅仅知道内存地址,不知道保存数据的类型,这样是不能取值的。
	printf("%d", *hoge_p);  // error :'void*' is not a pointer-to-object type
}

💚💚💚 改正

void test_pointer_02()
{
	int hoge = 5;
	void* hoge_p;
	// void 类型指针保存 hoge内存地址,这个是没问题的
	hoge_p = &hoge;
	
	// 将指针强制转换成 指向 int 类型指针,这样编译器就指针取出来的是 int 类型的值
	printf(" %d",*(int*)hoge_p);  // 打印5
}

3. 常见疑问:指针运算

  1. C指针运算是其他语言没有的
  2. 指针运算是针对指针进行整数加减运算
// 指针的运算
void test_pointer_03(){
	int hoge;
	int *hoge_p;
	// 将指向 hoge的指针赋予 hoge_p
	hoge_p = &hoge;
	// 输出 hoge_p的值
	printf("hoge_p...%p\n",hoge_p);
	// 给 hoge_p 加1
	hoge_p++;
	// 输出 hoge_p 的值
	printf("hoge_p...%p\n",hoge_p);
	// 输出 hoge_p后 加3 的值
	printf("hoge_p....%p\n",hoge_p +3);
}
// 打印结果
hoge_p...000000000061fde4
hoge_p...000000000061fde8
hoge_p....000000000061fdf4

🧡从上面的打印可知:指针加1 ,前进的字节是4个。这个结论和简单,也很直观。但是,我想通过:指针和数组的关系来证明下这个结论。请看下面示例

// 指针和数组之间关系
void test_pointer_04()
{
	int array[5];
	int *p;
	int i;
	// 给数组 array 的各元素设定值
	for(i=0; i<5;i++)
	{
		array[i] = i;
	}
	// 输出数组各元素的值(指针版本)
	for(p = &array[0]; p != &array[5]; p++)
	{
		printf("%d\n",*p);
	}
}

// 打印结果
0
1
2
3
4	

在这里插入图片描述
💚💚💚

4. 为什么存在奇怪的指针运算符

在第三章节中,我们知道访问数组的内容,老老实实用下标就可以了,为什么C语言需要存在指针运算符这样奇怪的功能了 ?

  1. C继承了 早期的 B 语言影响
  2. 使用指针运算可以写出高效的程序。
    在这里插入图片描述
    ▲●通过角标的方式访问数组,array[i] 在循环中会出现多次,每次都要进行相当于 *(array + i) 的加法运算,效率自然是比较低的
    ▲●但是通过 p+i 的方式,加法运算只有在循环结束的时候执行一次。

💚 如今,编译器在不断被优化,对于循环内部重复的表达式会集中处理,是编译器优化的基本内容,对于现在一般的C编译器,无论你使用的数组角标还是指针来访问 数组元素,效率上都不会出现明显的差距,基本删都是输出完整的机器码。

5. 试图将数组作为函数的参数进行传递。

从上几章节我们知道:
**💚 数组 buf[ ] 可以解读成 :

  1. 指向它的初始元素的指针
  2. *buf[ len] 是 (buf + len) 的语法糖
    下面,我们来看一个实用性的例子:从英文的文本中将单词 一个一个取出来 。
// 试图将数组作为函数参数来传递
void test_pointer_05(char* buf, int buf_size)
{
	
	
}
int main()
{
    char buf[256];
	test_pointer_05(buf,256);
	return 0;
}

6. 什么是空指针

💚 空指针是一个特殊的值

  1. 空指针是指可以确保没有指向任何一个对象的指针,通常使用 宏定义 NULL 来表示空指针常量值
  2. 空指针确保它和任何非空指针进行比较都不会相等,因此经常作为函数发生异常时的返回值使用
  3. 在现今的操作系统下,应用程序一旦视图通过空指针引用对象,就会马上招致一个异常并且当前应用程序会被操作系统强制终止

💚 常量0 和 NULL 的关系 。
4. 在C中,在为0的地址上,是不能保存数据的,放什么都不能起作用。所以标准运行 将 NULL 定义成 ( void*)0

int* p = 3; // error :invalid conversion from ‘int’ to ‘int*’
这是因为:编译器会认为 3 是int 类型,但是 p 指针 int* 指针类型,int类型和 指针类型肯定是有区别的

int* p1 = 0; // OK
std::cout <<*p1; // Segmentation fault
这是因为编译器根据上下文 “将常量0应该作为指针使用” , 这个时候编译是可以通过的,但是在运行时,获取指针类型保存的地址指向的值 是无效的,所以会出现 Segmentation fault

5.1 声明函数形参的方法

6. 指向函数的指针

函数在表达式中被解读成 “指向函数的指针” , 它本质上也是指针(地址) ,所以可以将它赋给指针型变量 。

下面我们用一个图分析下,指向函数的 指针和 指向函数的指针的数组。

在这里插入图片描述

7. 什么是指向数组的指针

  1. “数组”和“指针”都是派生类型,他们都是由基本类型开始重复派生生成的。也就是说派生出“数组”后,再派生出“指针”,就可以生成“指向数组的指针”。
  2. 我们需要区分:指向数组的指针和 指针数组初始元素的指针
// 指向数组的指针
void test_pointer_06()
{
	// array_p 是指向int 数组(元素个数为3个)的指针
	int (*array_p)[3]; 
	// p_array 是指向数组初始元素的指针
	int array[4];
	int *p_array = &array[0];
}

7.1 将数组类型解读成指针

💚 单目运算符& 被称为地址运算符 : &将一个左值作为操作数,返回指向该左值的指针
💚 单目运算符* 被称为解引用:将指针作为操作数,返回指针所指向的对象或函数。
💚 ->运算符:此运算符没有明确的定义,但可以将其称为“箭头运算符”,通过指针访问结构体成员时候,就会使用 ->运算符
💚 [ ] 下标运算符:在C语言中,遇导下标运算符[ ] 可以将元素个数省略不写,但是不同 编译器针对不同的情况,有不同的解释。

void test(){
	int hoge[10]{1,2,3,4,5,6,7,8,9,10}; // 这是一个数组
	// & 运算符
	int *p = &hoge[0]; // 将数组解读成指针
	// * 运算符
	int hoge_first = *p;  // 将指针解读成数组的值
	cout << hoge_first;  // 1
	
	// [] 小标运算符
	int hoge_second = hoge[1];
	int hoge_third = *(p+2);
	cout << "\n" << hoge_second;
	cout << "\n" << hoge_third;
	
	// ->运算符
	hoge_struct* hogeStru_p = &hogeStru;
	cout<< "\n"<< hogeStru_p->hoge;
	cout<< "\n"<< (*hogeStru_p).hoge;
}
int main()
{
	test();
	return 1;
}

💚下标运算符使用场景
在这里插入图片描述

8. const 修饰指针(常量指针/指针常量)

9. typedef 给指针定义别名

10. 指针和字符串常量

💚 使用 " " 包围起来的字符串被称为 字符串常量,字符串常量的类型是 " char的数组",因此在表达式中,可以被解读为指针 。

// 声明一个: 指向 char 类型的指针 str
char *str;
// 将 [指向 “abc” 的初始化元素的指针] 赋值给 str
str = "abc";

// note :  针对 字符串常量“abc” ,编译器事实上会将字符分开处理 即:
"abc" -----{'a','b','c','\0'}

11. 关于指向函数指针引起的混乱

💚

附录:代码示例

/**
	1: 指针: 指针类型,指针变量,指针变量的值
	2:指针就是存储的内存地址的
	3:将内存分区和指针结合起来
	4:void 指针,NULL指针
*/


#include<iostream>
using namespace std;

// 指针的基本操作:定义和概念
void test_pointer_01()
{
	
	int hoge = 5;
	int piyo = 10;
	int* hoge_p;
	
	// 输出每个变量的地址
	printf("&hoge...%p\n",&hoge);
	printf("&piyo...%p\n",&piyo);
	printf("&hoge_p...%p\n",&hoge_p);
	
	// 将 hoge的内存地址赋值给 hoge_p
	hoge_p = &hoge;
	// 打印指针变量的值
	printf("hoge_p...%p\n",hoge_p);
	
	// 通过hoge_p输出 hoge的内容
	printf("*hoge_p...%d\n",*hoge_p);
	
	// 通过 hoge_p修改 hoge的内容
	*hoge_p = 10;
	printf("*hoge...%d\n",hoge);
}

// 指针的转换
void test_pointer_02()
{
	int hoge = 5;
	void* hoge_p;
	// void 类型指针保存 hoge内存地址,这个是没问题的
	hoge_p = &hoge;
	
	// 但是由于编译器并不知道 hoge_p 指针类型,仅仅知道内存地址,不知道保存数据的类型,这样是不能取值的。
	// printf("%d",*hoge_p);  // error :invalid operands of types 'const char [4]' and 'void*' to binary 'operator*'
	
	// 将指针强制转换成 指向 int 类型指针
	printf(" %d",*(int*)hoge_p);  // 打印5
}

int main()
{
	test_pointer_02();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/578370.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎样用一周时间研究 ChatGPT

我是怎样用一周时间研究 ChatGPT 的&#xff1f; 上周大概开了 20 多个会&#xff0c;其中有一些是见了觉得今年可能会比较活跃出手的机构&#xff0c;其余见的绝大多数是和 ChatGPT 相关。 我后面就以 ChatGPT 为例&#xff0c;讲下我是如何快速一周 cover 一个赛道的&#x…

GDB 基础使用与多进程调试

​ GDB 全称“GNU symbolic debugger”是 Linux 下常用的程序调试器&#xff0c;当下的 GDB 支持调试多种编程语言编写的程序&#xff0c;包括 C、C、Go、Objective-C、OpenCL、Ada 等。 01 GDB 基础调试 1.1 基础使用 安装工具 # 安装 gcc sudo yum install gcc # 安装 g s…

记录一次el-table动态添加删除列导致表格样式错误(或不聚集)问题

记录一次el-table动态添加删除列导致表格样式错误问题 需求背景出现的问题解决方案理论&#xff1a;在el-table中设置key值&#xff0c;重新赋值表格数据之后&#xff0c;更新key值&#xff0c;达到动态更新效果 需求背景 一个电商类商品管理平台&#xff08;类似shopify产品编…

2023 华为 Datacom-HCIE 真题题库 06--含解析

多项选择 1.[试题编号&#xff1a;190185] &#xff08;多选题&#xff09;如图所示&#xff0c;PE 1和PE2之间通过Loopback0接口建立MP-BGP邻居关系&#xff0c;在配置完成之后&#xff0c;发现CE1和CE2之间无法互相学习路由&#xff0c;以下哪些项会导致该问题出现? A、PE1…

初识linux之简单了解TCP协议与UDP协议

目录 一、理解源IP地址和目的IP地址 二、端口号 1. 为什么要有端口号 2. 理解端口号 3. 源端口号和目的端口号 三、初步了解TCP协议和UDP协议 1. 初步认识TCP协议 2. 初步认识UDP协议 3. 可靠传输与不可靠传输 四、网络字节序 1. 网络字节序的概念 2. 如何形成网络…

python+django电子笔记交易系统vue

编码使用python&#xff08;我的pycharm版本是2021.3.3&#xff09;&#xff0c;数据库使用mysql&#xff08;我的mysql版本5.5&#xff09;。网站点击能够跳转各个页面&#xff0c;不用部署服务器&#xff0c;本地运行即可。 题目&#xff1a;基于django的电子笔记交易系统 功…

并发编程的三大特性之有序性

有序性的概念 Java文件在被cpu执行前会进行编译成cpu可以执行的指令&#xff0c;为了提高cpu的执行效率会对其中的一些语句进行重排序。Java指令最终是乱序执行的目的是为了提高cpu的执行效率&#xff0c;发挥cpu的性能 单例模式由于指令重排可能会出现上述的问题&#xff0…

ASP.NET Core

1. 入口文件 一个应用程序总有一个入口文件&#xff0c;是应用启动代码开始执行的地方&#xff0c;这里往往也会涉及到应用的各种配置。当我们接触到一个新框架的时候&#xff0c;可以从入口文件入手&#xff0c;了解入口文件&#xff0c;能够帮助我们更好地理解应用的相关配置…

SOC与MCU的区别及汽车电子未来发展以及展望

SOC与MCU的区别及汽车电子未来发展以及展望 MCU与SOC的区别 CPU&#xff08;Central Processing Unit&#xff09;&#xff0c;是一台计算机的运算核心和控制核心。CPU由运算器、控制器和寄存器及实现它们之间联系的数据、控制及状态的总线构成。差不多所有的CPU的运作原理可…

【PHP】问题已解决:宝塔面板搭建php网站无法上传图片或是文件(保姆级图文)

目录 问题情况原因和解决方法总结 『PHP』分享PHP环境配置到项目实战个人学习笔记。 欢迎关注 『PHP』 系列&#xff0c;持续更新中 欢迎关注 『PHP』 系列&#xff0c;持续更新中 问题情况 宝塔面板搭建php网站无法上传图片或是文件。 原因和解决方法 检查你的php里是否安装…

老板让你写个PPT没有头绪?没事,ChatGPT来帮你!

文章目录 前言一、先确定写什么——准备内容二、再看看能用吗——自动生成PPT三、最后再改改——看个人喜好写在最后 前言 自从人工智能横空而出&#xff0c;它在人们的生活中产生了巨大的影响。尤其在企业办公领域&#xff0c;借助人工智能的力量&#xff0c;能够迅速产出丰富…

千乎万唤始出来,支持gpt3和gpt4支持画图,的在线gpt应用接入案例开源上线啦

了解OPEN AI 平台用户一直在说&#xff0c;这个接口要怎么对接&#xff0c;如何在体验。 由于我一直忙于接口中台开发&#xff0c;所以在线基于OPEN AI 接口实例例子就一直没有写。现在终于写完了。 基于纯HTMLCSSJS 小白也能轻松上手部署。代码简单清晰。 这里不多做其他赘述…

tensorflow及其keras如何保存模型

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

如何从宏观层面构建优秀的大语言模型

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

高阶python | 装饰器

python版本&#xff1a;3.10.0 在学习装饰器前先了解一下闭包 阿-岳同学【python技巧060】形象理解闭包&#xff0c;玩转闭包 通过视频首先可以了解到主要的三个知识点 闭包是嵌套结构内层函数有调用外层函数的变量为闭包&#xff0c;同时内层函数是闭包函数&#xff08;所…

根据DataFrame1中指定列c1的每个值a1 从DataFrame2中指定列c2中的每个值a2 找到与a1最临近的值a2,进行所在行合并

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 根据DataFrame1中指定列c1的每个值a1 从DataFrame2中指定列c2中的每个值a2 找到与a1最临近的值a2,进行所在行合并 [太阳]选择题 关于以下代码说法错误的是&#xff1a; import pandas as pd…

新手应该如何快速上手MySQL数据库?

前言 数据库是计算机系统中用于存储、管理和检索数据的系统。它允许用户访问并管理数据&#xff0c;并具有可靠、可扩展和高效的特性。 文章目录 前言1. 数据库的相关概念1.1 数据1.2 数据库1.3 数据库管理系统1.4 数据库系统1.5 SQL 2. MySQL数据库2.1 MySQL安装2.2 MySQL配置…

我在CSDN的736个日子——两年纪念日“随想”

2021-05-21~2023-05-27&#xff0c;我在 CSDN 已有 736 个日子。 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那么简单…… 地址&#xff1a;https:/…

设计一:51单片机流水灯控制

目录 一、设计内容 二、硬件电路分析 三、仿真原理图 四、程序设计 五、仿真结果 六、思考题 作者有话说 一、设计内容 本次设计使用4个按键&#xff0c;当KEY1按下时&#xff0c;P0口所接的发光二极管&#xff08;D1~D8&#xff09;以100ms的时间间隔自下至上循环点亮3…

软考A计划-试题模拟含答案解析-卷六

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…