手把手教你深入理解c/c++中的指针

Posted AI算法与图像处理

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手教你深入理解c/c++中的指针相关的知识,希望对你有一定的参考价值。

 
   
   
 

重磅干货,第一时间送达

指针是c语言为什么如此流行的一个重要原因,正是有了指针的存在,才使得c/c++能够可以比使用其他语言编写出更为紧凑和有效的程序,可以说,没有掌握指针,就没有权利说自己会用c/c++.然而。然而对于大多数初学者,面对指针这个概念简直是望而生畏,如果前期指针运用的不熟练,后期编写的程序随时都有可能成为一颗定时炸弹,因此今天我就花点时间给大家解释一下我自己对c/c++中指针的理解。





int a = 10;

上面这行代码我们定义并初始化了这个变量a,系统会为a分配一块内存单元,a只是这块内存单元的别名,在程序中从变量中取值,实际上是通过变量名找到相应的内存单元,从其中读取数据。

手把手教你深入理解c/c++中的指针


例如:


int a = 10;  int* p = &a;  //定义指针变量 p*p = 20; //将指针p指向的值修改为 20



符号
意义
&
取地址
*
解引用


这三行代码的内存模型如下:


手把手教你深入理解c/c++中的指针



由此扩展到二级指针,如果我们再定义一个指针变量q来指向p,那么q就是一个二级指针,因为它指向的对象还是一个指针,只不过比他自己低一级,是一级指针,那么二级指针如何定义呢,请看下面的代码:


int a = 10;int* p = &a;int** q = &p;


上面第三行代码就是定义了一个二级指针q,它指向的是一级指针p,而一级指针p又指向了变量a,它的内存模型如下图所示:


手把手教你深入理解c/c++中的指针



cout <<"a的值为:"<< **q << endl;


我们观察一下输出结果:


手把手教你深入理解c/c++中的指针


没错,输出的结果完全正确。


由此再扩充到多级指针,二级指针是指向一级指针的指针,那么n级指针便是指向n-1级指针的指针,以此类推。


三,常量指针与指针常量


请看下面两行代码:


int a = 10;const int * p1 = &a; //常量指针int * const p2 = &a;    //指针常量


上面第二行代码中的p1是一个常量指针,就是指向常量的指针变量。意味着它指向的值不可以修改,但是指针的指向可以修改:


int a = 10;int b = 20;const int * p1 = &a; //常量指针*p1 = 100;  //错误,常量指针指向的值不可以修改p1 = &b;   //正确


而对于指针常量,它本质是一个常量,但是由指针修饰。意味着它指向的值可以修改,但是指针的指向不可修改,与常量指针刚刚相反:


int a = 10;int b = 20;int * const p1 = &a;    //指针常量*p1 = 100;  //正确p1 = &b;   //错误,指针的指向不可以修改


因此,我们总结下:


名称
意义
特点
const int * p
常量指针
指向可修改,指向的值不可修改
int * const p
指针常量
指向不可修改,指向的值可修改


四,指针与数组


我们知道,一维数组名本身就是一个指针,但是在使用的过程中要小心,因为这个指针分为指向数组首元素的指针与指向整个数组的指针,那么如何区分它们呢?我们来看下面几行代码:


int arr[] = {1, 2, 3, 4, 5};int* p1 = arr;int* p2 = &arr[0];int* p3 = &arr;    //报错



所以,我们得出结论,对于一维数组arr:


名称
意义
步长
arr
指向数组首元素
单个元素
&arr[0]
指向数组首元素 单个元素
&arr
指向整个数组
整个数组


在定义了指向数组首元素的指针变量后,我们可以通过这个指针变量来访问数组元素:


 int arr[] = { 1,2,3,4,5 };  int* p1 = arr; int length = sizeof(arr) / sizeof(int); for (int i = 0; i < length; i++) { cout << p1[i] << endl;    cout << *(p1 + i) << endl; }


上面几行代码中,p1[i]与*(p1+i)两者是等价的,所以输出的结果一样。但是要注意,当用sizeof操作符操作arr的时候,这个时候不能把arr当做一个指针来对待,因为sizeof操作数组的时候它返回的是数组的字节长度,而单个指针变量只占用四个字节。上面循环体中,我们也可以通过下面方式访问:


cout << *p1++ << endl;  cout << *(p1++) << endl;


*p1++与*(p1++)是等价的,这是因为++的运算符优先级比*要高,因此不管你加不加括号,都会优先执行p++,然而p++是先返回p的值,再与*结合,最后p再向后移动一位。


不过在这里要特别注意,有一种情况下我们是不能通过sizeof操作符来计算数组的长度的,就是当数组名作为函数参数传递的时候:


