我们最近在大学进行了一次演讲,我们的教授告诉我们在使用不同语言编程时需要注意的不同事项。 以下是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构造函数。该字符串从函数返回,函数c_str()
返回指向std::string
数据的指针。
由于函数返回的字符串未在任何地方引用,因此应立即取消分配。这就是理论。
然而,让这段代码运行没有问题。 很想知道你的想法。 谢谢!
答案 0 :(得分:45)
您的分析是正确的。你拥有的是未定义的行为。这意味着几乎任何事情都可能发生。在您的情况下,用于字符串的内存似乎虽然取消分配,但在访问时仍保留原始内容。这通常是因为操作系统没有清除解除分配的内存。它只是标记为可供将来使用。这不是C ++语言必须处理的事情:它实际上是一个OS实现细节。就C ++而言,适用所有“未定义的行为”都适用。
答案 1 :(得分:5)
我想解除分配并不意味着内存清理或归零。显然,这可能导致其他情况下的段错误。
答案 2 :(得分:3)
正如其他人所提到的,根据C ++标准,这是未定义的行为。
这个“工作”的原因是因为内存已经被返回给堆管理器,堆管理器保留它以供以后重用。内存已不已返回操作系统,因此仍属于该进程。这就是访问释放内存不会导致分段错误的原因。然而问题仍然是,现在程序的两个部分(您的代码和堆管理器或新所有者)正在访问他们认为唯一属于它们的内存。这迟早会破坏事物。
答案 3 :(得分:3)
我认为原因是堆栈内存没有被重写,所以它可以获取原始数据。我创建了一个测试函数并在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
这证明了我的想法。
答案 4 :(得分:1)
字符串被释放的事实并不一定意味着内存不再可访问。只要你不做任何可能覆盖它的事情,内存仍然可用。
答案 5 :(得分:1)
如上所述 - 这是不可预测的行为。它对我不起作用(在Debug配置中)。 在赋值给tempString后立即调用std :: string析构函数 - 当使用临时字符串对象的表达式完成时。 让tempString指向已释放的内存(在你的情况下仍然包含“它就是我!!”文字)。
答案 6 :(得分:1)
你无法通过巧合获得结果来得出结论没有问题。
还有其他方法可以检测'问题':
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)
[...]
答案 7 :(得分:-1)
实际上,字符串文字具有静态存储持续时间。它们包含在可执行文件本身内。它们不在堆栈中,也不是动态分配的。在通常的情况下,这将指向无效的内存并且是未定义的行为是正确的,但是对于字符串,内存在静态存储中,因此它总是有效的。
答案 8 :(得分:-1)
除非我遗漏了什么,否则我认为这是一个范围问题。 myFunction()返回一个std :: string。字符串对象不直接分配给变量。但它一直在范围内,直到main()结束。因此,tempString将指向内存中完全有效且可用的空间,直到main()代码块结束,此时tempString也将超出范围。