malloc不同方式的区别

Difference between different ways of malloc

struct node{
  int w;
  int x;
}
struct node *item;
  1. 请解释这些说法的区别。
  2. 我读作不需要 malloc 之后的类型转换。为什么呢?

    1) item = malloc(sizeof(*item));

    2) item = malloc(sizeof(struct node));

    3) item = (struct node *) malloc(sizeof(struct node));

在此特定情况下,所有方法都会为您提供完全相同的结果。

1:

item = malloc(sizeof(*item));

*item 是一个 struct node,因此 sizeof(*item) 将 return 一个 struct node 的大小,并且该行将为单个节点分配内存.这有时比 2) 更可取,就好像你要重构 item 为不同的类型一样,你不需要更改这一行。

2:

item = malloc(sizeof(struct node));

这一行是不言自明的。但是,如前所述,如果 item 要更改类型,您将需要更改此行,因为分配的内存量将不再正确(除非,也许是纯粹的运气)。

3:

malloc return 指向 voidvoid * 的指针。即使没有转换,它也会自动转换为正确的类型。事实上,有几个人(包括我自己)会强烈建议您永远不要使用类型转换。有关详细信息,请阅读 this question

的已接受答案

让我们看看这些:

1) item = malloc(sizeof(*item));

上面的代码是 C 中的经典方式。使用 calloc 可能更安全,因为它会将分配的结构清除为所有位零。如果您稍后将字段添加到结构中并且忘记在每个分配实例中的 malloc 后面的代码中初始化 ,这是一个有用的副作用,可以节省您的时间。我会如此推荐:

1 preferred) item = calloc(1, sizeof(*item)); 

下面的替代方案假设 item 是指向 struct node 的指针。当您第一次编写代码时它可能是真的,如果您稍后更改 item 的类型或者如果您将代码复制到 item 是不同类型的不同位置并且您忘记了做出改变。

2) item = malloc(sizeof(struct node));

最后一个选项显示了另一个问题:

3) item = (struct node *) malloc(sizeof(struct node));

转换 malloc 的 return 值的问题在其他地方已经有很多评论。它在 C++ 中是必需的,但在 C 中被认为是无用的并且可能容易出错。在某种情况下它实际上可以起到很好的作用:

#define alloc_node()  ((struct node*)calloc(1, sizeof(node)))

item = alloc_node();

这里建议使用强制转换来检测错误类型的分配。如果 item 不是指向 struct node 的指针,编译器会报错并阻止错误。

总之,calloc优于mallocsizeof(*item)优于sizeof(node node)。如果上下文不允许使用 sizeof(*dst),如上或在表达式或函数参数中,则使用强制转换。

在功能上,这三个都是等效的。

从风格上讲,1 是首选选项(至少对我和许多其他 C 程序员而言)。注意 sizeof 是运算符,不是函数,只有当操作数是类型名时才需要括号。所以你可以写 sizeof *item 而不是 sizeof (*item) - 除了可读性之外,括号不会伤害任何东西。还要注意 sizeof 不会尝试 评估 它的参数(它不会尝试取消引用 item);它所做的只是计算结果类型所需的字节数,并且它在编译时执行,而不是 运行 时。

就 C 而言,您应该而不是 转换 malloc 的结果。从 1989 年的标准开始,它不再是必需的; malloc returns a void *,可以将其分配给任何其他指针类型,而无需显式转换1

根据 C89 标准,如果您忘记包含 stdlib.h 或在范围内没有 malloc 的声明,则转换 malloc 的结果可能会抑制有用的诊断. C89 仍然允许隐式 int 声明;如果编译器在代码中看到一个没有声明的函数调用,它会假定该函数返回一个 int 值。如果您停止转换,编译器会抱怨您试图将整数值分配给指针对象。使用强制转换,编译器会在没有诊断的情况下编译代码,但您会将指针值转换为 int 并再次返回指针,这可能会或可能不会工作。

C99 标准摆脱了隐式 int 声明,因此这不再是问题;不管是否强制转换,如果您忘记包含 stdlib.h,您将得到一个诊断。出于可读性和可维护性的原因,您仍然应该取消转换;它所做的只是添加无用的视觉混乱。如果您更改目标的声明(从 struct foo *struct bar *),那么您还必须更新演员表,这只是额外的工作,如果您忘记这样做,那就是一个错误。 item = malloc( sizeof *item ) 始终 做正确的事情,无需任何进一步维护,无论您对 item 的类型进行何种更改。

C++ 是一种与 C 不同的语言,具有不同的规则,not 允许您将 void * 分配给另一个指针类型而无需显式强制转换,因此您在这种情况下, 必须转换 malloc 的结果。如果您从 C 迁移到 C++ 并且没有重做所有动态内存管理代码(这是 在 C++ 代码中使用 malloc 的借口,这就是一个问题).当然,你应该 而不是 在 C++ 中使用 malloc/calloc/realloc;使用标准容器之一,或使用 new 运算符。


1 - 在 C89 标准之前,malloccallocrealloc 都返回 char *,因此转换 如果目标是不同的指针类型则需要。不幸的是,即使不再需要,这个习惯仍然存在

Form 2 不好,因为您可能会不小心分配错误的内存量。

Form 1 和 Form 3 都允许程序员目视检查分配是否正确:

   item = malloc(sizeof(*item));
// ^^^^                 ^^^^^

   item = (struct node *) malloc(sizeof(struct node));
//        ^^^^^^^^^^^^^^^              ^^^^^^^^^^^^^

一旦您了解到 varname 应该与 *varname 一起使用,或者 (Typename *) 应该与 (Typename) 一起使用,那么您就更有可能快速检测到尺寸错误。

the main thread about this 所述,在 C89 中,Form 3 是危险的,而 Form 1 是安全的。在 C99 及更高版本中,该基本原理不那么令人信服,但是 Form 1 具有更紧凑且需要更少维护的好处。如果更改 item 的类型,那么任何 Form 3 行都会触发编译错误,而 Form 1 行将自动正确地使用更新后的类型。