2

我不明白 C 中动态分配的字符串是如何工作的。下面,我有一个例子,我认为我已经创建了一个指向字符串的指针并为其分配了 0 内存,但我仍然可以给它字符。我显然做错了什么,但是什么?

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
char *str = malloc(0);
int i;
str[i++] = 'a';
str[i++] = 'b';
str[i++] = '\0';
    printf("%s\n", str);

    return 0;
}
4

5 回答 5

3

你正在做的是未定义的行为。它现在可能看起来可以工作,但不是必须工作,如果有任何变化,它可能会中断。

malloc通常返回您可以使用的给定大小的内存块。在您的情况下,碰巧在您正在触摸的那个块之外有有效的内存。那个记忆不应该被触动;malloc可能会将该内存用于内部管理,它可能会将该内存作为某个malloc调用的结果,或者完全是其他东西。不管它是什么,它都不是你的,触摸它会产生不确定的行为。

于 2013-08-14T05:22:37.833 回答
0

当前 C 标准的第 7.20.3 节部分说明:

“如果请求的空间大小为零,则行为是实现定义的:要么返回空指针,要么行为就像大小是某个非零值,但返回的指针不应用于访问对象。”

这将由实现定义。它要么可以发送一个 NULL 指针,要么可以发送一个无法引用的东西

于 2013-08-14T05:24:57.087 回答
0

通过这样做malloc(0),您正在创建一个NULL可以传递给的指针或唯一指针free。那条线没有错。当您执行指针算术并将值分配给尚未分配的内存时,问题就出现了。因此:

str[i++] = 'a'; // Invalid (undefined).
str[i++] = 'b'; // Invalid (undefined).
str[i++] = '\0'; // Invalid (undefined).

printf("%s\n", str); // Valid, (undefined).

做两件事总是好的:

  1. 不要malloc0字节。
  2. 检查以确保您malloc编辑的内存块有效。

...要检查请求的内存块malloc是否有效,请执行以下操作:

if ( str == NULL ) exit( EXIT_FAILURE );

...在您致电malloc.

于 2013-08-14T05:26:46.283 回答
0

您正在覆盖未分配的内存。这可能看起来像工作。但是当你调用free堆函数试图返回内存块的地方时,你就有麻烦了。

每个malloc()返回的内存块都有一个标题和一个尾部。这些结构至少保持分配内存的大小。有时你有额外的守卫。您正在覆盖此堆内部结构。这就是为什么free()会抱怨或崩溃的原因。

所以你有一个未定义的行为。

于 2013-08-14T05:32:11.440 回答
0

你的 malloc(0) 是错误的。正如其他人指出的那样,最终可能会或可能不会分配一点内存,但无论 malloc 实际上对 0 做了什么,在这个简单的示例中,您都应该分配至少 3*sizeof(char) 字节的内存。

所以在这里我们有一个正确的麻烦。假设您为字符串分配了 20 个字节,然后用 19 个字符和一个 null 填充它,从而填充了内存。到现在为止还挺好。但是,请考虑您想在字符串中添加更多字符的情况;你不能把它们放在适当的位置,因为你只分配了 20 个字节并且你已经使用了它们。您所能做的就是分配一个全新的缓冲区(例如 40 个字节),将原来的 19 个字符复制到其中,然后在末尾添加新字符,然后释放原来的 20 个字节。听起来效率低下不是吗。而且它效率低下,分配内存需要大量工作,而且与其他语言(例如 C++)相比,您只需连接 str1 + str2 即可。

除了在底层,这些语言必须做完全相同的事情,即分配更多内存和复制现有数据。如果有人关心高性能,那么 C 会让您更清楚地花时间在哪里,而 C++、Java、C# 之类的语言则将昂贵的操作隐藏在方便使用的类后面。这些类可能非常聪明(例如,分配比严格必要的更多的内存以防万一),但如果您有兴趣从硬件中提取最佳性能,则必须做好准备。

这类问题是 Facebook 和 Twitter 等运营商在发展服务方面遇到困难的原因。迟早那些方便但低效的类方法加起来是不可持续的。

于 2013-08-14T05:55:53.570 回答