使用const char *的奇怪的std :: cout行为

时间:2015-10-20 10:51:29

标签: c++ string iostream cout c-strings

我有一个方法,它返回一个字符串以显示为错误消息。根据程序中出现此错误的位置,我可能会在显示错误消息之前添加一些解释。

string errorMessage() {
    return "this is an error";
}

// somewhere in the program...
const char* message = ("Some extra info \n" + errorMessage()).c_str();
cout << message << endl;

(我将消息存储为const char *,因为我实际上会将此错误提供给另一个接受const char *参数的方法)

此时它输出垃圾(控制台上的不可打印的字符)。

所以我玩了它,发现如果我做了:

// somewhere in the program...
const char* message = ("Some extra info \n" + errorMessage()).c_str();
cout << ("Some extra info \n" + errorMessage()).c_str() << endl << message << endl;

然后它会正确显示消息两次。

为什么为cout提供额外参数会使其按预期工作?

3 个答案:

答案 0 :(得分:12)

("Some extra info \n" + errorMessage())临时 std::string。这意味着,在声明完成后,它的生命周期已经结束。

cout << ("Some extra info \n" + errorMessage()).c_str() << endl

有效,因为在std::cout使用std::string时,其生命周期尚未结束。在

<< message
但是,

部分是未定义的行为。纯粹的运气。

要解决此问题,您需要使用std::string延长const std::string&的生命周期,或者从C ++ 11开始std::string&&

const std::string&  str_const_ref = "Some extra info \n" + errorMessage();
std::string&& str_rvalue = "Some extra info \n" + errorMessage();

现在您可以根据需要对它们进行操作。

另一种方式是

std::string str = "Some extra info \n" + errorMessage();

但是,如果编译器没有执行某些Return Value Optimization,这将导致构造函数复制构造函数(&lt; C ++ 11,非常糟糕< / strong>)或移动构造函数(&gt; = C ++ 11,更好,但不必要)执行。

顺便说一句,“C ++编程语言”4 th 版本甚至涵盖了这个确切的问题!

在§10.3.4“临时对象”中,Stroustrup先生写道:

  

标准库字符串有一个成员c_str()(§36.3),它返回一个C样式的指针,指向一个以零结尾的字符数组   (§2.2.5,§43.4)。此外,运算符+被定义为字符串   级联。这些是字符串的有用工具。但是,在   他们可能会导致模糊的问题。例如:

void f(string& s1, string& s2, string& s3) {
    const char* cs = (s1+s2).c_str();
    cout << cs;
    if (strlen(cs=(s2+s3).c_str())<8 && cs[0]=='a') {
        // cs used here
    }
}
     

[...]创建一个临时字符串对象来保存s1+s2。接下来,一个指针   从该对象中提取C样式的字符串。然后 - 在结束时   表达式 - 删除临时对象。但是,C-   由c_str()返回的样式字符串被分配为临时字符串的一部分   对象持有s1+s2,并且不保证存储后存储   那个临时的被毁了。因此,cs指向已取消分配   存储。输出操作cout<<cs可能会按预期工作,但是   这将是纯粹的运气。编译器可以检测并发出警告   这个问题的很多变种。   if语句的问题有点微妙。该   条件将按预期工作,因为其中的完整表达式   创建临时保留s2+s3是条件本身。   但是,该临时值在受控声明之前被销毁   已输入,因此无法保证cs的使用。

所以,不要担心你的C ++技能。甚至C ++圣经也解释了它。 ; - )

答案 1 :(得分:3)

const char* message = ("Some extra info \n" + errorMessage()).c_str();
cout << message << endl;  

errorMessage()会返回一个临时std::string对象
"Some extra info \n" + errorMessage()连接会创建另一个临时对象 取c_str它会返回一个指向其内部缓冲区的指针(不是副本) 然后删除临时对象,指针无效 其他一切都未定义。它可能会给出正确的输出,崩溃或做任何其他事情。

答案 2 :(得分:2)

问题在于:

const char* message = ("Some extra info \n" + errorMessage()).c_str();

errorMessage()将返回一个临时的std :: string,它将在下一行运行之前超出范围。

我建议改为:

std::string message = "Some extra info \n" + errorMessage();

然后,当您需要将指针传递给底层缓冲区时,您可以使用:

message.c_str();