这个指针算术如何工作?

wha*_*cko 3 c c++ puzzle

#include <stdio.h>

int main(void){
  unsigned a[3][4] = {
    {2,23,6,7},
    {8,5,1,4},
    {12,15,3,9}
 };
 printf("%u",*((int*)(((char*)a)+4)));
 return 0;
}
Run Code Online (Sandbox Code Playgroud)

我的机器中的输出是a[0][1]23的值.可以有人解释这是如何工作的?

编辑:回滚到旧yucky代码,确切地呈现给我:P

GMa*_*ckG 13

所以你把你的数组放在内存中:

2, 23, 6, 7, 8...
Run Code Online (Sandbox Code Playgroud)

这样做是将数组转换为a char*,它允许您访问单个字节,它指向这里:

2, 23, 6, 7, 8...
^
Run Code Online (Sandbox Code Playgroud)

然后它添加四个字节,将其移动到下一个值(稍后将详细介绍).

2, 23, 6, 7, 8...
   ^
Run Code Online (Sandbox Code Playgroud)

然后它将它变成一个int*并取消引用它,得到值23.


这个代码在技术上有三个问题.

第一个是它假设a的unsigned大小为4个字节.(因此+ 4).但这不一定是真的!+ sizeof(unsigned)不管unsigned发生什么样的规模,都会更好,确保正确性.

第二个问题是转换为int:原始数组是unsigned,但值正在转换为int.在unsigned范围内存在int无法表示的值(因为在int范围的一半中是负数.)因此,如果数组中的某个值不能表示为int(意味着值大于INT_MAX),则会得到错误的价值.更好的是转换为unsigned*,以保持正确的类型.

最后一件事是格式说明符.整数的说明符是%d,但代码使用%u,用于无符号整数.实际上,即使重新投入int*是错误的,printf也会将该值重新投入到unsigned*恢复中,恢复它的完整性.通过解决问题二,问题三解决了问题.

有一个隐藏的第四个问题:代码很糟糕.这可能是出于学习目的,但是很糟糕.


pax*_*blo 9

数组:

unsigned a[3][4] = {
    {2,23,6,7},
    {8,5,1,4},
    {12,15,3,9}
};
Run Code Online (Sandbox Code Playgroud)

在内存中布局为(假设a它本身位于内存位置0x8000,特定字节序和四字节int):

0x8000  0  0  0  2
0x8004  0  0  0 23
0x8008  0  0  0  6
0x800C  0  0  0  7
0x8010  0  0  0  8
0x8014  0  0  0  5
0x8018  0  0  0 14
0x801C  0  0  0 12
0x8020  0  0  0 15
0x8024  0  0  0  3
0x8028  0  0  0  9
Run Code Online (Sandbox Code Playgroud)

打破表达方式:

*((int*)(((char*)a)+4))
Run Code Online (Sandbox Code Playgroud)
  • ((char*)a)给你一个char指针.
  • +4将指针前进4个字节(4 * sizeof(char))
  • (int*)将其结果转换为int指针.
  • *取消引用指针提取int.

这是一种非常愚蠢的方式,因为它本质上是不可移植的(int例如,对于两个或八个字节的环境).