为什么会这样呢?使用cin读取小于给定输入的字符数组

Why does this work? Using cin to read to a char array smaller than given input

本文关键字:小于 输入 数组 字符 读取 cin 使用 为什么      更新时间:2023-10-16

我正在阅读c++ Primer Plus(第6版),我在第4章看到了一些示例代码,我有一个问题:

清单4.2 strings.cpp

// strings.cpp -- storing strings in an array
#include <iostream>
#include <cstring> // for the strlen() function
int main()
{
    using namespace std;
    const int Size = 15;
    char name1[Size]; // empty array
    char name2[Size] = "C++owboy"; // initialized array
    // NOTE: some implementations may require the static keyword
    // to initialize the array name2
    cout << "Howdy! I'm " << name2;
    cout << "! What's your name?n";
    cin >> name1;
    cout << "Well, " << name1 << ", your name has ";
    cout << strlen(name1) << " letters and is storedn";
    cout << "in an array of " << sizeof(name1) << " bytes.n";
    cout << "Your initial is " << name1[0] << ".n";
    name2[3] = ''; // set to null character
    cout << "Here are the first 3 characters of my name: ";
    cout << name2 << endl;
    return 0;
}

代码本身不会引起任何混淆,但是我一直在运行它,并且对某个场景感到困惑。

name1被初始化为长度为15个元素的数组-我认为这应该保持长度为14个字符的字符串是对的吗?结束字符应该保留给字符串结束符,对吧?

如果我输入我的名字为HowCanIPossiblyFitThisEntireStringIn?,我得到以下输出:

你好!我是c++ owboy !你叫什么名字?

HowCanIPossiblyFitThisEntireStringIn吗?

HowCanIPossiblyFitThisEntireStringIn ?,你的名字有37个字母,存储在

在一个15字节的数组中

你的首字母是h

这是我名字的前三个字符:c++

我输入的整个名称是如何存储的?如果我分步执行代码,在cin读取到name1之后,Visual Studio告诉我它包含元素0 - 14,最后一个是字符'y' ("HowCanIPossibly…)。我可以从这里假设输入的任何额外数据都被截断并丢失了,但显然不是这样,因为下面的cout成功地将整个名称写到了控制台。

出于好奇,谁能告诉我这里发生了什么事?为了记录,我使用的是Visual Studio 2012 Express。

您正在写入超出数组的边界。c++标准没有说这应该是一个错误;它说这是未定义的行为。这意味着任何事情都有可能发生,包括看起来工作正常。简单地说,你的代码没有良好定义的行为,所以你不应该相信它能工作。

我们可以想象为什么它可能工作。前15个字符可以很好地放入数组中:

|H|o|w|C|a|n|I|P|o|s|s|i|b|l|y|F|i|t|T|h|i|s|E|n|t|i|r|e|S|t|r|i|n|g|I|n|?|...
^                             ^
|    These characters fit     |
         in the array

其余字符将被写入以下内存位置。现在,请记住,用于终止c风格字符串的null字符被定义为具有全0位的表示。现在,如果包含?的位置后面的位置全部为0位,则字符串将以空结束。

但事实是,这是未定义的。它恰好起作用了。不幸的是,这是最可怕的错误类型,因为它似乎可以工作很长一段时间,直到有一天你开始接到非常非常生气的客户的电话。

您可以使用istream::get与缓冲区和缓冲区的大小:

cin.get(name1, Size);

正如其他人所指出的,使用std::string:

要容易得多。
std::string name1;
cin >> name;