在这个问题上,有人在评论中建议我不要使用malloc的结果。即,我应该这样做:
int *sieve = malloc(sizeof(*sieve) * length);
而不是:
int *sieve = (int *) malloc(sizeof(*sieve) * length);
为什么会这样?
在这个问题上,有人在评论中建议我不要使用malloc的结果。即,我应该这样做:
int *sieve = malloc(sizeof(*sieve) * length);
而不是:
int *sieve = (int *) malloc(sizeof(*sieve) * length);
为什么会这样?
当前回答
尽可能在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...
其他回答
您不需要强制转换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语言的理解)告诉我,这是绝对没有必要的,但只用于绝对具体,并让学生们养成绝对具体的心态。从本质上讲,强制转换不会改变它的工作方式,它完全按照它所说的做,分配内存,强制转换不影响它,你会得到相同的内存,即使你错误地将它强制转换为其他东西(并以某种方式规避编译器错误),C也会以同样的方式访问它。
编辑:铸造有一定的意义。当您使用数组表示法时,生成的代码必须知道要到达下一个元素的开头,需要前进多少个内存位置,这是通过强制转换实现的。通过这种方式,您可以知道,对于double,您可以提前8个字节,而对于int,则可以提前4个字节,依此类推。因此,如果您使用指针表示法,则不会产生任何影响,在数组表示法中,这是必要的。
不,您不强制转换malloc()的结果。
一般来说,你不会向虚空施法或从虚空施法。
不这样做的一个典型原因是#include<stdlib.h>失败可能会被忽略。这在很长一段时间内不再是问题,因为C99使隐式函数声明非法,所以如果您的编译器至少符合C99,您将得到一条诊断消息。
但有一个更强烈的理由不引入不必要的指针强制转换:
在C语言中,指针转换几乎总是一个错误。这是因为以下规则(N1570中的§6.5 p7,C11的最新草案):
对象的存储值只能由左值表达式访问,左值表达式具有以下类型:-与对象的有效类型兼容的类型,-与对象的有效类型兼容的类型的限定版本,-一种类型,它是与对象-类型,该类型是与对象的有效类型,-一种聚合或联合类型,在其成员(递归地包括子集合或包含联合的成员),或-字符类型。
这也称为严格别名规则。因此,以下代码是未定义的行为:
long x = 5;
double *p = (double *)&x;
double y = *p;
有时令人惊讶的是,以下内容也是:
struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;
有时,您确实需要强制转换指针,但考虑到严格的别名规则,您必须非常小心。因此,代码中出现的任何指针强制转换都需要重新检查其有效性。因此,您永远不会编写不必要的指针强制转换。
tl;博士
简而言之:因为在C语言中,指针强制转换的任何出现都会给需要特别注意的代码带来危险,所以您不应该编写不必要的指针强制转换。
附带说明:
在某些情况下,您实际上需要转换为void*,例如,如果您想要打印指针:int x=5;printf(“%p\n”,(void*)&x);这里强制转换是必要的,因为printf()是一个可变函数,所以隐式转换不起作用。在C++中,情况不同。在处理派生类的对象时,强制转换指针类型有些常见(也是正确的)。因此,在C++中,与void*的转换不是隐式的,这是有意义的。C++有一整套不同风格的铸造。
这就是GNU C库参考手册所说的:
您可以将malloc的结果存储到任何指针变量中强制转换,因为ISO C会自动将类型void*转换为另一个必要时的指针类型。但演员阵容在上下文中是必要的而不是赋值运算符,或者如果您可能希望代码运行在传统C。
事实上,ISO C11标准(第47页)是这么说的:
如果分配成功,则返回的指针将适当对齐它可以分配给指向任何类型对象的指针基本对齐要求,然后用于访问这样的对象或此类对象的数组(直到空间被显式释放)