精通c语言中的指针-以汇编的角度理解数组名
数组名最让人困惑的地方,是在使用多维数组名,做加减法取多维数组中数据的时候,当维数变多,或者出现指针数组的时候。
如果理解不深,那么很容易出现取数据错误的问题。
结论是:在使用多维数组名的时候,最好把数组名当做指向第一维数据的地址(同样适用于一维数组)。
多维数组,在内存中是按照一维的数组方式存储中,

如图:
我们定义局部变量int a,到时候编译器会在堆栈中给a这个变量分配一个空间,所以在程序中打印会发现a是2,&a是一个地址的值。
但是当定义局部2维数组的时候,是没有给这个数组名b分配空间的,当你打印的时候,就会发现b和&b是一样的。而当探测这数组名b和&b的类型的时候,
你会发现这两个类型不一样,&b是int (*)[3]的类型,b是int [3]的类型。程序员要明白一件事,CPU只认识0和1,只有程序员自己才关心使用的这个值是什么类型,对CPU来讲都是数字。
定义多维数组的时候,你会发现,多维数组在内存中是一样的,对编译器来讲,没有多维数组,只有一维数组。
c的类型是int [2][4]
c[0]等价于*c等价于*(c+0),是去掉一维以后的类型int [4]类型
c+1(c+i)是指向第i维数组的指针,类型是int (*)[4],要理解图中的c[0]和c+1。
因此会发现当使用数组名做加减法c+i和c[1]的时候,会发现这个时候数组名C就是当做指向第一维数据的地址。
类型_q c[m][n] = *(*(c + m) + n) = c+m×(去掉c的一个*类型宽度) × 类型_q 的宽度+ n*(去掉c+m一个*类型的宽度) × 类型_q 的宽度
那么把c当成是指向第一维数据的地址,那么c就是 类型_q (*)[n]
c[m][n] = c + m×n×类型_q 的宽度 + n×类型_q 的宽度 ,编译器取多维数组中的数据也是这样取的。
关键是一维数组中的第一维如何理解?

以上图为例子:
内存中是以字节为单位存储数组的,我们把字节当成一个维度,因此把一维数组b当成是3维的多维数组,那么第一维就是4个字节类型(int)。
那么以后理解数组名a,不管a(相当于a+0)是几维数组,都当成是指向第一维的地址就是通用的。
以一个实例来检验:
先定义一个结构体类型
struct Student{
int a;
char b;
}student;
//然后定义6个变量初始化,准备给2维数组赋值。
Student student1,student2,student3,student4,student5,student6;
student1.a = 1;
student1.b = 11;
student2.a = 2;
student2.b = 22;
student3.a = 3;
student3.b = 33;
student4.a = 4;
student4.b = 44;
student5.a = 5;
student5.b = 55;
student6.a = 6;
student6.b = 66;
//定义一个结构体数组
Student b[2][3]={student1,student2,student3,student4,student5,student6};
//定义一个指针数组
Student (*p)[2][3];
p = &b;
printf("Student:%d\n",sizeof(Student)); //注意,由于对齐的原因,这里Student宽度是8,而不是5。
printf("p:%d,%d\n",p,p+1);//+ 48 p+1等于p+2×3×(Stuedent类型宽度)
printf("*p:%d,%d\n",*p,*p+1);//+24 *p是Student [2][3]类型,用到我们前边讲的*p是Student (*)[3],p+1等于p+3×(Stuedent类型宽度)
printf("p[0]:%d,%d\n",p[0],p[0]+1);//+24 p[0]=*(p+0)=*p,因此同上,还可以这样理解,p[i]或*(p+i)都是Student [2][3]类型,*(p+i)相当于数组名,相当于为多维数组去掉一维后的指针类型,理解为Student (*)[3]
printf("&p[0]:%d,%d\n",&p[0],&p[0]+1);//+48 p[0]是Student [2][3]类型,&p[0]就相当于指针数组Student (*)[2][3],因此&p[0]+1为p+2×3×(Stuedent类型宽度)
printf("&p[0][0]:%d,%d\n",&p[0][0],&(p[0][0])+1);//+24 &p[0][0],p[0]是Student [2][3]类型,p[0][0]是Student [3]类型,&p[0][0]是Student (*)[3]类型,&p[0][0]+1=p + 33×(Stuedent类型宽度)
printf("&p[1][2]:%d,%d\n",&p[1][2],&p[1][2]+1);//+24 p[1][2]是Student [3]类型,&p[1][2]是Student (*)[3]类型,&p[1][2]+1因为这里是大3×(Stuedent类型宽度)
printf("p:%d,%d\n",p,&p[1][2]+1); //&*(*(p + 1) +2) + 1
分步计算 p + 1 = p+2*3*8=p+48,这个时候p[1]也就是*(p + 1 )是Student [2][3]类型,相当于Student (*)[3]
*(p+1) + 2 = p+3*2*8+2*3*8 = p + 48 +48 = p + 96,p[1][2]也就是,*(*(p+1)+2)相当于Student [3]
&*(*(p+1)+2)也就是&p[1][2]相当于Student (*)[3]
&p[1][2]+1 相当于p + 96 + 1*3*8 = p + 120
首先不要把&p[0][0]与&b[0][0]搞混了,两个完全不一样,(因为p=&b);可以把[]转换为*()进行理解
一个知识点:a[x] = *(a+x),即[] =*(),这2个是等价的。
学习指针要牢记:
指针是一种带*的新的类型,这种类型和int、char、long、Student等类型一样,是完美的。
不要把指针和地址必须联系在一起,指针和地址是没有关系的,地址只是一个内存编号,而指针是一种新类型,
为了取出内存中我们想要的数据,我们可以定义各种带*的类型,char*、int*、char****、int****,short (*)[3][4]、int (*Function)(int x ,int y)
指针这种类型就是为了随心所欲取出内存中的数据。

浙公网安备 33010602011771号