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

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

而不是:

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

为什么会这样?


当前回答

void指针是一个通用对象指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式类型转换。

然而,如果您希望相同的代码在不支持隐式转换的C++平台上完美兼容,则需要进行类型转换,因此这一切都取决于可用性。

其他回答

返回的类型为void*,可以将其转换为所需类型的数据指针,以便可以取消引用。

不强制强制强制强制转换malloc的结果,因为它返回void*,并且void*可以指向任何数据类型。

您不需要强制转换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的结果

or

为什么你不投malloc的结果

关于OP使用铸造的问题。评论本身包含指向此问题的超链接。

这在任何可能的情况下都是不恰当和不正确的。当这确实是一个人自己的编码风格的问题时,没有对也没有错。


为什么会发生这种情况?

它基于两个原因:

这个问题确实是基于意见的。从技术上讲,这个问题早在几年前就应该以观点为基础。一个“我应该”或“我不应该”或等效的“我应该吗”或“不应该”问题,如果没有自己的观点,你就无法集中回答。结束一个问题的原因之一是因为它“可能会导致基于意见的答案”,如这里所示。许多答案(包括最明显和最被接受的@unwind答案)要么完全或几乎完全基于观点(例如,如果你自己选角或重复选角,会在代码中添加一个神秘的“杂乱”,这会很糟糕),并显示出明显和专注的省略选角的倾向。他们一边争论演员阵容的冗余,但更糟糕的是,他们还争论解决由编程本身的错误/失败导致的错误——如果想使用malloc(),就不要#include<stdlib.h>。


我想对所讨论的一些观点提出一个真实的观点,而不是我个人的观点。需要特别注意以下几点:

这样一个很容易陷入自己观点的问题需要一个正反中立的答案。不只是缺点或优点。下面的答案中列出了一个很好的利弊概述:https://stackoverflow.com/a/33047365/12139179(由于这个原因,我个人认为这是迄今为止最好的答案。)


最多会遇到一个原因来解释省略强制转换的原因,即强制转换可能隐藏错误。如果有人使用隐式声明的malloc()返回int(自C99以来,隐式函数不再是标准函数)和sizeof(int)!=sizeof(int*),如本问题所示为什么这段代码在64位体系结构上是错误的,但在32位体系结构中运行良好?演员阵容会隐藏一个bug。虽然这是真的,但它只显示了故事的一半,因为省略了强制转换只能向前解决更大的错误-在使用malloc()时不包括stdlib.h。这永远不会是一个严重的问题,如果你,使用符合C99或更高版本的编译器(这是推荐的,应该是强制性的),以及当您想在代码中使用malloc()时,不要忘记包含stdlib.h,这本身就是一个巨大的错误。


有些人认为C代码符合C++,因为在C++中强制执行强制转换。首先要概括地说:用C++编译器编译C代码不是一种好的做法。C和C++实际上是两种完全不同的语言,具有不同的语义。但如果您真的想/需要使C代码符合C++,反之亦然,请使用编译器开关而不是任何强制转换。由于演员阵容被认为是多余的,甚至是有害的,所以我想把重点放在这些问题上,这些问题给出了演员阵容有用甚至必要的充分理由:https://stackoverflow.com/a/34094068/12139179https://stackoverflow.com/a/36297486/12139179https://stackoverflow.com/a/33044300/12139179


当您的代码、分配的指针的类型(以及转换的类型)发生变化时,转换可能是无效的,尽管这在大多数情况下是不可能的。然后,您还需要维护/更改所有的强制转换,如果代码中有几千个对内存管理函数的调用,那么这将真正地总结并降低维护效率。


摘要:

事实是,如果分配的指针指向基本对齐要求的对象(包括所有对象中的大多数),则根据C标准(从ANSI-C(C89/C90)开始),强制转换是冗余的。

在这种情况下,指针自动对齐,因此不需要执行强制转换:

对aligned_alloc、calloc、malloc和realloc函数的连续调用所分配的存储的顺序和连续性未指定。如果分配成功,则返回的指针将适当对齐,以便可以将其分配给具有基本对齐要求的任何类型对象的指针,然后用于访问空间分配中的此类对象或此类对象的数组ated(直到空间被显式释放)。"来源:C18,§7.22.3/1


“基本对齐是指小于或等于_Aligef(max_align_t)的有效对齐。所有存储持续时间的对象的实现应支持基本对齐。以下类型的对齐要求应为基本对齐:-所有原子、合格或不合格的基本类型;-所有原子、限定或非限定枚举类型;-所有原子、限定或非限定指针类型;-其元素类型具有基本对齐要求的所有数组类型;57)-第7条中规定为完整对象类型的所有类型;-所有结构或联合类型,其所有元素都具有具有基本对齐要求的类型,且其所有元素均没有指定非基本对齐的对齐说明符。如6.2.1所述,后面的声明可能会隐藏前面的声明。"来源:C18,§6.2.8/2

但是,如果为扩展对齐需求的实现定义对象分配内存,则需要强制转换。

扩展对齐由大于_align f(max_align_t)的对齐表示。实现定义了是否支持任何扩展对齐以及支持这些对齐的存储持续时间。具有扩展对齐要求的类型是过度对齐类型。58)来源C18,§6.2.8/3

其他一切都取决于具体的用例和个人的看法。

请注意如何教育自己。

我建议您首先仔细阅读到目前为止所做的所有答案(以及他们可能指出失败的评论),然后如果您或如果您没有将malloc()的结果应用于某个特定案例,请建立自己的观点。

请注意:

这个问题没有正确和错误的答案。这是一个风格问题,你自己决定选择哪种方式(当然,如果你不是受教育或工作所迫)。请注意这一点,不要让你上当。


最后一点:我最近投票结束了这个基于意见的问题,这是多年来确实需要的。如果您获得了关闭/重新打开特权,我也想邀请您这样做。

来自维基百科:

铸造的优点包括强制转换可以允许C程序或函数编译为C++。强制转换允许最初返回char*的1989年以前版本的malloc。如果目标指针类型发生变化,强制转换可以帮助开发人员识别类型大小的不一致,特别是如果指针声明的位置远离malloc()调用(尽管现代编译器和静态分析器可以在不需要强制转换的情况下警告此类行为)。铸造的缺点根据ANSI C标准,铸件是多余的。添加强制转换可能会掩盖将标头stdlib.h包含在找到了malloc的原型。如果没有malloc的原型,标准要求C编译器假设malloc返回一个int。如果没有强制转换,则警告为当该整数被分配给指针时发出;然而,随着演员,这个警告没有产生,隐藏了一个bug。在某些情况下体系结构和数据模型(例如64位系统上的LP64,其中long和指针为64位,int为32位),此错误可能实际上导致未定义的行为,正如隐式声明的malloc返回32位值,而实际定义的函数返回64位值。取决于调用约定和内存布局,这可能会导致堆栈损坏。这个问题不太可能在现代编译器中不被注意,因为它们统一地生成警告已使用未声明的函数,因此警告将仍然出现。例如,GCC的默认行为是显示警告:“内置的不兼容隐式声明函数”,而不管是否存在强制转换。如果指针的类型在其声明时发生了更改,则可以此外,需要更改调用和强制转换malloc的所有行。

尽管malloc不带强制转换是首选方法,大多数有经验的程序员都会选择它,但在意识到问题后,您应该使用任何您喜欢的方法。

即:如果您需要将C程序编译为C++(尽管它是一种单独的语言),则必须强制转换使用malloc的结果。