为什么 Kernighan 和 Ritchie 包含看似不必要的类型转换?

Why do Kernighan and Ritchie include seemingly unnecessary typecasts?

第二版。我正在查看第 6.6 节中的散列 table 示例。我找到了完整的源代码转录 here。这是我困惑的部分:

struct nlist *np;

if((np=lookup(name))==NULL){
    np=(struct nlist *)malloc(sizeof(*np));

为什么在最后一行强制转换为 (struct nlist *)?我可以删除它而不会收到任何编译器警告。

我同样困惑

free((void *)np->def);

这些是否旨在以某种方式提高可读性?

演员表已弃用。 void * 可以直接赋值给任何指针类型;你实际上什至应该这样做。 K&R 在某些方面有点过时,您绝对应该获得更新的东西(对于更新的标准 - C99 向上)。

参见 n1570:6.3.2.3/1。

尽管这里有大量张贴者的意见,他们会立即跳到任何带有不必要(但无害)强制转换的代码 malloc(),但事实是这无关紧要。是的,void * 的赋值不需要转换,但也不是禁止的,关于将它留在或取出它的争论真的没有那么强烈。

有更重要的事情需要花费脑细胞。没关系。

转换 malloc 的结果在 C 的一些前 ANSI 方言中是必要的,并且在 K&R2 中保留了用法(它涵盖了 1989 年 ANSI C 标准的语言)。

本书的勘误表中提到了这一点。我通过 Dennis Ritchie's home page, which isn't currently available (I hope AT&T hasn't permanently removed it), but I found it elsewhere 看到了它:

142(§6.5, toward the end): The remark about casting the return value of malloc ("the proper method is to declare ... then explicitly coerce") needs to be rewritten. The example is correct and works, but the advice is debatable in the context of the 1988-1989 ANSI/ISO standards. It's not necessary (given that coercion of void * to ALMOSTANYTYPE * is automatic), and possibly harmful if malloc, or a proxy for it, fails to be declared as returning void *. The explicit cast can cover up an unintended error. On the other hand, pre-ANSI, the cast was necessary, and it is in C++ also.

为了使该示例在今天完全正确,您必须输入

#include <stdlib.h>

所以 你得到了 malloc(3) 的正确原型。在这种情况下,是否进行强制转换并不重要,因为 malloc 在那里被声明为 returning void *,并且不需要从这种类型强制转换为另一种指针类型(但是如果你想要的)。

今天,最好不要进行转换,因为您可以隐藏一个非常频繁的错误。如果您 进行转换并且不向编译器提供 malloc 的原型,编译器假定 malloc 默认声明为 int malloc();(returning int 而不是 void *,并采用未指定数量的参数)并且您希望(正如您明确说明的那样)将 int 转换为指针。编译器将调用 malloc 并采用 假设的 int 结果 (32 位值,不是实际的,由 malloc 编辑的 64 位 return ---取决于体系结构调用约定这些值可以相关或不相关,但它们总是不同的,因为 int space 小于指针 space) 作为 return 值,将其转换盲目地使用你建议的类型(没有警告,因为你已经明确地进行了类型转换,添加缺失的 32 位来完成一个完整的 64 位指针 ---如果整数类型的大小与POINTER TYPES---,你可以在没有它们的 64 位平台上检查它,或者在大内存模型的旧 MS-DOS 编译器中,它们也没有)和 hide真正的问题(这是你没有提供一个合适的#include文件)如果它能工作你会很幸运,因为这意味着所有由malloc编辑的虚拟指针return都在下面0x100000000 限制(这在 64 位英特尔架构中,让我们在 64 位大端中看到体系结构)这是未定义行为的实际来源,你应该期待。

通常情况下,对于现代编译器,您可能会在使用没有声明的函数时收到某种警告(如果您没有提供原型),但编译器将编译程序并生成可执行程序,可能不是一个你想要的。这是 C 和 C++ 语言之间的主要区别之一(如果您之前没有为它声明原型,C++ 不允许您使用函数调用编译代码,所以您会得到一个错误,而不是可能的警告,如果你得到我上面提到的无效 malloc)

这种错误很难定位,这就是那些提倡不强制转换的人实际上这样做的原因。