将字符串存储在不带空字符的字符数组中
Storing a string in an array of chars without the null character
我正在阅读Stephen Prata的C++ Primer Plus。他举了这样一个例子:
char dog[8] = { 'b', 'e', 'a', 'u', 'x', ' ', 'I', 'I'}; // not a string!
char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', ' '}; // a string!
评论说:
这两个数组都是字符数组,但只有第二个是字符串。空字符 在 C 样式字符串中起着基本作用。例如,C++有许多函数 处理字符串,包括 COUT 使用的字符串。它们都通过处理字符串字符来工作- 按字符,直到它们达到空字符。如果你要求cout显示一个漂亮的字符串 与前面示例中的 cat 一样,它显示前七个字符,检测 null 字符,并停止。但是,如果您足够不客气地告诉cout显示狗阵列 在前面的示例中(不是字符串),cout 打印 数组,然后逐字节地在内存中行进,将每个字节解释为 字符进行打印,直到达到空字符。因为空字符,这实际上是 字节设置为零,往往在内存中很常见,损坏通常很快得到控制; 尽管如此,您不应将非字符串字符数组视为字符串。
现在,如果将我的变量声明为全局变量,如下所示:
#include <iostream>
using namespace std;
char a[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
char b[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
int main(void)
{
cout << a << endl;
cout << b << endl;
return 0;
}
输出将是:
abcdefgh12345678
12345678
因此,事实上,cout "逐字节地在内存中前进",但只到第二个字符数组的末尾。同样的事情发生在 char 数组的任何组合上。我认为所有其他地址都初始化为 0,这就是 cout 停止的原因。这是真的吗?如果我做这样的事情:
for (int i = 0; i < 100; ++i)
{
cout << *(&a + i) << endl;
}
我在输出时大部分是空白空间(也许是 95%),但并非无处不在。
但是,如果我声明我的字符数组短一点,例如:
char a[3] = {'a', 'b', 'c'};
char b[3] = {'1', '2', '3'};
保持所有其他内容相同,我得到以下输出:
abc
123
现在 cout 甚至没有通过第一个字符数组,更不用说第二个了。为什么会这样?我已经检查了内存地址,它们是顺序的,就像在第一种情况下一样。例如
cout << &a << endl;
cout << &b << endl;
给
003B903C
003B9040
为什么在这种情况下行为不同?为什么它没有读取超出第一个字符数组?
最后,如果我确实在 main 中声明了我的变量,那么我确实得到了 Prata 建议的行为,即之前打印了很多垃圾,在某个地方达到了空字符。
我猜在第一种情况下,char 数组是在堆上声明的,并且初始化为 0(但不是所有地方,为什么?
我正在使用Visual Studio 2010作为这些示例。
您的C++编译器正在以 4 字节块分配空间,因此每个对象的地址都是 4 的倍数(转储中的十六进制地址可以被 4 整除)。编译器喜欢这样做,因为他们喜欢确保较大的数据类型(如 int
和 float
(4 字节宽)与 4 字节边界对齐。编译器喜欢这样做,因为某些类型的计算机硬件需要更长的时间来加载/移动/存储未对齐的int
和float
值。
在第一个示例中,每个数组需要 8 个字节的内存 - 一个char
填充一个字节 - 因此编译器会恰好分配 8 个字节。在第二个示例中,每个数组为 3 个字节,因此编译器分配 4 个字节,用您的数据填充前 3 个字节,并将第 4 个字节保留为未使用。
现在在第二种情况下,似乎未使用的字节填充了一个 null,这解释了为什么cout
在字符串的末尾停止。但正如其他人指出的那样,您不能依赖未使用的字节来初始化为任何特定值,因此无法保证程序的行为。
数组更改为具有 4 个字节,则程序的行为将类似于第一个示例。
界内存的内容是不确定的。访问您不拥有的内存,即使只是为了读取,也会导致未定义的行为。
这是一种未定义的行为,你不能说会发生什么。
在其他系统上尝试,您可能会得到不同的输出。
您的问题的答案是,它是一种未定义的行为,其输出无法解释。
除了上述解释之外,在您的特定情况下,您已经全局声明了数组。因此,在您的第二个示例中,