首页 > 解决方案 > C ( (int*) (char*)) 中的铸造问题

问题描述

所以这是代码,直到第 4 次打印出来我很容易遵循它,但是在第 5 次打印输出时,我不明白

为什么"5: a[0] = 200, a[1] = 128144, a[2] = 256, a[3] = 302 "

我明白了,a[1]但我还在想a[2]256怎么来的?

#include <stdio.h>
#include <stdlib.h>

void f(void)
{
    int a[4];
    int *b = malloc(16);
    int *c;
    int i;

    printf("1: a = %p, b = %p, c = %p\n", a, b, c);

    c = a;
    for (i = 0; i < 4; i++)
    a[i] = 100 + i;
    c[0] = 200;
    printf("2: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
       a[0], a[1], a[2], a[3]);

    c[1] = 300;
    *(c + 2) = 301;
    3[c] = 302;
    printf("3: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
       a[0], a[1], a[2], a[3]);

    c = c + 1;
    *c = 400;
    printf("4: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
       a[0], a[1], a[2], a[3]);

    c = (int *) ((char *) c + 1);
    *c = 500;
    printf("5: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
       a[0], a[1], a[2], a[3]);

    b = (int *) a + 1;
    c = (int *) ((char *) a + 1);
    printf("6: a = %p, b = %p, c = %p\n", a, b, c);
    
    
}

int
main(int ac, char **av)
{
    f();
    return 0;
}

标签: c

解决方案


这一行:

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

可能会让您陷入未定义的行为(技术上),但它可以在英特尔处理器上得到可靠解释。

您的第 4 份打印声明显示了这一点:

4: a[0] = 200, a[1] = 400, a[2] = 301, a[3] = 302

假设 Intel 芯片和 int 的小字节序(很可能),您在和sizeof(int)==4引用的地址处的内存布局如下。 指向此时。acc&a[1]

C8 00 00 00 90 01 00 00 2D 01 00 00 2E 01 00 00 XX XX XX
[   A[0]  ] [   A[1]  ] [   A[2]  ] [  A[3]   ]
            [   C[0]  ] [   C[1]  ] [  C[2]   ]

因此,当您说 时c = (int *) ((char *) c + 1);,您已经将上面的内存阵列的视图移动了 1 个字节。因此c现在指向 内的第二个字节A[1],或者作为一个扁平数组:

C8 00 00 00 90 01 00 00 2D 01 00 00 2E 01 00 00 XX XX XX
[   A[0]  ] [   A[1]  ] [   A[2]  ] [  A[3]   ]
               [   C[0]  ] [   C[1]  ] [  C[2]   ]

然后这个语句发生了。

*c = 500;

这将转换C[0]当前01 00 00 2D500小端十六进制的字节值F4 01 00 00。调整内存值如下:

               | CHANGED |
C8 00 00 00 90 F4 01 00 00 01 00 00 2E 01 00 00 XX
[   A[0]  ] [   A[1]  ] [   A[2]  ] [  A[3]   ]
               [   C[0]  ] [   C[1]  ] [  C[2]   ]

结果,2D占用 A[2] 的第一个字节的值现在是00

所以 A[2] 包含00 01 00 00which in little endian 转换为0x00000100,即256

上面的代码有一些违反规则的地方,但是像上面这样进行指针数学运算的代码在C世界上并不少见,并且由编译器可靠地实现。请注意,在某些架构(例如 Sparc)中,写入在 4 字节边界上未字对齐的整数指针会产生异常(导致程序崩溃的SIGBUS信号)。在操作系统和 Intel/Windows 的 CPU 架构之间的某个地方,这是为您处理的(尽管速度较慢)。


推荐阅读