c语言的汇编表示(指针数组和数组指针)
c的两大支柱:数组和指针,优良的继承并发扬了它的爹的两大特性:地址加偏移。
什么是数组
那就得先说明什么是数组
当我们定义了一个数组时,虽然没有赋值,编译器已经给我们划分好了空间
int main()
{
int a[10];
printf("%d", sizeof(a));//结果为40,情理之中
}
此时数组里面的值都是垃圾数据,比如a[0]=4011276318,如果开启了缓冲区溢出检查的话,编译器还会爆异常
所以数组就是在栈中从上到下依次排序的相同变量的集合
汇编表示
int a[10]={1, 2, 3, 4, 5,6,7,8,9,0};
00341593 mov dword ptr [ebp-2Ch],1
0034159A mov dword ptr [ebp-28h],2
003415A1 mov dword ptr [ebp-24h],3
003415A8 mov dword ptr [ebp-20h],4
003415AF mov dword ptr [ebp-1Ch],5
003415B6 mov dword ptr [ebp-18h],6
003415BD mov dword ptr [ebp-14h],7
003415C4 mov dword ptr [ebp-10h],8
003415CB mov dword ptr [ebp-0Ch],9
003415D2 mov dword ptr [ebp-8],0
同样的我们可以把数组换成10个整形变量存储,这在汇编角度来说没有任何区别
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int f = 6;
int g = 7;
int h = 8;
int i = 9;
int j = 0;
return 0;
}
那我们为什么不这样做呢,因为我们觉得这样很傻,查找其中一个具体的变量就很麻烦,数组提供了一个功能 [],a[0]就是第一个变量,a[5],就是第六个变量,这很方便,记住数组这种特性,这是c编译器替我们做的(汇编就没有这样好了),[]这个符号本质来说是某一个偏移
那什么是指针数组呢?
它本质来说还是一个数组,就像老婆饼是饼而不是老婆一样
就是说编译器在栈中分配了连续的一段空间,用来存储什么呢,四个字节大小的数据,仅此而已
通常来说我们可以这样
int main()
{
int a;
int b;
int c;
int d;
int e;
int f;
int g;
int h;
int i;
int* a[10] = {&a,&b,&c,&d,&e,&f,&g,&h,&i};
return 0;
}
我们可以这样,整形常量也是4个字节,我们没有理由不能把它放到一个可以容纳四个字节成员的数组里,对吧?
int main()
{
int* a[10] = {(int*)1,(int*)2,(int*)3,(int*)4,(int*)5,(int*)6,(int*)7,(int*)8,(int*)9};
return 0;
}
什么是数组指针
他本质来说是一个指针,它包含了指针的所有特性和运算规则
数组指针通常情况下指向的类型是一个数组,众所周知,数组的每一个成员都有一个地址,这次我们还是用数组的首地址作为数组指针存储的内容,也就是那个a,现在数组指针指向的那个数组我们得看成一个整体
数组指针的类型是什么?
之前我们用char* a[10]表示一个指针数组,稍微变化一下,让a和*关系更贴近一些,
char (*a) [10]就是一个指向一个char[10]的指针
int a[10]={1, 2, 3, 4, 5,6,7,8,9,0};
int(*pa)[10] = &a;
看到这行代码,我惊讶了,a在我们的认知过程中不是一个常量吗,为什么&可以对常量取地址?
int* a = &123; 怎么看都是错误的吧
查看一下反汇编代码
int(*pa)[10] = &a;
00D63859 lea eax,[ebp-2Ch]
00D6385C mov dword ptr [ebp-30h],eax
汇编的意思是把a这个常量直接给pa这个变量,而不是我们刚才理解的把常量a取地址给pa
也就是说
int(*pa)[10] = &a;
int(*pa)[10] = (int(*)[10])a;
//这两行代码在汇编看来没有本质区别
int main()
{
int a[10]={1, 2, 3, 4, 5,6,7,8,9,0};
int(*p)[10] = (int(*)[10])a;
int(*pa)[10] = &a;
printf("%p %p\n%p %p",a,a+1,pa,pa+1);
//结果
//0043FB5C 0043FB60
// 0043FB5C 0043FB84
//所以 a的值==&a的值
//a的类型是int [10],&a的类型是int(*)[10];
return 0;
}
摘录一下大佬说的一句话
{
应该在了解数组名即是数组的首地址的同时,也要知道数组名仅仅是“相当于指针”,而非是真的指针
数组名只是一个常量(一个值为数组首元素地址的常量),所以不能进行++或者--运算。而常量更是无法取地址的,而之所以有&a,其实这里的a的意义早已不是当初那个数组名了,它此时代表了整个数组
}
通俗点来讲也就是说
这就是c的一个语法糖
那么c千辛万苦的造一个语法糖有什么用呢
当我们对int(*p1)[10] &a中的p做自加p1++;和对int *p2=p做p2++;
结果是p1加了40,p2加了4;
为什么要对指针做自加,通常情况下是当我们认为指针指向的变量有多个排列时自加可以让我们的指针指向下一个变量
也就是编译器认为,int(*p1)[10] &a中的p1变量可能在内存中存在多个排列,那这样的自加才是有意义的,那就引出了一个问题了,什么情况下一个同样类型的数组会在内存中存在多个排列呢?恍然大悟:二维数组(也就是二重偏移)
所以这个语法糖并没有他的兄弟二维数组这样让大家熟知
总结来说:没多大用