将字符串存储在不带空字符的字符数组中 我正在读Stephen Prata的C++入门书。他举了一个例子: char dog[8] = { 'b', 'e', 'a', 'u', 'x', ' ', 'I', 'I'}; // not a string! char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'}; // a string!

将字符串存储在不带空字符的字符数组中 我正在读Stephen Prata的C++入门书。他举了一个例子: char dog[8] = { 'b', 'e', 'a', 'u', 'x', ' ', 'I', 'I'}; // not a string! char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'}; // a string!,c++,arrays,string,visual-studio-2010,C++,Arrays,String,Visual Studio 2010,评论如下: 这两个数组都是字符数组,但只有第二个是字符串 在C样式字符串中起着基础作用。例如,C++有许多功能 处理字符串,包括cout使用的字符串。它们都通过处理字符串来工作- 按字符排序,直到它们到达空字符。如果你要求cout显示一个漂亮的字符串 与前一示例中的cat一样,它显示前七个字符,并检测空值 字符,然后停止。但是如果你不够粗鲁,让库特展示狗的阵列 从前面的示例(不是字符串)中,cout打印 数组,然后继续逐字节遍历内存,将每个字节解释为 要打印的字符,直到它达到空字符。因为空字符,

评论如下:

这两个数组都是字符数组,但只有第二个是字符串 在C样式字符串中起着基础作用。例如,C++有许多功能 处理字符串,包括cout使用的字符串。它们都通过处理字符串来工作- 按字符排序,直到它们到达空字符。如果你要求cout显示一个漂亮的字符串 与前一示例中的cat一样,它显示前七个字符,并检测空值 字符,然后停止。但是如果你不够粗鲁,让库特展示狗的阵列 从前面的示例(不是字符串)中,cout打印 数组,然后继续逐字节遍历内存,将每个字节解释为 要打印的字符,直到它达到空字符。因为空字符,实际上是 字节设置为零,往往在内存中很常见,损坏通常很快得到控制; 但是,不应将非字符串字符数组视为字符串

现在,如果a声明my variables全局,如下所示:

#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;
}
因此,实际上,cout“一个字节一个字节地在内存中移动”,但只移动到第二个字符数组的末尾。任何字符数组的组合都会发生同样的情况。我认为所有其他地址都初始化为0,这就是为什么不能停止。这是真的吗?如果我这样做:

for (int i = 0; i < 100; ++i)
{
    cout << *(&a + i) << endl;
}
在保持所有其他内容不变的情况下,我得到以下输出:

abc
123
现在cout甚至没有通过第一个char数组,更不用说第二个了。为什么会这样?我已经检查了内存地址,它们是顺序的,就像第一个场景一样。比如说,

cout << &a << endl;
cout << &b << endl;
为什么在这种情况下行为会有所不同?为什么它不读取第一个字符数组之外的内容

最后,如果我在main中声明了我的变量,那么我得到了Prata建议的行为,也就是说,在到达空字符的某个地方之前,有很多垃圾被打印出来

我猜在第一种情况下,char数组是在堆上声明的,它被初始化为0(但不是所有地方,为什么?)


我正在使用Visual Studio 2010进行这些示例。

内存超出范围的内容是不确定的。访问您不拥有的内存,即使只是为了阅读,也会导致未定义的行为。

这是一种未定义的行为,您无法说出会发生什么

在其他系统上尝试,您可能会得到不同的输出

您的问题的答案是,这是一种未定义的行为,其输出无法解释

除了上面的解释,在您的特定情况下,您已经全局声明了数组。
因此,在第二个示例中,
\0
附加在四字节边界的第四个字节中,如所示

> P>看起来你的C++编译器在4字节块中分配空间,这样每个对象都有一个4倍的地址(你的转储中的十六进制地址可以被4整除)。编译器喜欢这样做,因为他们喜欢确保较大的数据类型,如
int
float
(4字节宽)与4字节边界对齐。编译器喜欢这样做,因为某些类型的计算机硬件加载/移动/存储未对齐的
int
float
值需要更长的时间

在第一个示例中,每个数组需要8个字节的内存—一个
char
填充一个字节—因此编译器只分配8个字节。在第二个示例中,每个数组有3个字节,因此编译器会分配4个字节,用数据填充前3个字节,而不使用第4个字节

现在在第二种情况下,未使用的字节似乎填充了null,这解释了为什么
cout
在字符串末尾停止。但正如其他人所指出的,您不能依赖未使用的字节来初始化为任何特定值,因此无法保证程序的行为


如果将示例数组更改为4个字节,则程序的行为将与第一个示例中的相同。

使用“\0”只是一个解决方案,可以告诉您字符串的长度。假设您通过在字符串之前存储一个值来知道它的长度

但您的情况是,您故意将其排除在函数之外,并且通常您的代码也会继续搜索分隔符(这是一个空字符)。 它是未定义的,在指定内存的边界后面是什么,它的变化很大。 在Mingw的调试模式下,gdb通常会将其归零,而没有gdb则只是垃圾。。。虽然这只是我的经验。
对于本地声明的变量,它们通常位于堆栈上,因此您正在读取的可能是您的调用堆栈。

这是未定义的行为。你想得太多了。另外,我还没听说过这本书有什么了不起的地方。是的,我也不是这本书的粉丝,但Stroustrup版似乎更像是我的参考书,我也没听说过其他(好的)替代书。也许你可以看看。是的,你是对的。在向char数组添加一个元素后,它的行为与第一个示例中的行为类似。我仍然不知道为什么在第一种情况下内存(堆?!)被初始化为所有0,但在第二种情况下(堆栈?!)却没有。除了编写VS编译器的人之外,可能没有人不这样做。因为深入研究未定义的内容是浪费时间的以同样的方式操作(逐字节遍历内存,直到到达空字符)?
abc
123
cout << &a << endl;
cout << &b << endl;
003B903C
003B9040