我们最近在大学进行了一次讲座,我们的教授告诉我们在使用不同语言编程时要注意的不同事项。 以下是 C++ 中的示例:
std::string myFunction()
{
return "it's me!!";
}
int main()
{
const char* tempString = myFunction().c_str();
char myNewString[100] = "Who is it?? - ";
strcat(myNewString, tempString);
printf("The string: %s", myNewString);
}
失败的原因是
return "it's me!!"
使用 std::string
隐式调用 const char[]
构造函数。该字符串从函数返回,函数 c_str()
返回指向 std::string
中数据的指针。
理论是,由于从函数返回的字符串没有在任何地方被引用,因此应该立即释放它。 但是,运行此代码没有问题。这是为什么?
你的分析是正确的。你所拥有的是未定义的行为。这意味着几乎任何事情都有可能发生。在您的情况下,用于字符串的内存虽然被解除分配,但在您访问它时仍然保留原始内容。这种情况经常发生,因为操作系统没有清除已解除分配的内存。它只是将其标记为可供将来使用。这不是 C++ 语言必须处理的事情:它实际上是操作系统实现细节。就 C++ 而言,包罗万象的“未定义行为”适用。
我想释放并不意味着内存清理或清零。显然,这可能会在其他情况下导致段错误。
我认为原因是堆栈内存没有被重写,所以可以得到原始数据。我创建了一个测试函数并在 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
这证明了我的想法。
正如其他人提到的,根据 C++ 标准,这是未定义的行为。
之所以“有效”,是因为内存已返回给堆管理器,堆管理器保留该内存以供以后重用。内存尚未归还给操作系统,因此仍然属于该进程。这就是为什么访问释放的内存不会导致分段错误。然而,问题仍然存在,现在程序的两个部分(您的代码和堆管理器或新所有者)正在访问他们认为唯一属于他们的内存。这迟早会毁掉一切。
还有其他方法来检测“问题”:
静态分析。
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)
[...]
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)
[...]
唯一真正的方法是证明程序是正确的。但这对于过程语言来说确实很难,而 C++ 就更难了。