基于这个老问题,malloc返回一个指向void的指针,
is automatically and safely promoted to any other pointer type
但在阅读K&R时,我发现了以下代码
1 2 3 4 5
| char *strdup (char *s )
{
char *p ;
/* make a duplicate of s */
p = (char *) malloc(strlen(s )+1) |
有什么区别?
- 我认为K&R是一本旧书,显然旧的编译器没有正确地完成这一任务。
- C的非常老的版本没有(void*),所以需要一个演员表来传递"lint"。编译器从不需要强制转换。
- 在C++中,而不是在C.中,尽管使用malloc()对EDCOX1〔2〕和EDCOX1〔3〕将被劝阻。如果编译器向您发出警告,则需要时间切换编译器。
- 这是第二版的(因为它使用原型)。不必要的演员阵容有点令人惊讶。
- 在任何版本的C中都不需要这种特殊的强制转换,因为malloc()的返回类型要么是(void*)要么是(char*),所以这两种方式都不需要C消息。"林特"究竟会做什么是另一回事。(C++ Otoh重新定义(空*)从"指针到任何东西"到"无指针",所以它并不真正计数。
- @用户3710044:正如我在回答中所说的,正如我所说的,某些古代的不兼容编译器完全有可能遇到这种问题。不过,那是在那时,现在,幸运的是找到了一个有这个问题的编译器。
- 请注意,有关促销的报价并不完全正确。void *自动安全地升级为指向对象类型的任何指针,而不是任何函数指针。
对于任何符合C89或更高版本的实施,不需要强制执行malloc()的结果。
c编程语言勘误表第二版中提到了malloc()的转换结果:
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.
(该链接不再有效。已故的丹尼斯·里奇的主页现在在这里;它指向书主页的更新位置,但链接也无效。)
最佳实践是将malloc()调用的结果直接分配给指针对象,并让来自void*的隐式转换处理类型一致性。malloc()的声明需要通过#include 可见。
(很难想象您可能会在需要显式转换的环境中使用malloc()。我能想到的唯一情况是将malloc()的结果直接传递给一个变量函数,该函数需要一个指针类型的参数,而不是void*。在这种异常和人为的情况下,可以通过将结果赋给指针对象并将该对象的值传递给函数来避免强制转换。)
EDOCX1的8个结果的唯一好的原因是(a)与C++的兼容性(但是需要编写编译为C和AS C++的代码比您期望的更高),和(b)与ANSI C编译器之间的兼容性(但是满足这些编译器的需要是罕见的并且变得越来越稀少)。
首先:K&R C很古老。
其次:我不知道这是否是该示例中的完整代码,但在K&R C中,如果没有另外声明,则假定函数具有int返回值。如果他不声明malloc,就编译器而言,这意味着它返回int,因此是强制转换。请注意,即使在C89(最早的标准化版本)中,这也是未定义的行为,而且实践极为糟糕;但他这样做可能是为了简洁,或者可能是因为懒惰,或者是出于其他历史原因;我不知道K&R C的所有错综复杂之处,也可能是void*到char*类型的类型不是IMP。那就舔回去。
我应该补充一点,这是一个非常严重的问题,因为int和void*可能有不同的大小(而且通常是这样——最常见的例子是大多数x86 code,其中指针是64位,但int是32位)。
更新:@keitththompson已经通知我代码来自第二版,并且在那里声明了malloc。这一点(与第一版不同)仍然相关,如果在某些地方非常过时。
我猜演员阵容可能是为了与不符合标准的编译器兼容而进行的,而这在当时最重要的是(最多的是)?]编译器还没有完全符合要求。现在,你必须想办法找到一个需要强制转换的编译器(而不是,C++编译器不算数;它们是C++编译器,而不是C编译器)。
- 代码来自第二版(它使用的是原型,在第一版发布时不存在)。演员阵容是不必要的。
- @KeithThompson:可能是为了与不兼容的编译器兼容而添加的。当时,它很重要;现在,几乎每个编译器都实现C89(或者至少大部分实现)。
- 它也在勘误表中(见我的答案)。
- @基思汤普森:很好,我不知道。我认为这解决了"Cast Malloc"和"Don't Cast Malloc"的争论——标准委员会自己说,Cast是/可能是有害的。有一天我需要阅读K&R C,这样我才能帮助其他人更好地学习C(当他们引用C时)。
- K&R eratta列表不是由委员会编写的。
- @基思汤普森:啊,那就不要管那部分了。不过,我还是同意这种看法。
想要(或可能需要)交叉编译C和C++的人经常会抛出EDOCX1 0,因为C++不会自动地促进EDCOX1 1。
正如其他人在过去的好日子里指出的那样,当函数原型是可选的时,如果没有看到具有潜在灾难性运行时结果的原型,编译器会假设malloc()返回int。
我试图从这一论点中看到一些观点,但我简直无法相信这种情况会有规律地发生,这就要求燃烧的人们必须把埃多克斯一号(0号)扔出去。
看看在这个网站上铸造malloc(.)的任何问题,不管是关于什么,有些人会愤怒地要求他们移除他们的铸造,好像他们会让他们的眼睛流血一样。
注:这对我们没有好处,也没有错,所以我们把malloc(.)放在杂乱的地方。事实上,我所知道的唯一(真实)的论点是,它引导人们去看演员,而不是去看论点——这才是最重要的!
我今天在这里看到了代码:
1
| int** x =(int**)malloc(sizeof(int)*n ); |
很容易让人产生一种虚假的安全感。sizeof(int)正在做(或在这种情况下没有做)工作,以确保从malloc(.)返回的内容是正确的。(int**)不起作用。
我不是说"施展它"。我在呼吁冷静。我当然认为,如果一个新手没能打电话给free(.),我们会在进入到malloc(.)的细微差别之前给他们上一课。