4

const char* src = "你好";

调用strlen(src);返回大小 5...

现在说我这样做:

char* dest = new char[strlen(src)];
strcpy(dest, src);

这似乎不应该起作用,但是当我输出所有内容时,它看起来都是正确的。看起来我最后没有为空终止符分配空间......这是对的吗?谢谢

4

8 回答 8

15

您没有为终结器分配空间是正确的,但是不这样做并不一定会导致您的程序失败。您可能正在覆盖堆上的以下信息,或者您的堆管理器会将分配大小四舍五入为 16 字节的倍数或其他内容,因此您不一定会看到此错误的任何明显影响。

如果您在Valgrind或其他堆调试器下运行您的程序,您可能能够更快地检测到这个问题。

于 2009-10-17T03:55:38.997 回答
11

是的,您应该至少分配 strlen(src)+1 个字符。

于 2009-10-17T03:47:51.303 回答
7

这似乎不应该起作用,但是当我输出所有内容时,它看起来都是正确的。

欢迎来到未定义行为的世界。当你这样做时,任何事情都可能发生。你的程序会崩溃,你的电脑会崩溃,你的电脑会爆炸,恶魔会从你的鼻子里飞出来

最糟糕的是,你的程序可以运行得很好,看起来它运行正常,直到有一天它开始吐出垃圾,因为它在某处覆盖敏感数据,因为在某个地方,有人为他们的数组分配了一个太少的字符,并且现在您已经损坏了堆,并且在一百万英里外的某个点出现了段错误,或者更糟糕的是,您的程序愉快地随着损坏的堆一起运行,并且您的函数在损坏的信用卡号上运行,您遇到了巨大的麻烦。

即使它看起来有效,它也没有。那是未定义的行为。避免它,因为你永远无法确定它会做什么,即使你尝试它时它的作用还可以,但在另一个平台上可能就不行了。

于 2009-10-17T03:57:48.883 回答
3

我读过的最好的描述(在stackoverflow上)是这样的:

如果限速为 50,而您以 60 的速度开车。您可能会很幸运没有得到罚单,但有一天可能不是今天,也可能不是明天,但有一天那个警察会等你。在那一天,你将付出代价,你将付出沉重的代价。

如果有人能找到原件,我宁愿指出他们比我的解释更有说服力。

于 2009-10-17T04:02:23.597 回答
2

strcpy将复制空终止字符以及所有其他字符。

因此,您将长度hello为 6 的 + 1 复制到大小为 5 的缓冲区中。

但是,您在此处有缓冲区溢出,并且覆盖不属于您自己的内存将产生未定义的结果。

于 2009-10-17T13:35:15.937 回答
1

或者,您也可以使用 dest = strdup(src) 它将为字符串分配足够的内存 + 1 用于空终止符(+1 用于 Juliano 的答案)。

于 2009-10-17T04:00:59.877 回答
1

这就是为什么你应该总是,总是,总是在任何看起来可以工作的 C 程序上运行valgrind 。

于 2009-10-17T04:17:22.333 回答
1

是的,每个人都谈到了重点;你不能保证失败。事实上,空终止符通常为 0,而 0 是位于任何特定内存地址中的非常常见的值。所以它恰好起作用。您可以通过获取一组内存,向其中写入一堆垃圾然后在其中写入该字符串并尝试使用它来测试这一点。

无论如何,我在这里看到的主要问题是你在谈论 C 但你有这行代码:

char* dest = new char[strlen(src)];

这不会在任何标准 C 编译器中编译。C中没有new关键字。那是C++。在 C 中,您将使用其中一种内存分配函数,通常是malloc. 我知道这看起来很挑剔,但实际上并非如此。

于 2009-10-17T04:34:17.353 回答