当地址不是字对齐时,在C中将char*地址转换为int*时会发生什么?

yet*_*mer 2 c

我正在运行这段代码来更好地理解指针.

void foo(void)
{
    int a[4] = {0, 1, 2, 3};

    printf("a[0]:%d, a[1]:%d, a[2]:%d, a[3]:%d\n", a[0], a[1], a[2], a[3]);

    int *c;

    c = a + 1;
    c = (int *)((char*) c + 1);
    *c = 10;

    printf("c:%p, c+1:%p\n", c, c+1);
    printf("a:%p, a1:%p, a2:%p, a3:%p\n", a, a+1, a+2, a+3);

    printf("a[0]:%d, a[1]:%d, a[2]:%d, a[3]:%d\n", a[0], a[1], a[2], a[3]);

    printf("c[0]:%d, c[1]:%d\n", *c, *(c+1));

}
Run Code Online (Sandbox Code Playgroud)

我得到的输出是:

a[0]:0, a[1]:1, a[2]:2, a[3]:3
c:0xbfca1515, c+1:0xbfca1519
a:0xbfca1510, a1:0xbfca1514, a2:0xbfca1518, a3:0xbfca151c
a[0]:0, a[1]:2561, a[2]:0, a[3]:3
c[0]:10, c[1]:50331648
Run Code Online (Sandbox Code Playgroud)

有人可以解释一下[1]现在是2561吗?

我明白当我们这样做时:

c = (int *) ((char *) c + 1);
Run Code Online (Sandbox Code Playgroud)

c现在指向a [1]的第一个字节后面的4个字节.

但是[1]如何以2561结束呢?

我猜这与字节序有关吗?

Jea*_*bre 6

c = a + 1;
Run Code Online (Sandbox Code Playgroud)

现在c1(第二个元素a)

c = (int *)((char*) c + 1);
Run Code Online (Sandbox Code Playgroud)

你用指针算术"欺骗",在地址上加1,无论大小如何int(注意在68000这样的旧机器上它是不合法的,它不能容忍多字节访问奇数地址,或者能完成这项工作,虽然速度慢了很多,但是因为你没有注意到它,例如它在68020上工作但速度较慢).

现在c指向最后3个字节a[1]并在第一个字节溢出a[2],所以当你这样做时:

*c = 10;
Run Code Online (Sandbox Code Playgroud)

因为你的机器是小端,你将离开前导1值,10在下一个位置写入,然后在后面输入零,从而破坏前导2字节a[2]

所以现在:

 a[1] = 1 + (10<<8) = 2561
 a[2] = 0
Run Code Online (Sandbox Code Playgroud)

结果在大端机器上有所不同:

PowerPC大端(如果int是32位,否则它是一个不同的结果):

a[1] = 10485760
a[2] = 2   // first byte is overwritten, but with zero
Run Code Online (Sandbox Code Playgroud)

六万八千零一十分之六万八千:

bus error (coredump) / guru meditation
Run Code Online (Sandbox Code Playgroud)

总结一下:不要违反严格的别名规则

  • 许多RISC机器不喜欢错位访问.DEC Alpha将未对齐的访问转换为系统陷阱,该系统陷阱中止程序或处理几个部分的读取并破坏数据以汇总答案.这并不快 - 一个错位的内存访问是不惜一切代价避免的. (3认同)