c ++中char数组的意外输出

时间:2016-10-15 09:20:51

标签: c++ char

我在c ++方面缺乏经验,我编写了以下代码来了解字符和字符串的工作原理。

#include "stdio.h"
#include <iostream>
#include <string>

using namespace std;

int main()
{
    char asdf[] = "hello";
    char test[5] = {'h','e','l','l','o'};
    cout << test;
}

我原本应该输出&#34;你好&#34;,而是我得到了#34; hellohello&#34;,这对我来说真是令人费解。我做了一些实验:

如果我将asdf更改为另一个不同长度的字符串,则输出&#34; hello&#34;一般。 如果我改变测试中的字符数量,它会输出&#34;你好&#34;通常

我认为这只发生在两者长度相同的情况下,但是当我将它们都更改为&#34; hell&#34;它似乎输出&#34; hell&#34;通常

为了让事情变得更加混乱,当我让朋友在他们的计算机上运行这些代码时,它输出了#34;你好&#34;然后是一个随机的角色。

我在Ubuntu上运行全新的代码块安装。任何人都知道这里发生了什么?

7 个答案:

答案 0 :(得分:8)

这是未定义的行为

C和C ++中的原始char*char[]字符串必须为NULL-terminated。也就是说,字符串需要以'\0'字符结尾。你的test[5]没有这样做,所以打印输出的函数在最后o之后继续,因为它仍然在寻找NULL终止。

由于字符串如何存储在stack上(堆栈通常朝 lower 地址增长),它遇到的下一个字节是asdf[]的字节,您已分配"hello"。这就是内存布局的实际情况,箭头表示内存地址(思考指针)增加的方向:

      ---->
  +-------------------
  |hellohello\0 ...
  +-------------------
        \_ asdf
   \_ test

现在在C ++和C中,像"hello"这样的字符串文字是以NULL方式隐式终止的,因此编译器会在字符串末尾后面写一个隐藏的'\0'。输出函数继续打印asdf char-by-char的内容,直到它到达隐藏的'\0'然后停止。

如果要移除asdf,您可能会在第一个hello之后看到一些垃圾,然后是分段错误。但这是未定义的行为,因为您正在读出test数组的边界。这也解释了为什么它在不同的系统上表现不同:例如,一些编译器可能决定在堆栈上以不同的顺序布置变量,因此在您的朋友系统上,test实际上在堆栈中较低(记住,堆栈中的较低意味着在更高的地址处):

      ---->
  +-------------------
  |hello\0hello ...
  +-------------------
          \_ test
   \_ asdf

现在,当您打印test的内容时,它将打印hello char-by-char,然后继续读取内存,直到找到\0...的内容高度特定于所使用的架构和运行时,甚至可能是月亮的阶段和一天中的时间(并非完全严重),因此在您的朋友机器上它会打印一个“随机”字符然后停止。

您可以通过在'\0'数组中添加0test来解决此问题(您需要将大小更改为6)。但是,使用const char test[] = "hello";是解决此问题的最佳方法。

答案 1 :(得分:5)

您必须使用ascii 0 char终止test数组。现在发生的是在内存中它与你的asdf字符串相邻,所以由于test没有终止,<<将一直持续到asdf结束时遇到ascii 0 }。

如果您想知道:填充asdf时,会自动添加此ascii 0。

答案 2 :(得分:4)

原因是C样式字符串需要空字符来标记字符串的结尾。

由于你还没有将它放入数组test,它只会继续打印字符,直到它找到一个。在你的情况下,数组asdf碰巧在内存中跟随test - 但这不能保证。

而是将代码更改为:

 char test[] = {'h','e','l','l','o', 0};

答案 3 :(得分:2)

cout正在打印从给定地址开头的所有字符(此处为test,或等效表示法中的&test[0]),直至找到空终止符。由于您没有将空终结符放入测试数组,因此它将继续打印,直到它意外地在内存中找到一个。从这一点来看,它几乎是未定义的行为。

答案 4 :(得分:0)

最后一个字符应为'\0'以表示字符串结尾。

 char test[6] = {'h','e','l','l','o','\0'};

答案 5 :(得分:0)

除非有operator<<的重载用于引用5个字符的数组,否则数组将“衰减”为指向char的指针并被操作符视为C样式字符串。按照惯例,C样式字符串以0 char结尾,您的数组缺少该字符串。因此,操作员继续输出内存中的字节,将它们解释为可打印的字符。恰好在堆栈上,两个数组相邻,以便运算符进入asdf的内存区域,输出这些字符,最后遇到{{1}末尾的隐式0字符。 }。如果省略另一个声明,则程序可能会崩溃,即下一个0字节晚于程序的内存边界。

通过指向该对象的指针访问对象外部的内存(此处为:"hello")是未定义的行为。

答案 6 :(得分:0)

字符序列需要 null terminator \0)。

char asdf[] = "hello"; // OK: String literals have '\0' appended at the end
char test[5] = {'h','e','l','l','o'}; // Oops, not null terminated. UB

修正:

char test[6] = {'h','e','l','l','o','\0'}; // OK
//        ^                         ^^^^