malloc不同方式的区别
Difference between different ways of malloc
struct node{
int w;
int x;
}
struct node *item;
- 请解释这些说法的区别。
我读作不需要 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 指向 void
或 void *
的指针。即使没有转换,它也会自动转换为正确的类型。事实上,有几个人(包括我自己)会强烈建议您永远不要使用类型转换。有关详细信息,请阅读 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
优于malloc
,sizeof(*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 标准之前,malloc
、calloc
和 realloc
都返回 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 行将自动正确地使用更新后的类型。
struct node{
int w;
int x;
}
struct node *item;
- 请解释这些说法的区别。
我读作不需要 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 指向 void
或 void *
的指针。即使没有转换,它也会自动转换为正确的类型。事实上,有几个人(包括我自己)会强烈建议您永远不要使用类型转换。有关详细信息,请阅读 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
优于malloc
,sizeof(*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 标准之前,
malloc
、calloc
和 realloc
都返回 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 行将自动正确地使用更新后的类型。