为什么在返回字符串的函数上调用std :: string.c_str()不起作用? [英] Why does calling std::string.c_str() on a function that returns a string not work?
问题描述
我有以下代码:
std :: string getString(){
std :: string str (你好);
return str;
}
int main(){
const char * cStr = getString()。c_str();
std :: cout<< cStr < std :: endl; // this prints garbage
}
我认为会发生的是 getString()会返回一个 code>按值返回);因此,
str
的副本将在 main()
中保持活动,直到 main )
返回。这将使 cStr
指向有效的内存位置:底层 char []
或 char由
保存在 getString()
返回的 str
副本的 main()
。
然而,这显然不是这样,所以,问题是,何时 str
销毁,为什么?
blockquote>
getString()将返回一个str(getString()返回值的副本);
没错。
str的副本将在main()中保持活着直到main()返回。
否,返回的副本是临时的 std :: string
,它将在创建它的语句的结尾处被销毁,这意味着在 std :: cout < < cStr < std :: endl;
。这是为什么会打印出垃圾。
您可以将返回的值赋给一个命名的变量或一个const引用(临时的生命周期将被延长,直到引用出来范围)。如:
std :: string s1 = getString(); // s1将从临时变量
复制初始化const char * cStr1 = s1.c_str();
std :: cout<< cStr1<< std :: endl; // safe
const std :: string& s2 = getString(); //临时变量的生命周期将被绑定到一个const引用时扩展
const char * cStr2 = s2.c_str();
std :: cout<< cStr2 < std :: endl; // safe
这里是来自[The.C ++。Programming.Language.Special.Edition] 10.4.10临时对象[class.temp]]:
除非绑定到引用或用于初始化命名对象,否则
临时对象在创建的
中的完整表达式的末尾被销毁。完整表达式是一个表达式,
不是其他表达式的子表达式。
标准字符串类有一个成员函数c_str b返回一个C样式的零终止字符数组(§3.5.1,§20.4.1)。此外,运算符+被定义为意味着字符串连接。
这些是非常有用的字符串设施。然而,它们组合起来会导致模糊的问题。
例如: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风格字符串?可能是作为保存s1 + s2的
临时对象的一部分,并且该存储不能保证
在临时被销毁之后存在。因此,cs将
指向释放分配的存储。输出操作cout < cs可能工作
按预期,但这将是绝对运气。编译器可以检测和
警告这个问题的许多变体。
I have the following code:
std::string getString() {
std::string str("hello");
return str;
}
int main() {
const char* cStr = getString().c_str();
std::cout << cStr << std::endl; // this prints garbage
}
What I thought would happen is that getString()
would return a copy of str
(getString()
returns by value); thus, the copy of str
would stay "alive" in main()
until main()
returns. This would make cStr
point to a valid memory location: the underlying char[]
or char*
(or whatever) of the copy of str
returned by getString()
which, remains in main()
.
However, this is obviously not the case, as the program outputs garbage. So, the question is, when is str
destroyed, and why?
getString() would return a copy of str (getString() returns by value);
It's right.
thus, the copy of str would stay "alive" in main() until main() returns.
No, the returned copy is a temporary std::string
, which will be destroyed at the end of the statement in which it was created, it means that before std::cout << cStr << std::endl;
. That's why garbage was printed out.
You can assign the returned value to a named variable or a const reference (the lifetime of the temporary will be extended until the reference go out of scope). Such as:
std::string s1 = getString(); // s1 will be copy initialized from the temporary variable
const char* cStr1 = s1.c_str();
std::cout << cStr1 << std::endl; // safe
const std::string& s2 = getString(); // lifetime of temporary variable will be extended when bound to a const reference
const char* cStr2 = s2.c_str();
std::cout << cStr2 << std::endl; // safe
Here is an explanation from [The.C++.Programming.Language.Special.Edition] 10.4.10 Temporary Objects [class.temp]]:
Unless bound to a reference or used to initialize a named object, a temporary object is destroyed at the end of the full expression in which it was created. A full expression is an expression that is not a subexpression of some other expression.
The standard string class has a member function c_str() that returns a C-style, zero-terminated array of characters (§3.5.1, §20.4.1). Also, the operator + is defined to mean string concatenation. These are very 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 } }
Probably, your first reaction is "but don’t do that," and I agree. However, such code does get written, so it is worth knowing how it is interpreted.
A temporary object of class string 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. Now, where was the C-style string allocated? Probably 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.
这篇关于为什么在返回字符串的函数上调用std :: string.c_str()不起作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!