在“C专家编程”中的第四章中有个例子:
文件1:
int mango[100];
文件2:
extern int *mango;
在文件1中定义了mango,在文件2中将它声明为指针,虽然在很多场合下我们都认为数组和指针时可以混用的,但是在这里如果将数组和指针等价,其实就相当于把整数和浮点型混为一谈:
文件1:
int guava;
文件2:
extern float guava;
先来看看数组和指针是如何访问的:
首先我们需要注意”地址y“和”地址y的内容“之间的区别。这是一个相当微妙之处,因为在大多数编程语言中我们用一个符号来表示这两样东西,由编译器根据上下文环境判断它的具体含义。
举个例子:
X=Y;
在这个上下文环境中,符号X的含义是X所代表的地址,这被称为左值。左值在编译时可知,左值表示存储结果的地方;
在这个上下文环境中,符号Y的含义是Y所代表的地址的内容,这被称为内容。右值直到运行时才知,如无特别说明,右值表示”Y的内容“;
回到刚开始IDE粒子中,这里的关键之处在于每个符号的地址在编译时可知。所以,如果编译器需要一个地址(可能还需要加上偏移量)来执行某个操作,它就可以直接进行操作,并不需要增加指令首先取得具体的地址。相反,对于指针,必须首先在运行时取得它的当前值,然后才能对它进行解除引用操作。可以理解为:对于数组来说,在编译时就已经知道了其地址,这个地址指的是mango的值,也就是元素mango[0]的地址;而对于指针来说,编译时知道的是mango的地址,这个值也就是数组中mango的值,而为了得到指针地址mango对应的内容,必须对mango的地址值进行解引用从而得到指针mango的值,从这里可以看出extern int *mango;较int mango[100];多了一个环节,所以就导致了问题。
例子1:
文件a中:
int mango[100] = {1,2,3,4};
void func();
int main()
{
func();
return 0;
}
文件b中:
extern int *mango;
void func()
{
mango[0] = 1;
}
通过反汇编代码得到mango数组的地址为0x427330,而文件b中的反汇编代码如下所示:可以知道在文件b中把0x427330作为指针mango的地址,首先通过
mov eax,[mango (00427330)]得到了指针mango的值,然后再对该直接进行解引用并赋值。
22: mango[0] = 1;
004010B8 mov eax,[mango (00427330)]
004010BD mov dword ptr [eax],1
例子2:看一下最常见的下面这个函数
void func(int *p)
{
p[0] = 100;
}
int main()
{
int arr[] = {1, 2, 3};
func(arr);
return 0;
}
5: p[0] = 100;
00401038 mov eax,dword ptr [ebp+8]
0040103B mov dword ptr [eax],64h
其中[ebp+8]中存放的是arr的地址,通过这个例子可以看出,将指针作为函数参数时,它没有把arr的地址值作为p的地址,而是直接作为指针p的值,所以操作的结果是合理的。
例子3:这也是很容易误用的一个粒子
int main()
{
int arr[2] = {0};
int **p = (int **)&arr;
(*p)[0] = 1;
return 0;
}
18: int arr[2] = {0};
00401038 mov dword ptr [ebp-8],0
0040103F xor eax,eax
00401041 mov dword ptr [ebp-4],eax
19:
20: int **p = (int **)&arr;
00401044 lea ecx,[ebp-8]
00401047 mov dword ptr [ebp-0Ch],ecx
21: (*p)[0] = 1;
0040104A mov edx,dword ptr [ebp-0Ch]
0040104D mov eax,dword ptr [edx]
0040104F mov dword ptr [eax],1
例子4:
int main()
{
int *arr[2];
arr[0] = (int *)malloc(1 * sizeof(int));
arr[0][0] = 1;
arr[1] = (int *)malloc(1 * sizeof(int));
arr[1][0] = 2;
int **p = arr;
p[0][0] = 10;
return 0;
}
38: int *arr[2];
39: arr[0] = (int *)malloc(1 * sizeof(int));
0040D778 push 4
0040D77A call malloc (00404550)
0040D77F add esp,4
0040D782 mov dword ptr [ebp-8],eax
40: arr[0][0] = 1;
0040D785 mov eax,dword ptr [ebp-8]
0040D788 mov dword ptr [eax],1
41: arr[1] = (int *)malloc(1 * sizeof(int));
0040D78E push 4
0040D790 call malloc (00404550)
0040D795 add esp,4
0040D798 mov dword ptr [ebp-4],eax
42: arr[1][0] = 2;
0040D79B mov ecx,dword ptr [ebp-4]
0040D79E mov dword ptr [ecx],2
43:
44: int **p = arr;
0040D7A4 lea edx,[ebp-8]
0040D7A7 mov dword ptr [ebp-0Ch],edx
45: p[0][0] = 10;
0040D7AA mov eax,dword ptr [ebp-0Ch]
0040D7AD mov ecx,dword ptr [eax]
0040D7AF mov dword ptr [ecx],0Ah
例子5:
int main()
{
int arr[2] = {0};
int (*p)[2] = &arr;
(*p)[0] = 1;
return 0;
}
38: int arr[2] = {0};
0040D778 mov dword ptr [ebp-8],0
0040D77F xor eax,eax
0040D781 mov dword ptr [ebp-4],eax
39:
40: int (*p)[2] = &arr;
0040D784 lea ecx,[ebp-8]
0040D787 mov dword ptr [ebp-0Ch],ecx
41: (*p)[0] = 1;
0040D78A mov edx,dword ptr [ebp-0Ch]
0040D78D mov dword ptr [edx],1
42: return 0;
0040D793 xor eax,eax