12 指针进阶 写的烦且烂,别看
文章目录
0. 写在前面
定义纯地址数值运算:
运算过程用【】括起来,内部运算只考虑地址值不考虑指针类型,表示地址单位字节的常数应当加上符号byte.
例如:int a[3][4]
【
a=&a[0][0]
a+1=a+16byte
】
- 这里指a的地址值和a[0][0]的地址值数值相等,而不考虑a应为int(*)[4]类型,而a[0][0]为int*类型;
- a+1在C++中表示指针运算,增加sizeof(基类型)个字节,这里转换字节数后填上单位以免引起误解.
1. 一维数组与指针
short a[10];
一维数组的数组指针和数组元素指针具有相同的基类型,但本质上还是有区别的
当然,如果类似于fun(short a[])这样的函数:
将a作为参数传入,sizeof(short)只会当指针处理输出4,而非20,形参的传递的为指针,长度数据丢失了.
然后就是 p[i]=*(p+i) ,总之比较简单基础.
3. 多维数组与指针
3.1 从二维数组开始
3.1.1 基本描述
先理解从一维数组拓展过来的几个等价关系:
地址:
&
a
[
i
]
⇔
a
+
i
\&a[i] \Leftrightarrow a+i
&a[i]⇔a+i
值:
a
[
i
]
⇔
∗
(
a
+
i
)
a[i]\Leftrightarrow *(a+i)
a[i]⇔∗(a+i)
特殊的:
a
⇔
a
+
0
⇔
&
a
[
0
]
a\Leftrightarrow a+0\Leftrightarrow\&a[0]
a⇔a+0⇔&a[0]
∗
a
⇔
∗
(
a
+
0
)
⇔
a
[
0
]
*a\Leftrightarrow *(a+0)\Leftrightarrow a[0]
∗a⇔∗(a+0)⇔a[0]
例如:
int a[3][4];
这个二维数组可以看成一个3元的一维数组,元素类型为4元一维数组,而4元一维数组的元素为int类型.
讨论 a , a+i , &a[i]:
- 二维数组数组名为a;
- 数组名=数组地址=数组首元素地址;
∴ \therefore ∴ 二维数组地址为a
a=&a[0]
∴ \therefore ∴ 数组是一个三元数组,其首元素为第一个四元一维数组,a为该元素(四元一维数组)的地址,行地址
那么 a+i=&a[i] =第i+1个四元一维数组的地址…行地址
延伸一下:
既然a=&a[0]是第一个四元一维数组的地址,那么根据数组名=数组地址可不可以理解a为该一维数组的名称呢?根据数组地址=数组首元素地址是否又可以理解a也为四元一维数组首个int元素的地址呢?
先回答第二个问题:是的,如上图所示,a=2000,&a[0]=2000,&a[0][0]=2000,这不难理解;
第一个问题的答案是显然错误的,如果a能够理解为四元一维数组名称,那么推出a[1]对应int,明显与实际上对应的一维数组类型错误.
原因是:尽管对应的地址值一致,但用来承载该地址的指针类型是不同的.在这里编译器已经帮你做了处理,使得:
a. 使用int*指针承载的a对应地址(0097FD6C),理解为元素地址,通过pt[i]访问int元素;
b. 使用int (*)[4]指针承载的a对应地址(0097FD6C)的p[i],理解为行地址访问一维数组元素,通过p[i][j]访问int元素.
因此这条规则应当加上一个注解:数组地址值=数组首元素地址值=数组名地址值,而当地址被指针承载后,具体的使用依赖于指针对应的基类型!
讨论 a[i],*(a+i):
一般来说二维数组a的元素为一维数组,那么a[i]就应该是取出这个一维数组元素.
取出一个数组,可能不像取出一个int值得到100一样好理解,实际上取出的一个地址,大致可以理解为取出数组变量,取出数组名.数组的值是抽象的,只有访问其中具体元素时才能具象成100,123.5这样的数据.
a[i]的元素数组的值就是数组变量即数组名,如a[3][4]含有的3个一维数组元素的数组名依次为a[0],a[1],a[2].
- a[i]=四元一维数组的数组名=四元一维数组首元素地址=int类型的元素地址
3.1.2 绕晕你的辨析题
地址:
&
a
[
i
]
⇔
a
+
i
\&a[i] \Leftrightarrow a+i
&a[i]⇔a+i
值:
a
[
i
]
⇔
∗
(
a
+
i
)
a[i]\Leftrightarrow *(a+i)
a[i]⇔∗(a+i)
基本思路还是按如上两个公式来,尽可能向a[ ]和a[ ][ ]形式转换:
-
行地址:
- a=a+0=&a[ 0 ]
- &a[ i ]
- a+i=&a[ i ]
-
元素地址:
- a[ i ]=a[ i ]+0=&a[ i ][ 0 ]
- *(a+i)=a[ i ]=a[ i ]+0=&a[ i ][ 0 ]
- &a[ i ][ j ]
- a[ i ]+j=p+j=&p[ j ]=&a[ i ][ j ]
- *(a+i)+j=a[ i ]+j=&a[ i ][ j ]
-
值
- a[ i ][ j ]
- *(a[ i ]+j)=*(&a[ i ][ j ]) =a[ i ][ j ]
- *(*(a+i)+j)=*(a[ i ]+j)=*(&a[ i ][ j ])=a[ i ] [ j ]
3.1.3 指针加法测试
怎么理解?指针类型的分析先化为&形式
- a属于行指针,a+1,(a+1)+1均为行指针的加法,改变量为基类型大小,int(*)[4]行指针的基类型为int[4]数组,因此与a差16和32
- *(a+1)=a[1]=a[1]+0=&a[1][0],说明*(a+1)和*(a+1)+1为元素指针,两者相差sizeof(int)=4.a[1]首元地址值和a[1]地址相同 ⇒ \Rightarrow ⇒a[1]地址值等于a[0]地址值+16 ⇒ \Rightarrow ⇒a[0]地址值等于a地址值,因此为a+16
- a[2]=a[2]+0=&a[2][0],是元素指针,因此两者相差4;与a相比:
(地址运算,单位字节)
&a[2][0]=&a[2]=&a[0]+244=a+32 - &a[2],行指针,差32
完整解答如下:
3.1.4 sizeof测试
3.1.5 行指针
- int (*p)[4];
指向4个int组成的一维数组的指针.
分析步骤:
int (*p)[4]=a;
(*p)视为整体(类似数组名),有四个int元素;
p指向的东西有四个int元素
p指向一个int[4]数组
同时:a=&a[0],即a[0]数组的地址赋给p.
判断指针类型!
type* p=&type!,所以想办法化出&
判断指针运算!化出&知道type,用sizeof(type)就行!
- p[i][j]=(p[i]+j)=(*(p+i)+j)