令人震惊的事实:数组和指针并不相同——C专家编程

在“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












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值