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 是首选选项(至少对我和其他一些 C 程序员来说是这样(。 请注意,sizeof
是运算符,而不是函数,仅当操作数是类型名称时才需要括号。 因此,您可以编写sizeof *item
而不是sizeof (*item)
- 括号除了可读性之外不会伤害任何东西。 另请注意,sizeof
不会尝试评估其参数(它不会尝试取消引用item
(;它所做的只是计算结果类型所需的字节数,并在编译时而不是运行时执行此操作。
就 C 而言,您不应该投射 malloc
的结果。 从 1989 年的标准开始,它不再需要; malloc
返回一个void *
,可以分配给任何其他指针类型,而不需要显式强制转换1。
在 C89 标准下,如果您忘记包含stdlib.h
或没有范围内malloc
声明,则转换malloc
的结果可能会禁止显示有用的诊断。 C89仍然允许隐含的int
声明;如果编译器在代码中看到没有声明的函数调用,则假定该函数返回int
值。 如果忽略了强制转换,编译器会抱怨您正在尝试为指针对象分配整数值。 使用强制转换时,编译器将在没有诊断的情况下编译代码,但你会将指针值转换为int
并再次转换回指针,这可能有效,也可能无效。
C99 标准摆脱了隐式int
声明,所以这不再是问题; 无论是否投射,如果您忘记包含stdlib.h
,您将获得诊断。 出于可读性和可维护性的原因,您仍应保留强制转换;它所做的只是添加无用的视觉混乱。 如果您更改目标的声明(从 struct foo *
更改为 struct bar *
(,那么您还必须更新 cast,这只是额外的工作,如果您忘记这样做,那么这是一个错误。 无论您对item
类型进行何种更改,item = malloc( sizeof *item )
都将始终做正确的事情,而无需任何进一步的维护。
C++ 是一种与具有不同规则的 C 语言不同的语言,不允许在没有显式强制转换的情况下将void *
分配给另一种指针类型,因此在这种情况下,您必须强制转换 malloc
的结果。 如果要从 C 迁移到 C++并且尚未重做所有动态内存管理代码(这是在C++代码中使用 malloc
的唯一借口(,则会出现此问题。当然,您不应该在C++中使用malloc
/calloc
/realloc
;使用标准容器之一,或使用 new
运算符。
1 - 在 C89 标准之前,
malloc
、calloc
和 realloc
都返回char *
,因此如果目标不是不同的指针类型,则需要强制转换。 不幸的是,即使不再需要,这个习惯仍然存在
在这种特殊情况下,所有方法都会给你完全相同的结果。
1:
item = malloc(sizeof(*item));
*item
是一个struct node
,因此sizeof(*item)
将返回struct node
的大小,并且该行将为单个节点分配内存。这有时比 2 更可取(,就像您要重构item
为不同的类型一样,您不需要更改此行。
阿拉伯数字:
item = malloc(sizeof(struct node));
这句话是不言自明的。但是,如前所述,如果要更改类型item
则需要更改此行,因为分配的内存量将不再正确(也许纯粹是运气除外(。
3:
malloc
返回指向 void
或 void *
的指针。即使没有强制转换,这也会自动转换为正确的类型。事实上,有几个人(包括我自己(会强烈建议你永远不要使用类型转换。有关更多详细信息,请阅读此问题的已接受答案
让我们看看这些:
1) item = malloc(sizeof(*item));
上面的代码是 C 中的经典方式。 使用calloc
可能更安全,因为它会将分配的结构清除为所有位零。一个有用的副作用,如果您稍后向结构中添加字段并忘记在分配的每个实例中malloc
后面的代码中初始化 ,它将节省您的费用。 我建议:
1 preferred) item = calloc(1, sizeof(*item));
下面的替代方法假设item
是指向struct node
的指针。 当您第一次编写代码时,它可能为 true,如果您稍后更改item
类型,或者如果您将代码复制到item
是不同类型的其他位置并且您忘记进行更改,则可能会变为 false。
2) item = malloc(sizeof(struct node));
最后一个选项表现出另一个问题:
3) item = (struct node *) malloc(sizeof(struct node));
转换 malloc
的返回值的问题在其他地方已经讨论了很多。 这在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)
,如上所述或在表达式或函数参数中使用,则使用强制转换。
表单 2 不好,因为您可能会意外分配错误的内存量。
表单 1 和表单 3 都允许程序员目视检查分配是否正确:
item = malloc(sizeof(*item));
// ^^^^ ^^^^^
item = (struct node *) malloc(sizeof(struct node));
// ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
一旦您了解到varname
应该与*varname
一起使用,或者(Typename *)
应该与(Typename)
一起使用,那么您更有可能快速检测到尺寸错误。
正如主线程所解释的那样,在 C89 中,表格 3 是危险的,而表格 1 是安全的。在C99及更高版本中,这一基本原理不那么令人信服,但是Form 1具有更紧凑的优点,并且需要更少的维护。如果更改了item
的类型,则任何 Form 3 行都将触发编译错误,而 Form 1 行将自动与更新的类型正常工作。