数组和指针的对比---指针一定效率高吗
前言
在这个专栏的第一期里,笔者以实际项目中看到的一个模型,分析了指针类型对地址取值的影响。这一期,还是围绕指针这个重头戏,分析下数组和指针的异同。
一、指针和数组可以互换使用吗?
数组可以认为是一系列相同类型连续排列数据的集合,指针是指向一定类型数据的变量。单从定义上看,两者就不同了。参考以下2个说明:
int a[5];
int *b;
这里,可以让以上的指针b指向数组的首地址:
b = &a[0];
但不考虑上述赋值的情况,在编译器编译时,对两者的行为会有什么不同呢?
很显然两者不能简单的互换:
在编译时,对于一个数组,编译器首先指定数组元素的数量,留给数组足够的空间给每个元素使用,而之后才会创建数组名,且这个数组的各个元素的地址都确定下来;
对于一个指针,编译器只为指针本身保留内存空间,而他具体指向的位置还是不确定的。如果之后不给这个指针赋值,那么这个指针就认为是个“野指针”。
二、指针一定比数组效率高吗?
这里以VS2010x86编译器为例,针对实现同样功能的两种函数形式,利用VS自带的dumpbin反汇编工具,对比下用数组和指针实现时,具体在执行代码时的效率。
分别使用数组和指针两种方式,实现数组y到数组x里50个元素的copy:
#define SIZE 50
int x[SIZE];
int y[SIZE];
int i;
int *p1, *p2;
void
try1()
{
for(i=0; i<SIZE; i++){
x[i] = y[i];
}
}
void
try2()
{
register int *p1, *p2;
register int i;
for(i=0, p1=x, p2=y; i<SIZE; i++){
*p1++ = *p2++;
}
}
方法1对应try1 fuction,方法2对应try2 function。设置编译器参数使得在不优化和优化的两种情况下,对比编译后的obj在反编译为汇编时的行为:
1.不加入Ox优化选项
try1 function的反汇编结果如下:
可以看出,编译器为了实现数组中每个元素的copy,将数组下标i进行乘法运算来取值进行copy;如果某个硬件架构所支持的乘法运算周期较长,那么该case下明显会降低程序运行的效率。
try2 function的反汇编结果如下:
可以看出,编译器在编译时,将数组x和数组y的首地址分别放在了堆栈中[ebp-8]和[ebp-14h]的位置。而后对数组中每个成员的遍历,均依靠的是前一个数组单元地址的基础上+4的方法,因此这样效率明显高一些。但同时可以看到,在每次调整完数组下标后,将数组元素地址又重新塞进了堆栈中,也存在一些待优化的地方。
2.加入Ox优化选项
try1()和try2()的反汇编结果:
这时,可以惊喜的看到,两者的反汇编代码是相同的!
在采用优化大小或者优化速度的选项后,数组元素的copy过程,完全由rep movs指令实现!即采用edi和esi寄存器,指定ecx巧妙的实现:
rep movs dword ptr es:[edi], dword ptr [esi]
总结
最后,想必读者心中都有了一份答案。
指针不能简单的和数组互换,而且在编译器如此智能的今天,指针也不一定比数组效率高了。