c - 取消引用指针和访问数组元素之间的区别

标签 c arrays pointers dereference

我记得有一个例子演示了指针和数组之间的区别。

当作为函数参数传递时,数组衰减为指向数组中第一个元素的指针,但它们并不等效,如下所示:

//file file1.c

int a[2] = {800, 801};
int b[2] = {100, 101};
//file file2.c

extern int a[2];

// here b is declared as pointer,
// although the external unit defines it as an array
extern int *b; 

int main() {

  int x1, x2;

  x1 = a[1]; // ok
  x2 = b[1]; // crash at runtime

  return 0;
}

链接器不会对外部变量进行类型检查,因此在编译时不会产生错误。问题是 b 实际上是一个数组,但是编译单元 file2 并不知道这一点并将 b 视为指针,导致尝试取消引用时发生崩溃。

我记得当时有人对此进行解释时它非常有道理,但现在我不记得解释了,也无法自己想出来。

所以我想问题是在访问元素时数组与指针的处理方式有何不同? (因为我认为 p[1] 被转换为(汇编等价物)*(p + 1) 而不管 p 是否是一个数组或指针——我显然错了)。


两次解引用生成的程序集(VS 2013):
注意:1158000h1158008h分别是ab的内存地址

    12:   x1 = a[1];
0115139E  mov         eax,4  
011513A3  shl         eax,0  
011513A6  mov         ecx,dword ptr [eax+1158000h]  
011513AC  mov         dword ptr [x1],ecx  
    13:   x2 = b[1];
011513AF  mov         eax,4  
011513B4  shl         eax,0  
011513B7  mov         ecx,dword ptr ds:[1158008h]  
011513BD  mov         edx,dword ptr [ecx+eax]  
011513C0  mov         dword ptr [x2],edx  

最佳答案

感谢@tesseract 在评论中提供的链接:Expert C Programming: Deep C Secrets (第 96 页),我想出了一个简单的答案(书中解释的简单简化版本;要获得完整的学术答案,请阅读本书):

  • 声明 int a[2] 时:
    • 编译器为a 提供了一个存储该变量的地址。这个地址也是数组的地址,因为变量的类型是数组。
    • 访问 a[1] 意味着:
      • 检索该地址,
      • 添加偏移量和
      • 访问这个计算出的新地址的内存。
  • 声明 int *b 时:
    • 编译器也有一个b的地址,但这是指针变量的地址,而不是数组。
    • 因此访问 b[1] 意味着:
      • 检索该地址,
      • 访问该位置以获得b的值,即数组的地址
      • 给这个地址加上一个偏移量然后
      • 访问最终的内存位置。

关于c - 取消引用指针和访问数组元素之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21972465/

相关文章:

c - 如果 num 在 C 中小数点后没有数字,则浮点为 int

c - 将字符存储在数组段错误中

java - 确定数组中某个值的邻居之和是否等于设定值?

c - 添加要在函数中操作的大型数组时内存不足

javascript - 按两项对一维数组进行排序

c++ - 将二维数组转换为指针到指针

objective-c - 为什么 Objective-C 中的字符串指针接受并返回字符串的值而不是内存地址?

c++ - 我可以使用什么 winapi C 函数调用将 unicode 转换为 ascii,反之亦然?

c - C : Binary files 上的文件操作

Swift - 无法将类型 'UnsafePointer<Any>' 的值转换为预期的参数类型 'UnsafePointer<_>'