C/C++指针和数组的异同

    这个是C/C++中最容易混淆,最容易头晕的一个话题。
 
    我们先从一个简单的例子看起(一维数组)
void f(char* buf);    |         void f(char* buf);
int main(...){        |         int main(...){
char buf="abc";       |         char* pbuf="abc";
f(buf);               |         f(pbuf);            ->相同的生成代码
buf[2]='x';           |         pbuf[2]='x'         ->不同的生成代码
 
上面这两个程序有区别吗? 答案是:
(1)对于一维数组的处理,传递参数的地时候统统作为指针来看待,也就是f(buf)的调用被编译器等效成了
char* pbuf="abc",f(pbuf)这样的调用。
(2)对于寻址和赋值:
buf[2] 是编译器计算(buf的地址+2),放入x
pbuf[2]是编译器计算pbuf的地址,得到pbuf中的值,再以这个值为基地址,+2,放入x
也就是说,pbuf的赋值语句是2次跳转的,效率比不上buf[2]这样的语句。
--------------------------------------------------------------
   考虑复杂一点的情况,多维数组怎么办?
int main(...){
   int buf[2][3];//这个buf数组在内存中仍然是1维连续内存!
 
那么buf[10][10]=6;这样的语句是如何计算的呢? buf的结构被看成一个矩阵被拉直为行向量的表示,10行10列,buf[1][2]的地址就是:
   第二行的起始(1*10)+第3个元素的偏移(2),等效于((int*)(buf))[12]。
   这样说很清楚了吧,如果我们要把buf传递给一个函数作为参数,怎么办呢? 只需要保证编译器能看出,这个被拉直的,2维数组,每一行多少个元素:
   void f(int buf[][10]){
     buf[1][2]=6;//编译器能够通过f的形式参数声明来决定buf[1][2]是从buf偏移多少。
   ...
上面这个声明和void f(int buf[10][10])甚至void f(int buf[20][10])是等效的。因为我们只需要知道每行包括多少个元素,至于有多少行,(数组多大),不是编译器控制的,是程序元的责任。
 
--------------------------------------------------------------
    如果f的声明是f(int buf[][])呢? 它等效于f(int *buf[])或者f(int ** ppbuf)这样的声明,传入参数必须是一个真正的2维数组。像下面这样
    int** buf=new int*[10];
    for(int i=0;i<10;++i)buf[i]=new int[10];
    f(buf);
    buf数组本身必须是一个指针数组,buf[1][2]这样的计算是:
(a)计算buf[1]的值
(b)这个值是一个地址,指向一个数组,取这个数组的偏移量2中的值。
 
如果我混用f(int buf[][10])和f(int buf[][]),我就会得到一个编译警告或者错误:


void f2(int ppi[][2]){}
void f3(int *ppi[2]){}

int p2[3][2]={ {1,2},{3,4}, };
f2(p2);正确的用法
f3(p2);警告:传递参数 1 (属于 ‘f3’)时在不兼容的指针类型间转换。
 
   由于f3的生成代码是2次跳转,因此传入p2作为参数的时候,会把一个真正的数组元素的值作为地址看待,再次计算一个内存地址偏移量中的值,可能导致程序崩溃。
 
参考文献:《C专家编程》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值