【发布时间】:2010-12-07 13:41:23
【问题描述】:
const char* src = "你好";
调用 strlen(src); 返回大小 5...
现在说我这样做:
char* dest = new char[strlen(src)];
strcpy(dest, src);
这似乎不应该起作用,但是当我输出所有内容时,它看起来都是正确的。看来我最后没有为空终止符分配空间......这是对的吗?谢谢
【问题讨论】:
const char* src = "你好";
调用 strlen(src); 返回大小 5...
现在说我这样做:
char* dest = new char[strlen(src)];
strcpy(dest, src);
这似乎不应该起作用,但是当我输出所有内容时,它看起来都是正确的。看来我最后没有为空终止符分配空间......这是对的吗?谢谢
【问题讨论】:
您没有为终结者分配空间是正确的,但是不这样做并不一定会导致您的程序失败。您可能正在覆盖堆上的以下信息,或者您的堆管理器会将分配大小四舍五入为 16 字节的倍数或其他内容,因此您不一定会看到此错误的任何明显影响。
如果您在Valgrind 或其他堆调试器下运行您的程序,您或许能够更快地检测到这个问题。
【讨论】:
是的,您应该至少分配 strlen(src)+1 个字符。
【讨论】:
这似乎不应该起作用,但是当我输出所有内容时,它看起来都正确。
欢迎来到未定义行为的世界。当你这样做时,任何事情都可能发生。你的程序会崩溃,你的电脑会崩溃,你的电脑会爆炸,demons can fly out of your nose。
最糟糕的是,你的程序可以运行得很好,看起来它运行正常,直到有一天它开始吐出垃圾,因为它在某处覆盖敏感数据,因为在某处,有人为其分配了一个太少的字符数组,现在你已经损坏了堆,并且在一百万英里外的某个点出现了段错误,或者更糟糕的是,你的程序很高兴地伴随着损坏的堆而运行,并且你的函数在损坏的信用卡号上运行,你得到了巨大的麻烦。
即使看起来有效,但实际上并没有。那是未定义的行为。避免它,因为你永远无法确定它会做什么,即使你尝试它时它的作用是好的,但在另一个平台上可能就不行了。
【讨论】:
我读过的最好的描述(在stackoverflow上)是这样的:
如果限速为 50,而您以 60 的速度开车。您可能会走运而没有得到罚单,但有一天可能不是今天,也可能不是明天,但总有一天那个警察会等您。到那一天你会付出,你会付出惨痛的代价。
如果有人能找到原文,我宁愿指出他们比我的解释更有说服力。
【讨论】:
strcpy 将复制空终止字符以及所有其他字符。
因此,您将hello + 1 的长度(即 6)复制到大小为 5 的缓冲区中。
但是,这里存在缓冲区溢出,并且覆盖不属于您自己的内存将产生未定义的结果。
【讨论】:
或者,您也可以使用 dest = strdup(src) 为字符串分配足够的内存 + 1 用于空终止符(+1 用于 Juliano 的回答)。
【讨论】:
strdup() 是非标准函数,可能不适用于任何给定平台”的评论。
这就是为什么您应该始终、始终、始终在任何看起来可以工作的 C 程序上运行 valgrind。
【讨论】:
是的,每个人都谈到了重点;你不能保证失败。事实上,空终止符通常为 0,而 0 是位于任何特定内存地址中的非常常见的值。所以它恰好起作用。您可以通过获取一组内存,向其中写入一堆垃圾然后在其中写入该字符串并尝试使用它来测试这一点。
无论如何,我在这里看到的主要问题是你在谈论 C 但你有这行代码:
char* dest = new char[strlen(src)];
这不会在任何标准 C 编译器中编译。 C 中没有 new 关键字。那是 C++。在 C 中,您将使用其中一种内存分配函数,通常是malloc。我知道这看起来很琐碎,但实际上并非如此。
【讨论】:
strlen() 和 strcpy() 是 C 函数的事实)。分配并不重要 - 重要的部分是不正确的分配长度和未定义的行为。