Strange std::cout behaviour with const char*

大憨熊 提交于 2019-12-23 18:55:45

问题


I have a method which returns a string to display as an error message. Depending on where this error occurs in the program, I might add a bit more of an explanation to the error message before displaying it.

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

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

(I am storing the message as a const char* since I will actually provide this error to another method which accepts const char* arguments)

At this point it outputs garbage (unprintable characters on the console).

So I played with it and found that if instead I do:

// 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;

then it displays the message correctly twice.

Why does providing the extra argument to cout cause it to work as I intended?


回答1:


("Some extra info \n" + errorMessage()) is a temporary std::string. That means, after the statement is finished, it's lifetime has ended.

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

works because at the point std::cout uses the std::string its lifetime hasn't ended yet. The

<< message

part is undefined behavior, though. Sheer luck it works.

To fix the problem, you need to extend the std::string's lifetime with either a const std::string& or, since C++11, a std::string&&:

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

Now you can operate on them as you want to.

Another way is to

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

However, if the compiler doesn't do some Return Value Optimization, this will lead to a constructor and a copy constructor (< C++11, very bad) or move constructor (>= C++11, better, but unnecessary) getting executed.


BTW, this exact issue is even covered in "The C++ Programming Language" 4th Edition!

In §10.3.4 "Temporary Objects", Mr Stroustrup writes:

The standard-library string has a member c_str() (§36.3) that returns a C-style pointer to a zero-terminated array of characters (§2.2.5, §43.4). Also, the operator + is defined to mean string concatenation. These are useful facilities for strings. However, in combination they can cause obscure problems. For example:

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
    }
}

[...] A temporary string object is created to hold s1+s2. Next, a pointer to a C-style string is extracted from that object. Then – at the end of the expression – the temporary object is deleted. However, the C- style string returned by c_str() was allocated as part of the temporary object holding s1+s2, and that storage is not guaranteed to exist after that temporary is destroyed. Consequently, cs points to deallocated storage. The output operation cout<<cs might work as expected, but that would be sheer luck. A compiler can detect and warn against many variants of this problem. The problem with the if-statement is a bit more subtle. The condition will work as expected because the full expression in which the temporary holding s2+s3 is created is the condition itself. However, that temporary is destroyed before the controlled statement is entered, so any use of cs there is not guaranteed to work.

So, do not worry about your C++ skills. Even the C++ bible explains it. ;-)




回答2:


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

errorMessage() returns a temporary std::string object
Concatenating with "Some extra info \n" + errorMessage() creates another temporary object.
Taking c_str of it returns a pointer to its internal buffer (not a copy).
And then the temporary object is deleted, and the pointer invalid.
Everything else is undefined. It may give the correct output, crash, or do anything else.




回答3:


The problem is here:

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

errorMessage() will return a temporary std::string which will go out of scope before the next line runs.

I would suggest doing this instead:

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

Then when you need to pass a pointer to the underlying buffer you can use:

message.c_str();


来源:https://stackoverflow.com/questions/33234693/strange-stdcout-behaviour-with-const-char

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!