更改数组中的元素值未正确反映
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。
记住,
- 字符串文字 根据定义以 null 结尾。
- 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 不进行边界检查,因此您会遇到未定义的行为。在您使用的编译器、编译器选项、操作系统、处理器体系结构等的特定组合中,碰巧 x
和 y
之间没有 space,并且x
落后于 y
;因此,当您访问数组 y
末尾后面两个位置的元素时,您最终会访问数组 x
占用的内存。更改其中一个元素(操作 system/compiler(选项)/处理器),结果可能会大不相同。不过,它仍然不会是你所期望的那样。
另请注意,[=17=]
是多余的,这将导致您的编译器有效地尝试将 "12345[=18=][=18=]"
分配给数组,这是七个字节(因此会溢出)。它会可能发出警告,但不是必须的。
这里有个大问题:
char y[6] = "67890[=10=]";
y[7]='A';
y
是一个包含 6
个元素的数组,从 0
索引(即 0
、1
... 5
).这意味着 y[7]
是一个无效的表达式并且给它赋值是 未定义的行为.
您在 y
数组的边界之外写入,并且由于 x
和 y
数组在内存中的放置方式,您碰巧覆盖了 x
.
使用不同的 OS、编译器或编译器标志可以在内存中生成 x
和 y
变量的不同位置,代码将在某处写入 '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])。
我不是 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
我的计算机科学项目需要了解这一点,但我不确定发生了什么,代码是
#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。
记住,
- 字符串文字 根据定义以 null 结尾。
- 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 不进行边界检查,因此您会遇到未定义的行为。在您使用的编译器、编译器选项、操作系统、处理器体系结构等的特定组合中,碰巧 x
和 y
之间没有 space,并且x
落后于 y
;因此,当您访问数组 y
末尾后面两个位置的元素时,您最终会访问数组 x
占用的内存。更改其中一个元素(操作 system/compiler(选项)/处理器),结果可能会大不相同。不过,它仍然不会是你所期望的那样。
另请注意,[=17=]
是多余的,这将导致您的编译器有效地尝试将 "12345[=18=][=18=]"
分配给数组,这是七个字节(因此会溢出)。它会可能发出警告,但不是必须的。
这里有个大问题:
char y[6] = "67890[=10=]";
y[7]='A';
y
是一个包含 6
个元素的数组,从 0
索引(即 0
、1
... 5
).这意味着 y[7]
是一个无效的表达式并且给它赋值是 未定义的行为.
您在 y
数组的边界之外写入,并且由于 x
和 y
数组在内存中的放置方式,您碰巧覆盖了 x
.
使用不同的 OS、编译器或编译器标志可以在内存中生成 x
和 y
变量的不同位置,代码将在某处写入 '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])。
我不是 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