C((int*)(char*)中的强制转换问题
这是代码,在第四次打印之前,我很容易理解,但在第五次打印时,我不明白 为什么它是5:a[0]=200,a[1]=128144,a[2]=256,a[3]=302 我明白a[1]的意思,但我还在想a[2]是怎么来的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
#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访问可以节省您的时间