为什么这个c代码不会改变arr [3]的值?

时间:2017-03-30 21:53:48

标签: c arrays struct

我正在创建一个int数组,然后欺骗c,使其相信它是一个短值数组。我知道这不是好习惯,但我只是想了解为什么这不起作用。这不应该改变arr [3]的值吗?

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    int arr[5];
    arr[0] = 0; arr[1] = 0; arr[2] = 0; arr[4] = 0;
    arr[3] = 128;
    ((short*)arr)[6] = 128; // Shouldn't this change arr[3] ? as 6,7 indices in the arr of short would compromise of arr[3] in arr of ints?
    int i = 0;
    for (i = 0; i < 5; i++){
        printf("%d\n", arr[i]);
    }
    return 0;
}

PS:这是一个更深入的澄清: 当我将int数组转换为一个短数组时,它似乎变成了一个包含10个短元素(不是5个)的数组。所以当我改变arr [6]时,我只改变了int arr [3]的前16位。所以arr [3]仍然应该改变,并不是我再次将它改为128并且没有看到变化。

澄清:此代码仅用于实验原因!我只是学习指针如何工作,我认为这不是很好的做法。

3 个答案:

答案 0 :(得分:2)

您的代码具有未定义的行为,因为您通过指向其他类型的指针编写具有声明类型的数据,并且不同的类型不是char

int arr[5];
/* ... */
((short*)arr)[6] = /* ANYTHING */;

编译器有权生成根本不包含对((short*)arr)[6]的写入的机器代码,这很可能适用于现代编译器。它还有权删除main的整个主体,理论上该程序的所有可能执行都会引发未定义的行为,因此程序将永远不会被运行。

(有人说,当你编写一个具有未定义行为的程序时,C编译器有权让恶魔飞出你的鼻子,但作为一个退休的编译器开发人员,我可以向你保证,大多数C编译器实际上都不能做这一点。)

答案 1 :(得分:0)

它正在更改arr[3],但是您将其设置为128,因此您不会注意到更改。将行更改为:

((short*)arr)[6] = 72;

你应该看到以下输出:

enter image description here

如果您是C新手,还需要清理几件事。您可以通过执行以下操作将数组初始化为零。

... int arr[5] = { 0 }; arr[3] = 128; ...

希望这有帮助!

答案 2 :(得分:0)

您考虑过endianness吗?

编辑:现在增加更多清晰度......

正如其他人在评论中提到的那样,这绝对是 undefined 行为!这不仅仅是&#34;不是良好的做法&#34;,它只是不做!

Pointers on C是一本优秀的书,涵盖了你想要了解的关于指针的所有内容。它已过时但仍然非常相关。你可以在网上找到大部分的信息,但我还没有看到很多关于指针的书和这本书完全一样。

虽然听起来像是在试验,但可能是课堂的一部分。所以,这段代码有很多问题:

  • 端序
  • 内存访问模型
  • 假设类型大小
  • 假设硬件架构
  • 十字型铸造

请记住,即使C今天被认为是一种非常低级的语言,它仍然是一种高级编程语言,可以提供许多关键的抽象。

现在,再看看你的宣言。

int arr[5]; 

您已将5个整体分组在一起,并通过名为arr的公共变量进行访问。根据标准,该数组是每个元素至少 2个字节的5个元素,其基址为&arr[0]。因此,您无法保证 int 是2个字节,或4个字节或其他。同样,由于 short 被标准定义为至少2个字节。但是, short int ,即使它们具有相同的字节宽度!请记住,C是强类型的。

现在,看起来您正在一台机器上运行,其中 short 是2个字节而 ints 是4个字节。这就是字节序问题发挥作用的地方:你最重要的在哪里?您最重要的字节在哪里?

通过将 arr 的地址转换为短指针,首先会破坏类型和内存访问模型。然后,您想要从 arr 的偏移量中访问第6个元素。但是,您不能相对于您声明 arr int 进行访问,而是通过指向的短指针进行访问与 arr 的地址相同!

以下操作相同!它也属于 undefined的范畴 - 不要这样做!

int foo;
int pfooInt;
short bar;
short * pfooShort;

bar = (short) foo;
pfooShort = (short*)&foo;
pfooInt = &foo;
bar = *pfooShort;
pfooShort = (short*)pfooInt[0];

另一件需要澄清的事情是:

int arr[5];

((short *)arr)[6] ...

这不会将5个元素的int数组转换为包含10个元素的短数组。 arr 仍然是一个包含5个元素的int数组。您刚刚破解了访问方法,并试图以未定义的方式修改内存。你做的是告诉编译器&#34;忽略我之前告诉你的关于 arr 的内容,将 arr 视为该语句生命周期的短指针并访问/修改相对于此指针的第6个短。&#34;