C((int*)(char*)中的强制转换问题

C((int*)(char*)中的强制转换问题,c,C,这是代码,在第四次打印之前,我很容易理解,但在第五次打印时,我不明白 为什么它是5:a[0]=200,a[1]=128144,a[2]=256,a[3]=302 我明白a[1]的意思,但我还在想a[2]是怎么来的 #include <stdio.h> #include <stdlib.h> void f(void) { int a[4]; int *b = malloc(16); int *c; int i; printf(&q

这是代码,在第四次打印之前,我很容易理解,但在第五次打印时,我不明白

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

我明白a[1]的意思,但我还在想a[2]是怎么来的

#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 = (int *) ((char *) c + 1);
从技术上讲,这可能会使您陷入未定义的行为,但可以在英特尔处理器上可靠地解释

您的第四份打印声明显示:

4: a[0] = 200, a[1] = 400, a[2] = 301, a[3] = 302
假设英特尔芯片的小端数和int的sizeofint==4,则a和c引用的地址处的内存布局如下所示。此时c指向&a[1]

C8 00 00 00 90 01 00 00 2D 01 00 00 2E 01 00 00 XX XX XX XX [A[0]][A[1]][A[2]][A[3]] [C[0]][C[1]][C[2]] 所以当你说c=int*char*c+1;,您已将上面的内存阵列视图移动了1个字节。因此,c现在指向[1]中的第二个字节,或者作为扁平数组:

C8 00 00 00 90 01 00 00 2D 01 00 00 2E 01 00 00 XX XX XX XX [A[0]][A[1]][A[2]][A[3]] [C[0]][C[1]][C[2]] 然后这句话就发生了

*c = 500;
这将把C[0]处的字节值(当前为01 00 2D)转换为500,在小端十六进制中为F4 01 00。按如下方式调整内存值:

|改变| C8 00 00 90 F4 01 00 01 00 00 2E 01 00 00 XX [A[0]][A[1]][A[2]][A[3]] [C[0]][C[1]][C[2]] 因此,占用[2]第一个字节的2D值现在为00

所以A[2]包含00,在小尾端将其转换为0x00000100,即256

上面的代码有一些违反规则的地方,但是像上面这样做指针数学的代码在C世界中并不少见,并且由编译器可靠地实现。注意,有一些特定的体系结构,比如Sparc,写入一个在4字节边界上没有字对齐的整数指针将生成一个异常信号,导致程序崩溃。在操作系统和英特尔/Windows的CPU体系结构之间的某个地方,这是为您处理的,尽管速度较慢。

这一行:

c = (int *) ((char *) c + 1);
从技术上讲,这可能会使您陷入未定义的行为,但可以在英特尔处理器上可靠地解释

您的第四份打印声明显示:

4: a[0] = 200, a[1] = 400, a[2] = 301, a[3] = 302
假设英特尔芯片的小端数和int的sizeofint==4,则a和c引用的地址处的内存布局如下所示。此时c指向&a[1]

C8 00 00 00 90 01 00 00 2D 01 00 00 2E 01 00 00 XX XX XX XX [A[0]][A[1]][A[2]][A[3]] [C[0]][C[1]][C[2]] 所以当你说c=int*char*c+1;,您已将上面的内存阵列视图移动了1个字节。因此,c现在指向[1]中的第二个字节,或者作为扁平数组:

C8 00 00 00 90 01 00 00 2D 01 00 00 2E 01 00 00 XX XX XX XX [A[0]][A[1]][A[2]][A[3]] [C[0]][C[1]][C[2]] 然后这句话就发生了

*c = 500;
这将把C[0]处的字节值(当前为01 00 2D)转换为500,在小端十六进制中为F4 01 00。按如下方式调整内存值:

|改变| C8 00 00 90 F4 01 00 01 00 00 2E 01 00 00 XX [A[0]][A[1]][A[2]][A[3]] [C[0]][C[1]][C[2]] 因此,占用[2]第一个字节的2D值现在为00

所以A[2]包含00,在小尾端将其转换为0x00000100,即256


上面的代码有一些违反规则的地方,但是像上面这样做指针数学的代码在C世界中并不少见,并且由编译器可靠地实现。注意,有一些特定的体系结构,比如Sparc,写入一个在4字节边界上没有字对齐的整数指针将生成一个异常信号,导致程序崩溃。在操作系统和英特尔/Windows的CPU体系结构之间的某个地方,这是为您处理的,尽管速度较慢。

另一种看待它的方式是二进制little endian。步骤4之后,内存中的数组是:

a[0] : 11001000-00000000-00000000-00000000
a[1] : 10010000-00000001-00000000-00000000
a[2] : 00101101-00000001-00000000-00000000
a[3] : 00101110-00000001-00000000-00000000
此时c指向a[1]。表达方式:

c = (int *) ((char *) c + 1);
添加一个字节sizeofchar==1,因此当您分配*c=500时;您正在将其分配给[1]的第二个字节,由于数组在内存中是连续的,所以500的最后一个字节将覆盖[2]中的第一个字节

对正在发生的事情的视觉描述应为:

a[1] : 10010000-00000001-00000000-00000000
                ^
   c = (int *) ((char *) c + 1);
   
a[1] : 10010000-00000001-00000000-00000000
                +   
500  :          11110100-00000001-00000000-00000000
                                           ^
                                   overwrites LSB of a[2]
这将使数组在赋值后的状态保持为:

a[0] : 11001000-00000000-00000000-00000000
a[1] : 10010000-11110100-00000001-00000000
a[2] : 00000000-00000001-00000000-00000000
a[3] : 00101110-00000001-00000000-00000000
其中a[1]和a[2]的值为:


如果您有任何问题,请告诉我。

另一种看待它的方式是二进制little endian。步骤4之后,内存中的数组是:

a[0] : 11001000-00000000-00000000-00000000
a[1] : 10010000-00000001-00000000-00000000
a[2] : 00101101-00000001-00000000-00000000
a[3] : 00101110-00000001-00000000-00000000
此时c指向a[1]。表达方式:

c = (int *) ((char *) c + 1);
添加一个字节sizeofchar==1,因此当您分配*c=500时;您正在将其分配给第二个b 由于数组在内存中是连续的,所以500的最后一个字节将覆盖[2]中的第一个字节

对正在发生的事情的视觉描述应为:

a[1] : 10010000-00000001-00000000-00000000
                ^
   c = (int *) ((char *) c + 1);
   
a[1] : 10010000-00000001-00000000-00000000
                +   
500  :          11110100-00000001-00000000-00000000
                                           ^
                                   overwrites LSB of a[2]
这将使数组在赋值后的状态保持为:

a[0] : 11001000-00000000-00000000-00000000
a[1] : 10010000-11110100-00000001-00000000
a[2] : 00000000-00000001-00000000-00000000
a[3] : 00101110-00000001-00000000-00000000
其中a[1]和a[2]的值为:


如果您有任何问题,请告诉我。

您希望得到什么输出?您能向我们展示4的输出吗?这可能有助于了解您的体系结构和内存布局,但将字符*转换回int*是未定义的行为,gcc以惩罚未定义的行为而闻名。你希望得到什么样的输出?你能给我们展示4的输出吗?这可能有助于了解你的架构和内存布局,但将字符*转换回int*是未定义的行为,gcc以惩罚未定义的行为而闻名。感谢你的辩护,现在变得更容易了:如果你发现答案有用,不要忘记投票和/或接受它。我希望我能做到这一点,但该网站不允许我投票给任何人,因为我是一名新生:谢谢你的辩护,现在变得容易了:如果你觉得答案有用,别忘了投票和/或接受它。我希望我能做到这一点,但网站不允许我投票给任何人,因为我是一名新生:谢谢你的帮助,我真的很感谢你的帮助。我得到了它!很乐意帮忙。这是一个有趣的问题。因为这是一个1D数组,所以没有严格的别名冲突。这不是清晰的最好例子,但最后一点明确允许通过char访问节省了您的时间谢谢您的帮助,我非常感谢您的帮助。我得到了它!很乐意帮忙。这是一个有趣的问题。因为这是一个1D数组,所以没有严格的别名冲突。这并不是清晰的最好例子,但最后一点明确允许通过char访问可以节省您的时间