为什么会这样工作:从std :: string函数返回Cstring,并调用c_str()
我们最近在大学做了一个讲座,我们的教授告诉我们在用不同的语言进行编程时要注意不同的事情。 以下是C ++中的一个例子:
std::string myFunction() { return "it's me!!"; } int main(int argc, const char * argv[]) { const char* tempString = myFunction().c_str(); char myNewString[100] = "Who is it?? - "; strcat(myNewString, tempString); printf("The string: %s", myNewString); return 0; }
为什么这个失败的想法是return "it's me!!"
用char []隐式地调用std :: string构造函数。 这个string从函数返回,函数c_str()
返回一个指向std::string
的数据的指针。
由于从函数返回的string在任何地方都没有被引用,所以应该立即释放它。 那是理论。
但是,让这个代码运行没有问题。 会好奇听到你的想法。 谢谢!
你的分析是正确的。 你有什么是未定义的行为 。 这意味着几乎可以发生任何事情。 在你的情况下,用于string的内存看起来虽然是解除分配,但在访问时仍保留原来的内容。 这经常发生,因为操作系统不清除取消分配的内存。 它只是标记为可供将来使用。 这不是C ++语言必须处理的事情:它实际上是一个OS实现细节。 就C ++而言,适用于全部“未定义的行为”。
我想取消分配并不意味着内存清理或调零。 显然这可能会导致其他情况下的段错误。
正如其他人所提到的,根据C ++标准,这是未定义的行为。
这个“工作”的原因是因为内存已经回馈给堆pipe理器,以便以后重用。 内存还没有回到操作系统,因此仍然属于这个过程。 这就是为什么访问释放的内存不会导致分段错误。 但问题依然存在,现在你的程序的两个部分(你的代码和堆pipe理器或新的所有者)正在访问他们认为独特属于他们的内存。 这件事迟早会搞砸的。
我认为原因是堆栈内存没有被重写,所以它可以得到原始数据。 我创build了一个testing函数,并在strcat之前调用它。
std::string myFunction() { return "it's me!!"; } void test() { std::string str = "this is my class"; std::string hi = "hahahahahaha"; return; } int main(int argc, const char * argv[]) { const char* tempString = myFunction().c_str(); test(); char myNewString[100] = "Who is it?? - "; strcat(myNewString, tempString); printf("The string: %s\n", myNewString); return 0; }
并得到结果:
The string: Who is it?? - hahahahahaha
这certificate了我的想法。
string解除分配的事实并不一定意味着内存不再可用。 只要你不做任何事情可以覆盖它,内存仍然是可用的。
如上所述 – 这是不可预测的行为。 它不适用于我(在debuggingconfiguration中)。 std :: string析构函数在赋值给tempString之后立即调用 – 当使用临时string对象的expression式结束时。 离开tempString指向一个释放的内存(在你的情况下,仍然包含“这是我!!”文字)。
巧合得出你的结果,你不能断定没有问题。
还有其他的手段来检测“问题”:
- 静态分析。
- Valgrind会抓住这个错误,告诉你这两个违规行为(试图从释放zone -by strcat中复制)以及导致释放的释放。
Invalid read of size 1
at 0x40265BD: strcat (mc_replace_strmem.c:262) by 0x80A5BDB: main() (valgrind_sample_for_so.cpp:20) [...] Address 0x5be236d is 13 bytes inside a block of size 55 free'd at 0x4024B46: operator delete(void*) (vg_replace_malloc.c:480) by 0x563E6BC: std::string::_Rep::_M_destroy(std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.13) by 0x80A5C18: main() (basic_string.h:236) [...]
- 唯一的方法就是certificate程序是正确的。 但程序语言真的很难,而C ++则更难。
除非我错过了一些东西,否则我认为这是一个范围问题。 myFunction()返回一个std :: string。 string对象不是直接分配给一个variables。 但是直到main()结束,它仍然在范围内。 所以,tempString将指向内存中完全有效且可用的空间,直到main()代码块结束,此时tempString也将落入作用域。
实际上,string文字有静态的存储时间。 它们被封装在可执行文件本身中。 它们不在堆栈上,也不是dynamic分配的。 在通常情况下,这是正确的,这将是指向无效的内存和未定义的行为,但是对于string,内存是在静态存储,所以它将永远是有效的。