将字符串存储在不带空字符的字符数组中

时间:2013-10-14 05:48:54

标签: c++ arrays string visual-studio-2010

我正在阅读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', '\0'}; // a string!

评论:

  

这两个数组都是char数组,但只有第二个是字符串。空字符   在C风格的字符串中起着重要作用。例如,C ++有许多功能   处理字符串,包括cout使用的字符串。他们都通过处理字符串字符来工作 -   按字符直到达到空字符。如果你要求cout显示一个很好的字符串   与上例中的cat一样,它显示前七个字符,检测为null   性格,并停止。但是,如果你有足够的语言告诉cout显示狗阵列   从前面的例子,它不是一个字符串,cout打印出八个字母   数组然后逐字节地继续遍历内存,将每个字节解释为a   要打印的字符,直到它达到空字符。因为空字符,真的是   字节设置为零,往往在内存中很常见,损坏通常很快;   但是,您不应将非字符串字符数组视为字符串。

现在,如果声明我的变量是全局的,就像这样:

#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数组有点短,例如:

char a[3] = {'a', 'b', 'c'};
char b[3] = {'1', '2', '3'};

保持所有其他内容相同,我得到以下输出:

abc
123

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

cout << &a << endl;
cout << &b << endl;

给出

003B903C
003B9040

为什么在这种情况下行为会有所不同?为什么它不读取第一个char数组?

而且,最后如果我确实在main中声明了我的变量,那么我确实得到了Prata建议的行为,即之前很多垃圾被打印出来,到达了空字符。

我猜测在第一种情况下,char数组在堆上声明,并且初始化为0(但不是到处都是,为什么?)和cout根据char数组的长度表现不同(为什么?)

我正在使用Visual Studio 2010来完成这些示例。

4 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:5)

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

在您的第一个示例中,每个数组需要8个字节的内存 - char填充单个字节 - 因此编译器恰好分配8个字节。在第二个例子中,每个数组是3个字节,因此编译器分配4个字节,用你的数据填充前3个字节,并且不使用第4个字节。

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

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

答案 2 :(得分:1)

它是一种未定义的行为,你不能说会发生什么。

尝试其他一些系统,你可能得到不同的输出。

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

除了上述解释之外,在您的特定情况下,您已全局声明了数组。 因此,在第二个示例中,\0explained作为Peter Raynham附加在四字节边界的第四个字节中。

答案 3 :(得分:1)

'\ 0'只是一个告诉字符串有多长的解决方案。让我们说你知道在字符串之前存储一个值多长时间。

但是你的情况是当你有意将其遗漏掉函数时,通常你的代码也会继续搜索分隔符(这是一个空字符)。 未定义指定内存范围背后的内容会有很大差异。 在Mingw的调试模式下,gdb通常将其归零,没有gdb它只是垃圾......但这只是我的经验。 对于本地声明的变量,它们通常位于堆栈中,因此您正在阅读的内容可能是您的调用堆栈。