最近SZU开始教指针,对于比较基础的概念和问题有一些自己的理解
特此将自己的学习过程记录下来
目录
二级指针
输出结果:
可以看到注释的猜想基本正确。每一次new出来的空间是连续的,不同的new空间之间一般不连续
对于二级指针的理解这里有一篇很好的博客:
https://blog.csdn.net/yongheng_1999/article/details/52765130
动态分配数组空间(二维及以上)与memset
以前敲题经常会遇到动态分配的数组memet赋初值有误的问题,学懂了其原理以后就理解了
问题演示:
实际上会出现错误,甚至无法输出:
那么问题出在哪里呢,首先看一下memset的源码:
可以看出来memset是对连续空间进行逐字节赋初值的(关于其更深入的理解可以看下面链接),而对于以下数组定义方式,结果却是正确的:
仔细对比其中的区别就能轻松知道问题的根源:数组空间定义,还有sizeof()
数组空间定义:
结合前面我们对二级指针的理解,我们可以知道:当我们用动态分配的方式(new)来分配数组空间的时候,以a数组为例,(a[0][0], a[0][1], a[0][2])之间是连续的,(a[0][0], a[1][0], a[2][0])不一定是连续的,这个不一定来源于概率,我们当然不能期望这个概率,所以对于动态分配空间的数组,数组空间不连续,参考一下源码就知道memset无法对其进行赋值。而对于直接定义的a[3][3],数组空间是以a为首地址的3*3*sizeof(int)空间的连续空间,自然memset并没有问题
sizeof() :
输出直接定义的a和动态分配内存的b的sizeof()结果
可以看到结果并不一致
原因是这里的a是数组类型,空间大小是数组大小,b是指针类型(指向int*类型的指针),空间大小是sizof(int*)
此时有人会提问:a作为数组名不也是一个指针吗?其实不然,a其实是一种数据结构,名为数组。但是int *p=a为什么又合法呢?原因是数组名可以隐式转化为指代实体的指针常量。关于数组和数组名这里附一篇解读:数组名和指针的深入理解(C++) - fenghuan - 博客园
另附一篇关于memset原理的文章:
memset函数的使用原理_catTom的博客-CSDN博客_memset原理
int (*p)[3] 和 int *p[3]的区别
首先有:[]优先级高于*
int (*p)[3]:数组指针(行指针),数据类型 int(*)[3],指针p指向int[3]的数组,(*p)使得p优先被解释成指针,步长sizeof(int)*3
int *p[3]: 其实看成int* p[3]更有助于理解,数据类型为int*,空间大小为3*sizeof(int*),首地址为p的指针数组,[]使得p优先被解释为数组
对数组名取&
既然数组a可以隐式转化为其首地址,那么对a取地址结果又会是什么呢?
可以看到,两者的值是一样的,但这并不意味着两者可以等价
我们可以做一个实验:
可以看到:
&a[0]=a,这是正确的,因为a隐式转化为了数组的首地址
a+4=&a[1],这也是对的,因为&a[1]是数组第二个元素(下标从0开始)的地址,应该是首地址加上sizeof(int),也就是4
&a+1=a+12!=&a[1],这里看上去和我们的“数组名取地址等于数组名”的猜想不一致,事实上证明这个猜想是错误的
在《C和指针》p142中:在以下两种场合下,数组名并不是用指针常量来表示,就是当数组名作为sizeof操作符和单目操作符&的操作数时。 sizeof返回整个数组的长度,而不是指向数组的指针的长度。 取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。
也就是说&a的类型实际上是数组指针int(*)[3],如果仔细阅读上文的话,就会知道其步长等于sizeof(int)*3=12,与测试中&a+1=a+12相吻合
总结:&a和a的结果一致,但数据类型是 数组指针int(*)[ ] 和 指针常量int* const 的区别
函数指针
函数指针类型可以理解为 数据类型+(*函数指针名)+参数类型 ,每一个函数对需要内存分配空间,而指针可以指向函数的首地址,函数指针示例如下图:
然而在以前敲Leetcode的时候,遇到过需要对sort函数自定义比较函数的情况,众所周知sort第三个参数是函数指针,但是单纯的写bool cmp()会报一下错误:
翻译一下就是函数指针为非静态成员,加上static修饰即可通过
问题出在this指针。Leetcode的Solution是类形式存在,非静态成员函数参数列表会隐式添加this指针,普通类成员需要通过 对象名.cmp() 来调用cmp函数,但是sort在编译的时候无法访问对象,所以会出错,而static修饰类成员不用加对象名就可以直接访问函数
参考博客:恼人的函数指针(二):指向类成员的指针 - AnnieKim - 博客园
一些零零碎碎
指针与左值、右值
引用:数组类型、函数类型到左值和右值的转换 - CG@CPPBLOG - C++博客
输出指针值的严格格式
printf, fprintf, sprintf, snprintf, printf_s, fprintf_s, sprintf_s, snprintf_s - cppreference.com
关于int* p和int *p
两者实际上不太存在差别,我个人建议用int *p的写法,这样更能强调p是个指针
关于delete和delete[]
参考博客:C++中的delete和delete[ ]的区别_Fearless的博客-CSDN博客_delete和delete[]
关于void*
指针内容博大精深,C++作为一门基础语言,很有必要学的透彻,这边建议人手一本C++ primer
发个钢铁侠: