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

时间:2015-04-23 23:21:10

标签: c casting coding-style kernighan-and-ritchie

第二版。我正在查看6.6节中的哈希表示例。我发现完整的来源转录了here。这是我令人费解的部分:

struct nlist *np;

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

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

我同样感到困惑

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

这些是否有助于以某种方式提供可读性?

4 个答案:

答案 0 :(得分:4)

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

本书的勘误表中提到了这一点。我通过Dennis Ritchie's home page看到了它,目前还没有(我希望AT& T没有永久删除它),但我发现它elsewhere

  

142(§6.5,接近结尾):关于转换malloc的返回值(“正确的方法是声明...然后明确强制”)的注释需要重写。这个例子是正确和有效的,但这个建议在1988-1989 ANSI / ISO标准的背景下是值得商榷的。这是没有必要的(假设对* ALMOSTANYTYPE *的void *是自动的),并且如果malloc或其代理不能被声明为返回void *,则可能是有害的。显式强制转换可以掩盖意外错误。另一方面,在ANSI之前,演员是必要的,它也在C ++中。

答案 1 :(得分:3)

尽管有大量海报在这里的观点会立即跳出任何代码与malloc()不必要(但无害)的演员,但事实是它并不重要。是的,来往于void *的任务不需要投射,但也不允许投射,并且将其留下或取出的论据实际上并不强烈。

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

答案 2 :(得分:2)

为了让这个例子今天完全正确,你必须把

#include <stdlib.h>

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

今天,它更好不进行投射,因为你可以隐藏更多的错误。如果您执行转换并且没有为malloc 的编译器提供原型,则编译器假定malloc默认声明为int malloc();(返回int而不是一个void *,并且取一个未指定数量的参数)并且您希望(正如您明确声明的那样)将int转换为指针。编译器将调用malloc并获取假设的int结果(32位值,而不是实际的,malloc返回的64位 - 取决于体系结构调用约定,这些值可以相关或不,但它们总是不同,因为int空间小于指针空间)作为返回值,将它盲目地转换为您建议的强制转换类型(withoug警告,因为您已明确地放置了强制转换,添加缺少32位来完成一个完整的64位指针--- 这是完全危险的,如果整数类型不是同样大小的指针类型 ---,你可以在64位平台上查看它们不是&#39 ;或者,在大型内存模型中的旧MS-DOS编译器中,它们也不是)并且隐藏真正的问题(这是你没有提供正确的{{1}如果它有效,你会很幸运,因为这意味着malloc返回的所有虚拟指针都低于#include限制(这在64位intel架构中,让我们在64b中看到)它是大端架构)这是你应该期待的未定义行为的实际来源。

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

这种错误非常难以定位,这就是倡导不投射的人实际上做错的原因。

答案 3 :(得分:1)

不推荐演员。 void *可以直接分配给任何指针类型;你甚至应该这样做。 K&amp; R在某些方面有点过时,你应该明确地获得更新的东西(对于更新的标准--C99以上)。

见n1570:6.3.2.3/1。