为啥在返回字符串的函数上调用 c_str() 不起作用?
Posted
技术标签:
【中文标题】为啥在返回字符串的函数上调用 c_str() 不起作用?【英文标题】:Why does calling c_str() on a function that returns a string not work?为什么在返回字符串的函数上调用 c_str() 不起作用? 【发布时间】:2014-12-23 20:17:02 【问题描述】:我有一个返回字符串的函数。但是,当我调用它并对其执行 c_str() 以将其转换为 const char* 时,它仅在我先将其存储到另一个字符串时才有效。如果我直接从函数中调用 c_str(),它会将垃圾值存储在 const char* 中。
为什么会这样?感觉我在这里遗漏了一些非常基本的东西......
string str = SomeFunction();
const char* strConverted = str.c_str(); // strConverted stores the value of the string properly
const char* charArray= SomeFunction().c_str(); // charArray stores garbage value
static string SomeFunction()
string str;
// does some string stuff
return str;
【问题讨论】:
c_str() 返回指向底层 char 数组的指针。问题是你临时调用它。所以在;
之后,对象被删除,然后:你会得到垃圾。
一些关于“如何”它不起作用的信息会有所帮助.. 编译器错误?垃圾数据?在这个 sn-p 中有很多潜在的错误。
@Yeraze OP 明确表示它存储了一个垃圾值,所以它绝对不是编译器错误。
【参考方案1】:
SomeFunction().c_str()
为您提供一个指向临时变量的指针(SomeFunction
正文中的自动变量 str
)。与引用不同,在这种情况下,临时对象的生命周期不会延长,并且您最终会得到 charArray
是一个悬空指针,解释您稍后在尝试使用 charArray
时看到的垃圾值。
另一方面,当你这样做时
string str_copy = SomeFunction();
str_copy
是SomeFunction()
的返回值的副本。现在调用c_str()
会给你一个指向有效数据的指针。
【讨论】:
Nowdaysstr_copy
实际上只是移动了所有权,但这一点仍然有效。【参考方案2】:
函数返回的值对象是临时的。 c_str()
的结果仅在临时的生命周期内有效。在大多数情况下,临时的生命周期是到完整表达式的末尾,通常是分号。
const char *p = SomeFunction();
printf("%s\n", p); // p points to invalid memory here.
解决方法是确保在完整表达式结束之前使用c_str()
的结果。
#include <cstring>
char *strdup(const char *src_str) noexcept
char *new_str = new char[std::strlen(src_str) + 1];
std::strcpy(new_str, src_str);
return new_str;
const char *p = strdup(SomeFunction.c_str());
注意strdup
是一个POSIX函数,所以如果你是一个支持POSIX的平台,它已经存在了。
【讨论】:
【参考方案3】:方法SomeFunction()
中的“string str
”是SomeFunction()
中的局部变量,只存在于SomeFunction()
的范围内;
由于SomeFunction()
方法的返回类型是字符串,不是字符串的引用,所以在“return str;
”之后,SomeFunction()
会返回一个str
值的副本,即作为临时值存储在内存的某个地方,调用SomeFunction()
后,临时值会立即销毁;
“string str = SomeFunction();
”将返回的临时值SomeFunction()
存储到字符串str
,实际上是该值的一个副本并存储到str
,分配了一个新的内存块,并且str
的生命周期大于SomeFunction()
返回的临时值,“;
”调用结束后SomeFunction()
的返回值立即销毁,内存被系统回收,但是此值的副本仍存储在str
中。这就是为什么“const char* strConverted = str.c_str();
”可以得到正确的值,其实c_str()
返回了str
的初始元素的指针(str
指向的字符串值的首元素内存地址),而不是返回的临时值SomeFunction()
;
“const char* charArray= SomeFunction().c_str();
”不同,“SomeFunction().c_str()
”会返回一个指向返回临时值的初始元素的指针(返回临时字符串值的首元素内存地址),但是在调用之后SomeFunction()
,返回的临时值被销毁,那个内存地址被系统重用,charArray
可以得到那个内存地址的值,但不是你期望的值;
【讨论】:
【参考方案4】:使用 strcpy 将字符串复制到本地定义的数组中,您的代码就可以正常工作了。
【讨论】:
不管怎样,SomeFunction().c_str()
会给你一个无效的地址。如果您尝试strcpy
它,那将导致未定义的行为。
不。今天做了。效果很好。
您能否提供一个小而完整的工作示例来演示此行为?
这在很大程度上是不同的。问题是 c_str()
在函数调用的结果上被调用。这会使c_str()
提供给您的内部char
指针无效,因为您调用的函数的本地对象string
已超出范围。如果你有类似string a = SomeFunction(); ... a.c_str();
的东西,它也没有被复制。这不会在链接到的代码中发生,因为 c_str()
不会直接在函数调用的结果上调用。
我觉得你没有说得很清楚。一个例子真的会有很长的路要走。事实上,我仍然不能完全确定你的意思是不是像 char* str = new char[...length goes here...]; strcpy(str, SomeFunction().c_str());
这样的东西。以上是关于为啥在返回字符串的函数上调用 c_str() 不起作用?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在父类的构造函数中调用重载函数时,在 ES6 类上设置属性不起作用