C语言 不同方式之间的差异

  • 本文关键字:之间 方式 语言 c 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 是首选选项(至少对我和其他一些 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 标准之前,malloccallocrealloc 都返回char *,因此如果目标不是不同的指针类型,则需要强制转换。 不幸的是,即使不再需要,这个习惯仍然存在

在这种特殊情况下,所有方法都会给你完全相同的结果。

1:

item = malloc(sizeof(*item));

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

阿拉伯数字:

item = malloc(sizeof(struct node));

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

3:

malloc返回指向 voidvoid * 的指针。即使没有强制转换,这也会自动转换为正确的类型。事实上,有几个人(包括我自己(会强烈建议你永远不要使用类型转换。有关更多详细信息,请阅读此问题的已接受答案

让我们看看这些:

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 行将自动与更新的类型正常工作。

相关内容

  • 没有找到相关文章

最新更新