使用C指针与字符数组

3
int i=512;
char *c = (char *)&i;
c[0] =1;
printf("%d",i);

这个代码显示 "513",它将变量 i 加 1。

int i=512;
char *c = (char *)&i;
c[1] =1;
printf("%d",i);

这里显示的是256。将其除以2。
有人能解释一下为什么吗?非常感谢。


1
请阅读这个问题这个维基,因为结合起来很可能会回答你刚才提出的所有问题。 - WhozCraig
6个回答

12

二进制

用二进制表示的32位数512,就是:

00000000000000000000001000000000

因为2的9次方等于512。惯例上,你需要将二进制位从右至左依次读取。

以下是其他一些十进制数的二进制表示:

0001 = 1
0010 = 2
0011 = 3
0100 = 4

演员阵容:将Int重新解释为字节数组

当您执行以下操作时:

int i = 512;
char *c = (char *)&i;

你正在将4字节整数解释为字符数组(8位字节),正如你可能已经知道的那样。如果不知道,这里是发生的事情:

&i

获取变量i的地址。

(char *)&i

将其重新解释为指向字符类型的指针(或者强制转换),这意味着它现在可以像数组一样使用。由于您知道在您的计算机上,int至少有32位,因此可以使用c[0],c[1],c[2],c[3]访问它的字节。

根据系统的字节序不同,数字的字节可能会以大端(最高有效字节在前)或小端(最低有效字节在前)的方式排列。 x86处理器是小端。这基本上意味着数字512的排列方式如上例所示,即:

00000000 00000000 00000010 00000000
    c[3]     c[2]     c[1]     c[0]

我已将位组分成单独的8位块(字节),与它们在内存中的布局相对应。请注意,这里也是从右到左阅读它们,因此我们可以遵循二进制数字系统的惯例。

后果

现在设置c[0] = 1会产生以下影响:

00000000 00000000 00000010 00000001
    c[3]     c[2]     c[1]     c[0]

在十进制下等于 2^9 + 2^0 == 513

c[1] = 1 设置会产生以下效果:

00000000 00000000 00000001 00000000
    c[3]     c[2]     c[1]     c[0]

在十进制中,即为2 ^ 8 == 256,因为您已经使用1覆盖了第二个字节的00000010

请注意,在大端序系统上,字节的存储顺序与小端序系统相反。这意味着,如果您在其中一台机器上运行它,将会得到完全不同的结果。


谢谢您的详细解释,但我还有一个疑问.. code (char *)&i code 如何被解释为4字节整数? - Rachit Kyte.One
1
不是的。它是将4字节整数解释为一组字节(char)。基本上,您正在获取i的地址,然后将其强制转换char指针。这意味着它可以被解释为字节数组。 - Daniel Hanrahan

2

如果你觉得看到的内容很“奇怪”,请先考虑你运行代码的平台和其中的字节序(endianness)。

接下来请考虑以下内容:

int main(int argc, char *argv[])
{
    int i=512;
    printf("%d : ", i);
    unsigned char *p = (unsigned char*)&i;
    for (size_t j=0;j<sizeof(i);j++)
        printf("%02X", p[j]);
    printf("\n");

    char *c = (char *)&i;
    c[0] =1;
    printf("%d : ", i);
    for (size_t j=0;j<sizeof(i);j++)
        printf("%02X", p[j]);
    printf("\n");

    i = 512;
    c[1] =1;
    printf("%d : ", i);
    for (size_t j=0;j<sizeof(i);j++)
        printf("%02X", p[j]);
    printf("\n");
    return 0;
}

在我的平台上(Macbook Air,OS X 10.8,Intel x64架构)
512 : 00020000
513 : 01020000
256 : 00010000

结合您所看到的内容以及您希望阅读的字节序相关信息,您可以清楚地看到我的平台是小端字节序。那么你的呢?


2

请记住char是8位的,512的二进制表示为
512 = 10 0000 0000

当你执行char *c = (char *)&i;时,你会得到:

c[1] = 10
c[0] = 0000 0000

当你执行c[0] = 1时,你会得到10 0000 0001,它是513。

当你执行c[1] = 1时,你会得到01 0000 0000,它是256。


1

由于您正在通过char指针对int进行别名处理,而char宽度为8位(一个字节),因此赋值语句如下:

c[1] = 1;

i的第二个字节设置为000000001。字节1、3和4(如果sizeof(int)==4)将保持不变。以前,第二个字节是000000010(因为我假设您使用的是基于x86的计算机,这是一种小端架构)。因此,您基本上将唯一设置的位向右移了一个位置。这是除以2。

在小端机器和带有32位int的编译器上,最初在i中有这四个字节:

  c[0]      c[1]      c[2]     c[3]
00000000  00000010  00000000 00000000

任务完成后,i 的值被设置为:

  c[0]      c[1]      c[2]     c[3]
00000000  00000001  00000000 00000000

因此它从512变成了256。

现在您应该明白为什么c [0] = 1会导致结果为513 :-) 想想哪个字节被设置为1以及该赋值不会改变其他字节。


0

这取决于机器是 little endian 还是 big endian,数据如何以位存储。要了解更多,请阅读关于 endianness 的内容。

C 语言对此没有保证。

512 in binary :

    =============================================
    0000 0000 | 0000 0000 | 0000 0010 | 0000 0000   ==>512
    =============================================
      12          34          56          78       

(0x12345678 假设是这个整数的地址)

char *c =(char *)&i now c[0] either point to 0x78 or 0x12
Modifying the value using c[0] may result to 513 if it points to 0x78
    =============================================
    0000 0000 | 0000 0000 | 0000 0010 | 0000 0001   ==> 513
    =============================================

or, can be 

    =============================================
    0000 0001 | 0000 0000 | 0000 0010 | 0000 0000  ==>2^24+512
    =============================================

同样地,对于256也是一样的:因为您的c1将具有从右侧第二个字节开始的地址。如下图所示,
    =============================================
    0000 0000 | 0000 0000 | 0000 0001 | 0000 0000  ==>256
    =============================================

这是我们系统中数字表示的实现方式。


0

这是因为你的机器是小端,也就是最不重要的字节先存储在内存中。

你说过int i=512;512在十六进制中表示为0x00000200(为简单起见,假设使用32位操作系统)。让我们看看i在内存中以十六进制字节的形式存储:

  00 02 00 00  // 4 bytes, least-significant byte first

现在,我们通过执行char *c = (char *)&i;将同一内存位置解释为字符数组 - 相同的内存,不同的解释:
  00 02 00 00
c[0][1][2][3]

现在我们用 c[0] = 1; 替换 c[0],内存看起来像这样

  01 02 00 00

这意味着如果我们再次将其视为小端 int(通过执行printf("%d",i);),它的十六进制为0x00000201,即十进制的513

现在,如果我们回去并使用c[1] = 1;更改c[1],则您的内存现在变为:

  00 01 00 00

现在我们回过头来将其解释为小端 int,它的十六进制为0x00000100,即十进制的256

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接