void test(int arr[]){ int lenth = sizeof(arr) / sizeof(int);}


上面这行代码语法上没有问题,但是得出的结果却不是我们想要的结果,为什么呢,这是因为数组名作为函数传递的时候,会退化成一个指针,如果是二维数组的话,会退化成指向一维数组的指针,所以sizeof(arr)计算出来的结果就不是数组的字节长度了。所以说,在c/c++中传递数组的时候,一般我们也会把数组的长度作为形参传递过去。


但是我们不能通过下面方式去访问数组元素:


cout << *arr++ << endl; //报错


这是因为arr本身是一个指针常量,指针的指向不可更改,因此编译器直接报错。


五,数组指针与指针数组


数组指针顾名思义,本质就是一个指针,这个指针指向整个数组;指针数组本质上是一个数组,但是数组的每个元素都是指针。请看下面两行代码:


int *p1[10];    //指针数组int (*p2)[10]; //数组指针


上面两行代码,p1是一个数组,而p2却是一个指针,它指向一个匿名数组。为什么是这样呢?这是因为[]的优先级比*要高。p1 先与[]结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。至于p2 就更好理解了,在这里括号的优先级比[]高,*号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。


手把手教你深入理解c/c++中的指针

p1为数组名,每个元素都是int型指针


手把手教你深入理解c/c++中的指针

p2为指针变量,指向一个匿名数组


如果我们定义:


int(*p)[10] = &arr;


那么如何访问数组的元素呢?且看,由于上行代码中,p=&arr,那么对其解引用,*p就是arr,因此我们可以通过(*p)[]来进行访问数组的元素:


for(int i = 0; i < 10; i++){  cout<< (*p)[i] << endl;}


六,指针函数与函数指针


指针函数顾名思义,他是一个函数,但返回值是一个指针,例如下面这几行代码:


inttest(){ int a = 10; int* p = &a; return p;}


这个test就是一个指针函数,它返回的是一个int型的指针。


函数指针本质是一个指针,这个指针指向一个函数,那么我们如何定义函数指针呢?请看下面代码:


int myAdd(int a, int b){ return a + b;}void test(){  int(*pFun)(intint) = myAdd;    //定义一个函数指针  cout << (*pFun)(25) << endl;    //用函数指针调用函数 cout << pFun(2, 5) << endl; //用函数指针调用函数}


上面test函数代码中,我们定义了一个函数指针,在最后进行调用函数的时候,有两种方法,一种是用*pFun来调用,一种是直接用pFun来调用,可见两种方法结果都一样。


最后,我们来看个比较混合指针复杂的案例:


char *(* c[10])(int **p);


乍一看,让人眼花缭乱,不知道是什么东西,在这里请大家记住一个规则:C语言标准规定,对于一个符号的定义,编译器总是从它的名字开始读取,然后按照优先级顺序依次解析。注意是从名字开始,不是从开头也不是从末尾,这是理解复杂指针的关键。


有了上面的规则,我们来逐步剖析上面哪行代码的意义:


首先从*c[10]开始,由于[]的优先级比*高,因此,*c[10]代表一个指针数组,每个元素都是指针,但类型还不知道。再看右边的(int** p),它是一个函数,参数为一个二级指针。最左边char* 代表这个函数的返回类型。因此,整行代码的含义就是:c 是一个拥有 10 个元素的指针数组,数组每个元素指向一个原型为char *(int **p)的函数。


好了,关于c/c++中的指针就先讲述到这里,希望这篇文章对你理解指针有帮助,后面还会持续更新。更多精彩的文章可以扫描下面的二维码关注我,感谢大家的支持!


下载1:何恺明顶会分享



下载2:终身受益的编程指南:Google编程风格指南




 
   
   
 
下载3 CVPR2020

AI算法与图像处公众号后台回复: CVPR2020 即可下载1467篇CVPR 2020论文
  
    
    
  
个人微信(如果没有备注不拉群!)
请注明: 地区+学校/企业+研究方向+昵称


觉得不错就点亮在看吧


以上是关于手把手教你深入理解c/c++中的指针的主要内容,如果未能解决你的问题,请参考以下文章

深度长文教你彻底掌握C++/C指针:指针和数组与字符串

深度长文教你彻底掌握C++/C指针:指针和数组与字符串

《软件工具》手把手教你使用Visual Studio Code开发C/C++(Windows)

《软件工具》手把手教你使用Visual Studio Code开发C/C++(Linux)

只用70行代码,手把手教你遍历当前windows所有进程!

只用70行代码,手把手教你遍历当前windows所有进程!