在这个问题上,有人在评论中建议我不要使用malloc的结果。即,我应该这样做:

int *sieve = malloc(sizeof(*sieve) * length);

而不是:

int *sieve = (int *) malloc(sizeof(*sieve) * length);

为什么会这样?


当前回答

强制转换只适用于C++而不是C。如果您使用的是C++编译器,最好将其更改为C编译器。

其他回答

尽可能在C语言中编程时最好做的事情是:

通过C编译器编译程序,并打开所有警告-Wall并修复所有错误和警告确保没有声明为auto的变量然后使用带有-Wall和-std=C++11的C++编译器对其进行编译。修复所有错误和警告。现在再次使用C编译器进行编译。您的程序现在应该在没有任何警告的情况下编译,并且包含更少的错误。

这个过程使您可以利用C++严格的类型检查,从而减少错误的数量。特别是,此过程强制您包含stdlib.否则您将得到

malloc未在此范围内声明

并且还强制你使用malloc的结果,否则你会得到

从void*到T的转换无效*

或者你的目标类型是什么。

用C而不是C++编写的唯一好处是

C具有明确规定的ABIC++可能会生成更多代码[异常、RTTI、模板、运行时多态性]

请注意,在理想情况下,当使用C所共有的子集和静态多态特性时,第二个缺点应该消失。

对于那些觉得C++严格规则不方便的人,我们可以使用带有推断类型的C++11特性

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...

正如其他人所说的,它不是C所必需的,而是C++所必需的。如果您认为要使用C++编译器编译C代码,无论出于何种原因,都可以使用宏,例如:

#ifdef __cplusplus
# define MALLOC(type) ((type *)malloc(sizeof(type)))
# define CALLOC(count, type) ((type *)calloc(count, sizeof(type)))
#else
# define MALLOC(type) (malloc(sizeof(type)))
# define CALLOC(count, type) (calloc(count, sizeof(type)))
#endif
# define FREE(pointer) free(pointer)

这样,您仍然可以以非常紧凑的方式编写它:

int *sieve = MALLOC(int); // allocate single int => compare to stack int sieve = ???;
int *sieve_arr = CALLOC(4, int); // allocate 4 times size of int => compare to stack (int sieve_arr[4] = {0, 0, 0, 0};
// do something with the ptr or the value
FREE(sieve);
FREE(sieve_arr);

它将为C和C++编译。

TL;博士

int *sieve = (int *) malloc(sizeof(int) * length);

有两个问题。类型转换,并且您使用类型而不是变量作为sizeof的参数。相反,请这样做:

int *sieve = malloc(sizeof *sieve * length);

长版本

不您不必计算结果,因为:

这是不必要的,因为在这种情况下,void*会自动安全地升级为任何其他指针类型。它给代码增加了混乱,强制转换不太容易阅读(特别是如果指针类型很长)。这会让你重复自己,这通常是不好的。如果您忘记包含<stdlib.h>,它可能会隐藏错误。这可能会导致崩溃(或者更糟糕的是,直到稍后在代码的某个完全不同的部分发生崩溃)。考虑如果指针和整数的大小不同会发生什么;然后你通过投射来隐藏警告,可能会丢失返回地址的一部分。注意:从C99开始,隐式函数从C中消失,这一点不再相关,因为没有自动假设未声明的函数返回int。

作为澄清,请注意我说的是“你不需要投”,而不是“你不必投”。在我看来,这是一个失败的演员阵容,即使你做得对。这样做根本没有好处,但有一系列潜在的风险,包括演员阵容表明你不知道这些风险。

另外,正如评论家指出的,上面提到的是直接C,而不是C++。我非常坚信C和C++是独立的语言。

此外,代码不必要地重复类型信息(int),这可能会导致错误。最好取消引用用于存储返回值的指针,将两者“锁定”在一起:

int *sieve = malloc(length * sizeof *sieve);

这还将长度移到前面以增加可见性,并删除sizeof的多余括号;只有当参数是类型名时,才需要它们。许多人似乎不知道(或忽略)这一点,这使得他们的代码更加冗长。记住:sizeof不是函数!:)


虽然在一些罕见的情况下,将长度移到前面可能会增加可见性,但也应注意,在一般情况下,最好将表达式写成:

int *sieve = malloc(sizeof *sieve * length);

在这种情况下,由于首先保持sizeof,确保乘法至少使用size_t数学运算完成。

比较:malloc(sizeof*sive*length*width)与malloc,当width和length的类型小于size_t时,第二个可能会溢出长度*宽度。

您不需要强制转换malloc的结果,因为这样做会给代码增加无意义的混乱。

人们之所以选择malloc的结果,最常见的原因是他们不确定C语言是如何工作的。这是一个警告信号:如果你不知道特定的语言机制是如何工作的,那么就不要猜测。查找或询问堆栈溢出。

一些意见:

空指针可以转换为任何其他指针类型,而无需显式转换(C11 6.3.2.3和6.5.16.1)。但是,C++不允许在void*和另一个指针类型之间进行隐式转换。因此,在C++中,演员阵容是正确的。但是如果你用C++编程,你应该使用new而不是malloc()。而且您永远不应该使用C++编译器编译C代码。如果需要使用相同的源代码同时支持C和C++,请使用编译器开关来标记差异。不要试图用相同的代码来满足两种语言标准,因为它们不兼容。如果C编译器因为忘记包含标头而找不到函数,则会出现编译器/链接器错误。因此,如果您忘记包含<stdlib.h>,这没什么大不了的,您将无法构建您的程序。在遵循超过25年的标准版本的古代编译器上,忘记包含<stdlib.h>将导致危险行为。因为在那个古老的标准中,没有可见原型的函数隐式地将返回类型转换为int。显式地从malloc转换结果将隐藏这个错误。但这真的不是问题。你不是在使用一台25年前的计算机,那么你为什么要使用一台有25年历史的编译器呢?

现在不需要强制转换malloc()返回的值,但我想补充一点,似乎没有人指出:

在古代,也就是说,在ANSI C提供void*作为指针的通用类型之前,char*是这种用法的类型。在这种情况下,强制转换可以关闭编译器警告。

参考:C常见问题解答