更改数组中的元素值未正确反映

Changing an element value in array does not reflect properly

我的计算机科学项目需要了解这一点,但我不确定发生了什么,代码是

#include <stdio.h>
void main () {
    char x[6] = "12345[=10=]";
    char y[6] = "67890[=10=]";
    y[7]='A';
    printf("X: %s\n",x);
    printf("Y: %s\n",y);
}

输出为:

X: 1A345
Y: 67890

现在我不确定为什么当我明确指定 y.

A 位于 x 数组的第二个元素中

您的程序正在显示 undefined behaviour

记住,

  1. 字符串文字 根据定义以 null 结尾。
  2. C 数组基于 0。索引。

为了澄清,

  • 当您尝试将像 "12345[=13=]" 这样的字符串文字放入包含 6 个 char 元素的数组中时,编译器将尝试在字符串文字的元素之后放置一个额外的 null ,这将成为尝试访问过去分配的内存区域,这又会调用 undefined behaviour。在 char x[6] = "12345[=15=]"; 的情况下,您不需要 [=16=] 作为字符串文字的一部分。此外,当您为初始化提供字符串文字时,最好将元素的分配(换句话说,数组的大小)留给编译器。您可以使用

    char x[ ] = "12345";
    
  • 然后,有一个维度为 x 的数组,对该数组的有效访问是从索引 [0][x-1]。在分配的内存之外访问也是 UB。例如,上面的数组 x,可以(应该)在

    这样的范围内安全访问
    len = strlen(x);      //get the length of the string
    for (int i = 0; i < len; i++)
    {
         x[i] = i*i;  //access the array
    
    }
    

也就是说,请注意 main() 的推荐签名是 int main(void)

当您在 C 中分配一个长度为 n 的数组时,您实际上在内存中分配了一个长度为 n+1 的数组,因为编译器会为空终止符创建空间。您不需要添加空终止符。这实际上导致了未定义的行为。

尝试删除空终止符。让我知道发生了什么!

您指定有两个数组,每个数组大小为六个字节;这意味着它们的元素编号为 0 到 5(因为 C 使用从零开始的数组偏移,而不是像其他一些语言那样从一开始)。

由于您尝试访问 y[7],您访问的元素不属于您的数组。 C 不进行边界检查,因此您会遇到未定义的行为。在您使用的编译器、编译器选项、操作系统、处理器体系结构等的特定组合中,碰巧 xy 之间没有 space,并且x 落后于 y;因此,当您访问数组 y 末尾后面两个位置的元素时,您最终会访问数组 x 占用的内存。更改其中一个元素(操作 system/compiler(选项)/处理器),结果可能会大不相同。不过,它仍然不会是你所期望的那样。

另请注意,[=17=] 是多余的,这将导致您的编译器有效地尝试将 "12345[=18=][=18=]" 分配给数组,这是七个字节(因此会溢出)。它会可能发出警告,但不是必须的。

这里有个大问题:

char y[6] = "67890[=10=]";
y[7]='A';

y 是一个包含 6 个元素的数组,从 0 索引(即 01... 5 ).这意味着 y[7] 是一个无效的表达式并且给它赋值是 未定义的行为.

您在 y 数组的边界之外写入,并且由于 xy 数组在内存中的放置方式,您碰巧覆盖了 x.

使用不同的 OS、编译器或编译器标志可以在内存中生成 xy 变量的不同位置,代码将在某处写入 'A'别的。甚至可以在 read-only 内存区域中写入,在这种情况下 OS 将由于页面错误异常而终止您的程序。

这就是为什么它被称为未定义的行为

char x[6] = "12345[=10=]";
char y[6] = "67890[=10=]";
y[7]='A';

'C/C++' 中的数组索引从零开始。这意味着您只能访问 x,y 的 [0-5] 个索引。

访问 y[7] 会导致未定义的行为;在这种情况下,堆栈很可能向下增长并覆盖 x 的第二个元素(即 x[1])。

相关阅读:http://en.wikipedia.org/wiki/Stack_buffer_overflow

我不是 100% 确定这一点,但据我所知,您已经在数组边界之外写入了数据,并且它写入了相邻块中的内存。

char x[6] = "12345[=10=]";
char y[6] = "67890[=10=]";

混淆可能来自于,如果y在x之后声明,那么内存肯定应该是这样的:

x[0] x[1] x[2] x[3] x[4] x[5] y[0] y[1] y[2] y[3] y[4] y [5]

这归结为 Big Endian v Little Endian。

在大端存储中,最高有效字节存储在最小地址中。在little endian中,它存储在最大地址中。

很多计算机使用 iittle endian 系统(例如,很多 intel 硬件都使用),这可能意味着您的数组实际上是这样存储在内存中的:

y[0] y[1] y[2] y[3] y[4] y[5] x[0] x[1] x[2] x[3] x[4] x [5]

如果是这种情况,那么调用y[7]实际上对应于设置x[1],即x数组的第二个元素。导致数据被覆盖,结果如下:X: 1A345 Y: 67890