在这个问题上,有人在评论中建议我不要把malloc的结果投射出来,也就是说。
1
| int *sieve = malloc(sizeof(int) * length ); |
而不是:
1
| int *sieve = (int *) malloc(sizeof(int) * length ); |
为什么会这样?
- 此外,编写sizeof=malloc(sizeof*sizeof*length)更容易维护;
- stackoverflow.com/q/7545365/168175
- @这不是唯一的原因。另一种方法(也是最重要的方法,imho)是,如果sieve的类型从int变为float,那么代码就更灵活(但安全性和健壮性也不差)。malloc( sizeof *sieve * length );无论类型如何都可以工作,而cast需要维护,不添加任何内容,并且可能会创建难以跟踪的bug。
- @mestrelion:"而且可能会造成难以跟踪的bug"-您所要做的就是启用编译警告,这是永远不会发生的。如果你不…你真的活该。再说一次,我不是使用它的倡导者。我只想说,这真的没那么重要…大部分是无害的。)
- 具体来说,铸造malloc()的结果有什么危险?
- 同样感兴趣的可能是各自的CERT建议MEM02-C。我学到的是:一定要阅读评论,不要盲目遵循CERT建议。
- 在C语言中,从不强制转换malloc()的结果,但总是在堆内存分配之后放置if() else{}来检查堆内存分配是否成功。
- 这个问题,以及许多答案,正在meta上讨论。
不;您不投射结果,因为:
- 这是不必要的,因为在这种情况下,void *会自动安全地升级为任何其他指针类型。
- 它增加了代码的混乱,强制转换不太容易读取(特别是如果指针类型很长的话)。
- 它会让你重复你自己,这通常是不好的。
- 如果忘记包含,则可能隐藏错误。这可能导致崩溃(或者更糟的是,直到后来在代码的某些完全不同的部分中才导致崩溃)。考虑一下,如果指针和整数的大小不同,会发生什么情况;然后通过强制转换隐藏一个警告,可能会丢失返回地址的位。注:从c11开始,隐式函数不再是c,这一点不再相关,因为没有自动假设未声明的函数返回int。
作为澄清,请注意我说过"你不需要演员表",而不是"你不需要演员表"。在我看来,即使你做得对,也不包括演员阵容。这样做根本没有好处,但是一堆潜在的风险,包括演员表表明你不知道风险。
也请注意,正如评论者指出的,上面提到的是直通C,而不是C++。我非常相信C语言和C++语言是独立的语言。
此外,您的代码不必要地重复类型信息(int),这可能导致错误。最好取消对用于存储返回值的指针的引用,将两者"锁定"在一起:
1
| int *sieve = malloc(length * sizeof *sieve ); |
这也会将length移到前面以提高可见性,并删除带有sizeof的多余括号;只有当参数是类型名时才需要这些括号。许多人似乎不知道(或忽略)这一点,这使得他们的代码更加冗长。记住:sizeof不是一个函数!:)
当把length移到前面时,在一些罕见的情况下可能会增加可见性,但也应注意,在一般情况下,最好将表达式写为:
1
| int *sieve = malloc(sizeof *sieve * length ); |
由于保持sizeof优先,在这种情况下,确保至少使用size_t数学进行乘法。
比较:当width和length比size_t小时,malloc(sizeof *sieve * length * width)和malloc(length * width * sizeof *sieve)第二个可能溢出length * width。
- @不释放,即使在严格的C代码中,我也使用Cast来实现可移植性。我所说的可移植性对于旧版本的C标准来说是可移植的,在C标准中,void*不会得到提升。我引用K&R的"C编程语言":"在C语言中,正确的方法是声明malloc返回一个指向void的指针,然后通过强制转换将指针显式地强制转换为所需的类型。"我强加这种要求的原因是,您不必总是选择可以使用的编译器。
- @Chacham15虽然有早期版本的C语言,但演员阵容是必要的(K&R C没有void*类型!)这些版本从来都不是标准的。C89(第一个标准化版本)要求malloc返回void*,void*隐式可兑换。
- @展开"考虑如果指针和整数大小不同,会发生什么情况;然后通过强制转换隐藏一个警告,可能会丢失返回地址的位。"——你能更详细地解释这个问题吗?(或使用任何其他示例)
- @苏门可能太模糊了;如果没有包含返回类型的int,则会假定该返回类型小于void *,因此该分配可能会生成一个警告,而该警告会被演员抑制。例如,看看这个问题。
- 警告?gcc -std=c89 -pedantic-errors说这是一个错误(即如果我们严格遵守标准)。
- @展开大多数编译器都会警告您隐式定义的函数…(所以我看不到强制转换的危险)
- 请考虑更新答案。演员阵容不再危险,重复自己也不一定是坏事(冗余可以帮助捕捉错误)。
- @你说的"不再"是什么意思?什么改变了?
- 编译器已更改。最新的编译器将警告您malloc的声明丢失。
- 嗯,好吧。我认为假设任何阅读本文的人都有一个特定的编译器是不好的。另外,由于C11的整个"隐函数"概念已经不存在了,我不知道。不过,我不认为添加无意义的强制转换有什么意义。你也这么做只是为了澄清问题吗?
- 我遇到过一个案例,在这个案例中,malloc cast的冗余帮助找到了一个bug。这种情况不会经常发生,但仍然会发生。
- 当对特定类型使用sizeof时,我喜欢用一个空格(即sizeof (int))来分隔sizeof和(type),只是为了强调sizeof的参数是(int),而不是int。
- @N.M.如果显式地抛出一个空指针"帮助"解决了一个bug,那么您更可能遇到未定义的行为,这意味着所讨论的程序可能有一个更糟糕、未发现的bug,而您还没有遇到。有一天,在一个寒冷的冬夜,你下班回家的时候会发现你的Github页面上充斥着关于恶魔从用户鼻子里飞出来的问题报告。
- @贝登贝斯特不,事实并非如此,甚至远不止如此。您可以在这里查看相关程序。
- @即使我同意你的看法,我也不同意。12是一个int,演员什么都不做。malloc()的retval是void *,不是强制转换的指针类型。(如果不是void *。因此,与(int)12的类比就是没有人讨论的(void*)malloc(…)。
- "我认为假设任何阅读本文的人都有一个特定的编译器是很糟糕的。而且,由于c11,整个"隐函数"概念消失了,我不知道"—它从c99(而不仅仅是c11)消失了。如果不包括#include ,则任何c99编译器都需要发出诊断。在4个项目符号中,前3个项目符号是主观的(有足够的证据表明有些人倾向于在本页中使用它),第4个项目符号在该答案前10年左右已用C语言"修复"。虽然我不主张支持演员阵容,但这确实是一个用鼹鼠造山的例子。
- @与double d = (double)12;或unsigned int u = (unsigned int)12;相比,放松可能更有意义。
- "当宽度和长度是较小的类型时,第二个可能会溢出length*宽度,但大小不同。"—我不明白。我以为变量会被提升到更大的类型。如何可能溢出?
- 除非我强制转换malloc的结果,否则编译器怎么会抛出警告呢?
- @不,晋升与任务左侧的类型无关。这是一个常见的误解。double a = 1 / 2;是0,因为它是整数除法。
- @没有代码的话,gewur显然很难回答,但可能是因为你没有为malloc()的原型使用#include ,因此编译器假定它返回int,如果没有强制转换,你就不能将它赋给指针。这种情况下,演员阵容不够。
- 在更改数据类型大小/切换到其他数据类型时,如果不检查代码中的所有分配,则会出现问题。你可以利用你的一个优势,魁克找到所有的malloc呼叫为某一类型。
- 假设我们有C++项目和C库文件,其中头有EDCOX1,19个设置。当编译器编译.c文件时,如果没有EDCOX1(20)的调用,它会工作吗?因为我们在C++项目中?
- @是的,当然。EDCOX1(21)是用于C++编译器,它只是告诉它在寻找C函数时不要命名Mangle。C函数由C编译器构建,并用C语言编写。
- 所以我不知道为什么我们总是为你不能为同一个项目写C和C++代码而争吵。我在我的每个C库中都使用extern"C"方法,因此允许它不强制转换malloc,并且仍然编译。那个时候我的案子结束了。如果这真的是隐藏的错误,如果你投射结果,我不会再投射它了。总的来说,你的第四点为什么不投没有意义,或者至少我不明白。如果返回非空值,则会分配内存,您可以根据需要对其进行强制转换。
- "季前tilz0r C11,没有函数原型是两个intassumed回报。但int往往是小比void *(32和64位),这样做可能会隐藏一foo *x = (foo *) malloc(sizeof *x);预警引起的肺炎,制作《(undeclared,assumed int)返回的值是从malloc()处理作为一个指针,当它事实上是不足够大和因此不会工作。
- "前"unwind C11,没有函数原型int返回assumed是两个"",这是在C99已经走了。
- unwind",事实上,我认为你说的点,这是不相关的概念仍然适用,因为如果你试图编译代码没有specifying --std=c11(AS多做),你会得到更多的预警,但是一个……事实上,即使在specifying --std=c11get"警告:隐式声明的函数"malloc",当是不包括在标准库中的头信息。这不是个非凡执行,要么;摩尔20160609 gcc版本…
- "unwind我们的房间有一个misinterpretation;有些用户可能会认为"这意味这一点不再是不相关的,因为这只是一emits预警和(erroneously)我们可以忽视"预警"。
- "compare:malloc(sizeof××长×宽筛)vs. malloc(长×宽××sizeof筛)在第二个可能溢出的长度*宽度,长度和宽度时,是比小尺寸的T型_"不明白什么错误可以发生在这里。
- "如果lengthsavram width是ints鸭,在int有fewer位比size_t,然后length*width可能是较高的比"最大的int。这是不可能会是个问题,如果你使用size_t数学,这可以做在《城市sizeof第一方(编译自《"读"multiplications从左右)
- "danielh谢谢你,明白了吗?(int。但为什么solves阶变化是什么?如果sizeof的乘积(×长度超过"最大的sizeof吗?
- "savram《malloc功能需要一size_t作为输入。如果整个乘积超过这一点,那么你就可以做你想要的配置。这是不可能的,在一个自由的程序错误,因为必须在64位系统,会要求18 exabytes大学的记忆,我想超过我有曾经被加工。在其他的手上,最大int在线系统往往是这样的~(31)1,2,或只是在双字节;计划的很多allocate会想那么多。
- "danielh的原形,有较高的问题。为什么我选择了《intAA型和widthlength吗?我们应该照顾我们在选择的类型的变量,除非它也绝对有道理的宽度,长度和/或是负面的,全discarded签署期权应该从选择。毕竟,你不会想要一个两维的数组,每个维度可以有负指数,对吗?好的,我begging为undefined行为。你不会知道的,而lengthwidthAA和重构的两个size_t,避免进一步的并发症?
- "sebivor取决于它的上下文。有时有成因类型为使用signed无论如何,和更多的往往是length可能比size_tunsigned但小一些的原因。在agree,全被摧毁,平等的,是更好的,如果一size_tlength冰。我也savram自愿的问题是关于如何与length * width * sizeof *sieve溢出可能发生,如上述的两个问题,我只是说让length和width是学院式size_t会不会是个答案。
- 对sizeof sizeof(VS):是的,这不是一个功能,但它behaves somewhat功能(像一个或更多的宏观accurately(想)。(鸭)也使得它透明两人到底是什么尺寸的阅读你的应用。我觉得道贺的能力强,离开父母的关闭是一个错误,不是一个特征。凯文:"这就是为什么你使用sizeof(*x),作为上述的回答。
- 我非常相信C语言和C++语言是独立的语言。=>面对历史和现实,你不应该对那些飞扬的事物保持坚定。C++由于其内在的美而没有继承C的语法,因此C代码库可以逐步采用C++特征。此外,在C++编译器中编译C代码库有很大的价值,无论你最终如何使用二进制代码,请参阅C++核心指南。我们可以为C编写许多基于特征的整洁类型静态检查。
- 当C++是C语言上的一个简单的预处理器时,HuthiLoFoek可能是真实的,它有一个坚实的哲学指导语言。但是在过去的几十年中,句法蠕动使它几乎不能被理解为C语言。许多兼容的C代码拒绝用C++编译器编译(尝试用名为"类"的字段创建一个名为"模板"的结构)。同时,ISO C++委员会忽略了跟上现代标准对ISOC标准的发展,把语言放在两个单独的轨道上:一个有前途的可移植性,另一个则拥有灵活性。
- @ HostileFork @瑞安,你甚至不必使用C++关键字来获得C/C++不兼容性:我最近在使用C++时遇到了一个问题,因为我使用的库(OpenCV)在C(WTF!)中被弃用了。而唯一稳定的选择是使用C++。当我尝试使用我的C库时,它包含了这样的函数,比如EDCOX1(0),我意识到C++不允许这样做,我认为这是很愚蠢的(告诉我另外的事情),然后我决定为C(.h)和C++(.HPP)做两个单独的标题(两个都引用C代码),即使这意味着重复头中的大部分代码。
在C中,您不需要强制转换malloc的返回值。malloc返回的void指针自动转换为正确的类型。但是,如果希望代码用C++编译器编译,则需要一个强制转换。社区中的首选替代方案是使用以下各项:
1
| int *sieve = malloc(sizeof *sieve * length ); |
另外,如果您更改了EDOCX1的类型(3),就不必担心更改表达式的右侧。
正如人们所指出的,石膏是不好的。特别是指针投射。
- @Makz我认为malloc(length * sizeof *sieve)使它看起来像sizeof是一个变量,所以我认为malloc(length * sizeof(*sieve))更可读。
- 而malloc(length * (sizeof *sieve))更具可读性。恕我直言。
- @Michael Anderson ()的问题除外,请注意,您建议的样式改变了顺序,当计算元素计数时,如length*width,保持sizeof在这种情况下,确保至少使用size_t数学进行乘法。比较malloc(sizeof( *ptr) * length * width)与malloc(length * width * sizeof (*ptr))—当width,length是size_t的较小类型时,第二个可能溢出length*width。
- @不太明显,但我的回答被修改了,所以我的评论没有那么中肯——最初的建议是malloc(sizeof *sieve * length)。
- C不是C++。假装他们最终会导致困惑和悲伤。如果你使用C++,那么C风格的转换也很糟糕(除非你使用了一个非常古老的C++编译器)。而static_cast>()或reinterpret_cast<>()与c的任何方言都不兼容。
- @Tobyspeight和我认为calloc(length, sizeof(*sieve))比我在这个问题上看到的任何替代方案都更具可读性。
- @当然可以,但是如果不需要把内存调零,在某些系统上可能会慢一些。
- 这很难看,没有初始化。你需要使用calloc和sizeof()。
铸造:你,因为
- 它使您的代码更便携之间的C和C + +,和一个伟大的体验节目那样,他们是在尝试写作要求写作时,他们是真的我在C(或C++中的C编译器的扩展,加上本地)。
- 否则,错误隐藏可以这样做:注意所有这样的例子,当type *与type **写到位。
- 库,它让你从你#includenoticing无法适当的头文件,在森林的树。它同样是说"不要担心你无法买卖的事实在逆境里看到一个编译器的原型--这是真正重要的东西pesky stdlib.h难忘!"
- 额外的部队在认知信息交叉检查。它使(光)所需的类型旁边的算术你做为原始大小是可变的。我打赌你可以做这样的研究,这是多重要的节目malloc()错误当有一个铸造速度。作为一个assertions滴注意向,减少错误。
- 一个方法是,在重复自己的机器可以检查是经常的一大理念。事实上,这就是对这一断言,使用CAST是断言。assertions仍然是最通用的技术,因此我们有正确的代码,因为图灵与想法来这么多年前。
- @ ULIDTKO如果你不知道,可以编写编译为C和AS C++的代码。实际上,大多数头文件都是这样的,它们通常包含代码(宏和内联函数)。有一个EDCOX1 5 /EDCOX1,6个文件,编译为两个都不是很有用,但一个例子是添加C+EDCOX1,7支持,当编译用C++编译器(但EDCOX1,8),当编译用C编译器,或任何东西)。
- 如果有人在标题中有malloc调用,我不会被打动,ifdef u cplusplus和extern"c"用于此任务,而不是添加额外的强制转换。
- 还可以与编译器进行兼容性,编译器默认编译为C++。(msvc)
- 好吧,第一点无关紧要,因为C!= C++,其他点也是微不足道的,如果在EDCOX1调用9调用中使用变量:EDOCX1,10,如果完全充分证明:3指针指向字符指针。然后循环,执行foo[i] = calloc(101, sizeof(*(foo[i])));。分配101个字符的数组,整齐地初始化为零。不需要演员表。把申报单改成unsigned char或其他类型,这样你还是很好的。
- 强制转换不会隐藏错误-事实上,它引入了进一步错误的可能性(最明显的是,将int强制转换为指针)
- 我一拿到它,它就来了!答案很好。这是我第一次在StackOverflow中+1两个相反的答案!+1不,你不施法,+1是,你施法!哈哈,你们真是太棒了。对于我和我的学生,我下定决心:我是演员。学生们犯的错误在铸造时更容易被发现。
- 有趣的是,@leushenko是这里唯一一位评论者,他说了一个非常重要的观点,那就是铸件不干燥。IMOH:这是一个Switter和为什么我不投C。我吸它在C++,因为我必须。也就是说,"重复自己"确实有它的用途:在单元测试中。但当然不是在生产代码中。重复自己只是在自找麻烦。我很高兴我们都能同意不同意,但我看不到自己被种姓人的论点所动摇。;-)
- 也许Donotcasters可以阅读以下文档:securecodeing.cert.org/confluence/display/seccode/&hellip;。我相信,不施法比施法更危险,而施法的结果马尔洛克不是"重复自己"。
- @Jean-Baptisteyun&232;S:值得一看本文下面的评论。p = malloc(sizeof(gadget))的基本原理没有什么意义,因为更多的惯用形式将只是p = malloc(sizeof *p)。他们没有提到,当头不存在时(在这种情况下,C89编译器没有义务进行任何诊断),CAST可能很危险,会产生讨厌的错误,因为malloc假定返回int。
- 那么,你是否也把free()的论点投射到void *上?
- @莫希特詹认为这一论点毫无意义。在Java中,你会用EDCOX1×20的方法来转换一个参数吗?
- @Leushenko:以一种既不能通过机器验证也不能通过当地检查的方式重复自己是不好的。用这样的方法来验证你自己,这样做就不那么糟糕了。在struct Zebra *p; ... p=malloc(sizeof struct Zebra);的情况下,malloc不能避免对p类型的信息进行重复计算,但是如果一种类型发生了变化,而另一种类型没有发生变化,编译器和本地代码检查都不会检测到任何问题。将代码改为p=(struct Zebra*)malloc(sizeof struct Zebra);,如果转换类型与p不匹配,编译器将发出尖叫声,本地检查将显示…
- …如果强制类型与sizeof类型不匹配。用类型转换检查代码版本的人可以知道(如果代码完全编译)p是void*或struct Zebra*,而无需查看p的实际声明。在我看来,这是一场胜利。
- "以机器可以检查的方式重复自己"。不!如果你漏掉一个演员表(一般来说),你会得到一个警告,如果你做了一些不合理的,或合理的和不可携带的事情。如果使用强制转换,您会对编译器说:"闭嘴!我已经研究过了,我更清楚了。"。初学者不应该被教育养成铸法的习惯。malloc()是一个例子,在这个例子中,添加强制转换的危害相对较小。
- 回答很好,谢谢你。隐式转换会导致很多问题,而且你很好地阐明了原因。也许是改写了"愚蠢至极"的部分?这可能会增加接触那些不同意你的人的机会,而不是让他们处于守势,让他们听不到你说的明智的话。
- @Albertvanderhorst新手应该启用编译器标志,将隐式转换转换转换为错误。
- 这个答案中有很多谬误。基本上,唯一有效的参数是第一个:交叉编译C/C++代码。但是它只适用于交叉编译的C/C++代码,而不是到处使用CAST的理由。重述的论点是基于一个简单事实的失败而产生的谬误:避免强制转换本身并不是一个目的,而是更广泛的惯用实践的一部分——编写独立于类型/不可知类型的代码。
- 上述推理试图解决T *p = malloc(N * sizeof(T))中存在的问题。但你一开始不应该这样做。为T *p = malloc(N * sizeof *p)分配内存调用的惯用类型不可知论方法,也就是说,在大小表达式中根本没有提到类型名。此版本完全没有上述答案试图"解决"的任何问题。换句话说,对内存分配函数结果的显式强制转换是一个类C"寻找问题的解决方案"。你自己制造的问题。不要制造问题,你也不需要解决方案。
- 为什么你们大多数人说强制转换是坏的,因为如果你把一个指针强制转换成一个整数,你可能会因为平台的不同而丢失比特,如果这里的整点是关于强制转换void*到一个任意但需要的T*?
- 看这个例子,不要使用案例:en.cppreference.com/w/c/memory/malloc
- @jean-baptisteyun&232;s:mem02-c的sei引用:立即将内存分配函数调用的结果强制转换为指向已分配类型的指针位于新位置(链接今天有效;谁知道明天)。
正如其他陈述的那样,C是不需要的,但是C++是不需要的。如果你认为你将用C++编译器编译你的C代码,不管是哪个原因,你都可以使用宏,比如:
1 2 3 4 5
| #ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif |
这样,您仍然可以非常紧凑地编写它:
1
| int *sieve = NEW(int, 1); |
它将编译为C和C++。
- 既然你正在使用宏,那么为什么不在C++的定义中使用EDCOX1 1?
- 因为没有理由这样做。它主要是用C++编译器编译的C程序。如果你想用"新"这个词,你唯一能得到的就是问题。你还需要一个免费的宏。你需要一个宏来释放一个数组,一个C中不存在的微分。
- 更不用说,如果不是你释放了内存,也许是你正在使用的C库,等等,许多可能的问题没有任何收获。
- 六羟甲基三聚氰胺六甲醚。。。我没想到。使用free()释放分配给new的内存是错误的吗?
- @何萨姆:是的,肯定是。如果使用new,必须使用delete,如果使用malloc(),必须使用free()。不要混合它们。
- 函数、变量、关键字和宏虽然允许,但只在大小写上有所不同,它们提供了很小的差别。可能是CNEW(t, n)。
- @ CHOX,EDCOX1,1是不是C中的关键字,在一个真正的C++程序中,我根本不使用这个宏,所以没有混淆。
- 如果要采用这种方法,那么调用宏new可能是一个坏主意,因为使用delete或delete永远不会返回资源,所以您混合了词汇表。相反,在这种情况下,将其命名为MALLOC,或者更确切地说,命名为CALLOC,将更有意义。
- 看到这样的定义我会很不高兴。我想如果真的没有其他选择的话,也许没关系。但总有一天你会希望自己没有做到。至少给这该死的东西起个比新名字更好的名字。
- @戴夫布兰顿,你对#define的使用有什么看法?宏不会改变任何变量,变量只使用一次,它是一个简单的表达式,而不是一系列语句。另外,它不能用内联函数来完成。所以我想知道是什么让你担心。
- glib ineed定义了这个宏。它称之为g_new和g_new0。
- 实际上,如果您使用这个宏,那么在C中添加强制转换会更安全。它将允许编译器捕获这个错误:long array = NEW_INT(int, n);。
- 为什么EDCOX1〔0〕新的NEW如果在C++中存在一个旧的EDCOX1×1算子????Maloc(3)和Field(3)在C++中被严格禁止使用C函数(它们只支持C向C++中的可移植性要求)。C++鼓励使用EDCOX1、1和EDCOX1,4个操作符来代替。
- C++中的关键字是新的吗?我想不是,如果我是对的,那么@quinmars的宏是一个很好的解决方案。使用起来也不会有问题。
- 这个解决方案是用于C和C++之间的互操作风格代码。它不是用C++的惯用方式来做事情,而是让C代码编译并容易地移植到C++编译器上。
来自维基百科
Advantages to casting
-
Including the cast may allow a C program or function to compile as C++.
-
The cast allows for pre-1989 versions of malloc that originally returned a char *.
-
Casting can help the developer identify inconsistencies in type sizing should the destination pointer type change, particularly if the pointer is declared far from the malloc() call (although modern compilers and static analyzers can warn on such behaviour without requiring the cast).
Disadvantages to casting
-
Under the ANSI C standard, the cast is redundant.
-
Adding the cast may mask failure to include the header stdlib.h, in
which the prototype for malloc is found. In the absence of a
prototype for malloc, the standard requires that the C compiler
assume malloc returns an int. If there is no cast, a warning is
issued when this integer is assigned to the pointer; however, with
the cast, this warning is not produced, hiding a bug. On certain
architectures and data models (such as LP64 on 64-bit systems, where
long and pointers are 64-bit and int is 32-bit), this error can
actually result in undefined behaviour, as the implicitly declared
malloc returns a 32-bit value whereas the actually defined function
returns a 64-bit value. Depending on calling conventions and memory
layout, this may result in stack smashing. This issue is less likely
to go unnoticed in modern compilers, as they uniformly produce
warnings that an undeclared function has been used, so a warning will
still appear. For example, GCC's default behaviour is to show a
warning that reads"incompatible implicit declaration of built-in
function" regardless of whether the cast is present or not.
-
If the type of the pointer is changed at its declaration, one may
also, need to change all lines where malloc is called and cast.
虽然不使用强制转换的malloc是首选方法,而且大多数有经验的程序员都会选择它,但是您应该使用任何您愿意知道问题的方法。
IE:如果你需要编译C程序作为C++(尽管这些是单独的语言),你应该使用EDCOX1,5,用铸造。
- 如果目标指针类型发生变化,"强制转换"可以帮助开发人员识别类型大小不一致,特别是当指针声明为远离malloc()调用时",这意味着什么?你能举个例子吗?
- @Coolguy:看前面关于另一个答案的评论。但是请注意,p = malloc(sizeof(*p) * count)习惯用法会自动获取类型中的更改,因此您不必获取警告并进行任何更改。所以这不是一个真正的优势,而不是最好的选择。
- 这是正确的答案:有利弊,归结为味道的问题(除非代码必须编译为C++),然后强制强制转换。
- 点3是无意义的,因为如果指针的类型在声明时被更改,那么应该检查malloc的每个实例,重新分配并释放该类型。演员阵容会迫使你这样做。
在C中,可以将一个空指针隐式转换为任何其他类型的指针,因此不需要强制转换。使用一个可以向不经意的观察者建议,需要一个的原因是有一些的,这可能是误导性的。
你不铸造的结果,因为malloc,这样不加杂波您的代码。
最普通的原因为什么人铸造的结果,因为他们是unsure malloc是C语言是如何工作的。这是一个警告标志:如果你不知道一个特定的语言机制的作品,然后Don’t Take a的猜测。它要求在外观上或堆栈溢出。
一些评论:
a void指针可以转换到/从其他任何指针类型没有显现(C11和6.5.16.1容器)。
要允许不需要的C + +在灌注void*和另一个之间的隐式指针类型。所以在C++中,现已经是正确的。但如果你的程序在C + +,你不应该使用newmalloc()。你应该从来没有使用编译的C代码的C + +编译器。
如果你需要支持这两个C和C + +和使用相同的源代码,编译一个标识该交换机等。不要试图用同样的语言标准是国家代码,因为他们是不兼容的。
如果一个C编译器,因为你无法找到一个良好的功能包括一个标题,你会得到一个编译/链接错误吗。所以,如果你忘了包括这是不重要的,你不可以编译您的项目。
这一古老的编译器在后续版本的标准是超过25岁,肮脏的玻璃包括会导致危险的行为。这是因为在古代的标准,没有可见的函数原型implicitly返回到int型转换。从铸造的结果明确了这是malloc然后隐藏的BUG。
但这真的是一个非问题。你不使用计算机的25岁,那么你为什么要使用一个25岁的编译器?
- "毫无意义的混乱"是一种轻蔑的夸张,它会破坏说服任何不同意你的人的可能性。演员阵容当然不是毫无意义的;罗恩·伯克和卡兹的回答提出了支持我非常赞同的演员阵容的论点。这些问题是否比您提到的问题更重要是一个合理的问题。对我来说,与他们相比,你的顾虑相对较小。
- 6.3.2.3不支持"void指针可以转换为任何其他指针类型,也可以从任何其他指针类型转换为其他指针类型,而无需显式转换"。也许您在考虑"指向任何对象类型的指针"?"空指针"和"指向函数的指针"不太容易转换。
- 事实上,参考资料是不完整的。"隐含性"的相关部分是简单赋值规则6.5.16.1。一个操作数是指向对象类型的指针,另一个操作数是指向限定或不限定的void版本的指针。为了完整性,我将这个引用添加到了答案中。
在C语言中,您可以得到从void*到任何其他(数据)指针的隐式转换。
- @詹斯:好吧,也许更恰当的措辞是"隐式转换"。类似于在浮点表达式中使用整型变量。
- @实际上会导致一个强制转换,并且在这一点上是一个隐式强制转换。
铸造malloc()值返回的是没有必要的,但现在,我想添加一个似乎没有一个尖锐的点出:安切洛蒂
在古代的日子,这是在ANSI C提供的void *为通用型分型研究char *是搜索使用。在这样的实例,编译器警告CAN;浇下来。
参考:c FAQ
- 关闭编译器警告是一个坏主意。
- @阿尔伯特范德霍斯特如果你这样做是通过解决确切的问题,警告是有警告你。
- @丹。如果解决确切的问题意味着重写子例程以返回现代的ANSIC类型而不是char*,我同意。我不会叫它关闭编译器。不要屈服于那些坚持没有编译器警告的管理者,而不是通过每次重新编译来使用它们来发现可能的问题。格莱特斯-艾伯特
不必强制强制强制转换malloc的结果,因为它返回void*,并且void*可以指向任何数据类型。
加上我的经验,学习计算机工程,我发现我在C语言中看到过的两三位教授总是投马洛克的票,但是我问的那位教授(有着丰富的简历和对C的理解)告诉我,这是绝对不必要的,但只不过过去是绝对具体的,并让学生进入一种存在的心态。非常具体。从本质上说,强制转换不会改变它的工作方式,它完全按照它所说的做,分配内存,强制转换也不会影响它,你得到相同的内存,即使你错误地将它强制转换成其他东西(并且以某种方式规避编译器错误),C也会以同样的方式访问它。
编辑:铸造有一定的意义。当使用数组表示法时,生成的代码必须知道它必须前进多少内存才能到达下一个元素的开头,这是通过强制转换实现的。这样你就知道,对于一个双精度数,你向前移动8个字节,而对于一个整型数,你向前移动4个字节,依此类推。因此,如果您使用指针表示法,它就没有任何效果,在数组表示法中,它是必要的。
- 除非前面已经提到,否则强制转换可能隐藏错误并使代码更难为编译器或静态分析器分析。
- "本质上,铸造不会改变它的工作方式"。强制转换为匹配类型不应更改任何内容,但如果var的类型更改且强制转换不再匹配,是否会出现问题?IWOS、CAST和VAR类型应保持同步,这是维护工作的两倍。
- 我明白为什么prof更喜欢铸造。从教育的角度来看,演员阵容可能很有用,因为它传达给教员信息,而学生代码不需要维护——它的抛弃代码。然而,从编码、同行评审和维护的角度来看,p = malloc(sizeof *p * n);是如此简单和更好。
这就是GNU C库参考手册所说的:
You can store the result of malloc into any pointer variable without a
cast, because ISO C automatically converts the type void * to another
type of pointer when necessary. But the cast is necessary in contexts
other than assignment operators or if you might want your code to run
in traditional C.
实际上,ISO C11标准(p347)是这样说的:
The pointer returned if the allocation succeeds is suitably aligned so
that it may be assigned to a pointer to any type of object with a
fundamental alignment requirement and then used to access such an
object or an array of such objects in the space allocated (until the
space is explicitly deallocated)
void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地进行类型转换。
但是,如果您希望在C++平台上完全兼容的代码工作,它不支持隐式转换,则需要进行类型转换,因此这取决于可用性。
- 将单个源编译为C和C++(而不是使用包含声明的头文件将C和C++代码链接在一起)不是一个正常的使用情况。在C++中使用EDCOX1 1和朋友是一个很好的警告信号,它值得特别注意(或者在C中重写)。
- "void指针是一个通用指针"-->"void指针是一个通用对象指针"。函数指针的大小可以超过void *,因此void *不足以很好地存储函数指针。
返回类型是void *,可以灌注到所需的数据指针的类型是可以的。
- void*可以转换为所需的类型,但不需要这样做,因为它将被自动转换。因此,演员阵容是不必要的,事实上,不受欢迎的原因所提到的高分答案。
在C语言中,可以将空指针分配给任何指针,这就是为什么不应使用类型转换的原因。如果您想要"类型安全"分配,我可以推荐以下宏函数,我在我的C项目中总是使用这些函数:
1 2 3
| #include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1) |
有了这些,你可以简单地说
1
| NEW_ARRAY(sieve, length); |
对于非动态数组,第三个must-have函数宏是
1
| #define LEN(arr) (sizeof (arr) / sizeof (arr)[0]) |
这使得阵列循环更安全、更方便:
1 2 3 4 5
| int i, a[100];
for (i = 0; i < LEN(a); i++) {
...
} |
- "空指针可以分配给任何对象指针"函数指针是另一个问题,尽管不是malloc()指针。
- 在这些情况下,将void*分配给/从函数指针分配可能会丢失信息,因此"空指针可以分配给任何指针"是一个问题。不过,将void*从malloc()分配给任何对象指针都不是问题。
- 与涉及循环的宏相关的do循环注释,但不知道标题问题。删除该注释。稍后也会取下这个。
它取决于在编程语言和编译器。如果你使用mallocC型铸造没有需要它,它会自动为您的铸造型,但是如果使用C + +,然后你应该想,因为malloc型铸造void*返回a的类型。
- 函数Maloc也在C中返回空指针,但是语言的规则不同于C++。
习惯GCC和Clang的人被宠坏了。外面不太好。
这些年来,我一直被要求使用的那些老掉牙的编译器吓坏了。通常,公司和管理人员都采用极为保守的方法来更改编译器,甚至不会测试新的编译器(具有更好的标准遵从性和代码优化)是否能在其系统中工作。对于工作的开发人员来说,实际的情况是,当您在编码时,需要覆盖您的基础,不幸的是,如果您不能控制什么编译器可能应用到您的代码,那么抛出malloc是一个好习惯。
我还建议许多组织应用自己的编码标准,如果定义了这一标准,人们就应该遵循这一标准。在缺乏明确指导的情况下,我倾向于选择最有可能在任何地方进行编译的方法,而不是盲目地遵循标准。
在现行标准下不需要这样做的论点是非常有效的。但这一论点忽略了现实世界的实际情况。在一个完全由当时的标准所统治的世界里,我们并不编码,而是根据我喜欢称之为"地方管理的现实领域"的实用性来编码。这比时空更弯曲和扭曲。-)
YMMV。
我倾向于把malloc当作防御行动。不漂亮,不完美,但一般安全。(老实说,如果你没有包括stdlib.h,那么你有比铸造malloc更多的问题!).
- 很好地说明了另一种观点,但YMMV是什么意思?(您的里程数可能会有所不同,这是我能找到的最常见的定义,但不知道它在这里是如何应用的:)
我加入强制转换只是为了表示对类型系统中丑陋的漏洞的不赞成,它允许代码(如以下代码片段)在没有诊断的情况下编译,即使没有强制转换用于导致错误的转换:
1 2 3
| double d;
void *p = &d;
int *q = p; |
我希望它不存在(并且它不在C++中),所以我投下。它代表了我的品味和我的编程政治。我不仅投出了一个指针,而且实际上,投了一张选票,并且驱逐了愚蠢的恶魔。如果我不能真正摆脱愚蠢,那么至少让我用一种抗议的姿态来表达这样做的愿望。
实际上,一个好的实践是用返回unsigned char *的函数包装malloc(和friends),基本上不要在代码中使用void *。如果需要指向任何对象的通用指针,请使用char *或unsigned char *,并在两个方向上进行强制转换。一个可以放松的方法是使用像memset和memcpy这样的函数而不使用强制转换。
在铸造和C++兼容的主题上,如果编写代码以编译为C和C++(在这种情况下,当将EDCOX1 OR 0的值返回到EDCOX1(2)之外)时,您可以为自己做一件非常有益的事情:您可以使用宏来进行转换,将其转换为C++样式转换。编译为C++,但编译为C时编译为C:
1 2 3 4 5 6 7 8 9 10
| /* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif |
如果您坚持这些宏,那么在代码库中搜索这些标识符的一个简单的grep搜索将显示您的所有强制转换的位置,这样您就可以查看其中是否有任何强制转换是不正确的。
然后,向前看,如果你经常用C++编译代码,它将强制使用适当的CAST。例如,如果您使用strip_qual只是为了删除const或volatile,但程序的更改方式现在涉及到类型转换,那么您将获得诊断,并且您将不得不使用强制转换的组合来获得所需的转换。
为了帮助你坚持这些宏,GNU C++(不是C!)编译器有一个漂亮的特性:一个可选的诊断程序,它为所有出现的C样式转换生成。
1 2 3 4 5
| -Wold-style-cast (C++ and Objective-C++ only)
Warn if an old-style (C-style) cast to a non-void type is used
within a C++ program. The new-style casts (dynamic_cast,
static_cast, reinterpret_cast, and const_cast) are less vulnerable
to unintended effects and much easier to search for. |
如果你的C代码编译为C++,你可以使用这个EDCOX1的13个选项来找出EDOCX1的14个可能的语法,这些语法可能会蠕动到代码中,并通过在上面的宏中替换一个适当的选择来替换这些诊断(或者如果需要的话,一个组合)。
这种转换处理是在"干净C"中工作的唯一最大的技术证明:组合C语言和C++语言,这反过来又证明EDCOX1〔0〕的返回值是合理的。
- 正如其他人指出的,我通常建议不要混合C和C++代码。但是,如果您有充分的理由这样做,那么宏可能是有用的。
- @ PHIL1970,所有这些都是用一个内聚的方言编写的,它恰好是C和C++编译器的可移植性,并充分利用了C++的一些功能。它必须全部编译为C++,否则都编译为C。
- 也就是说,在前一篇评论中我想说的是,C和C++没有混合。目的是代码都被编译成C或全部编译成C++。
尽可能在C语言中编程时要做的最好的事情是:
使您的程序通过C编译器编译,所有警告都打开-Wall,并修复所有错误和警告。
确保没有声明为auto的变量
然后使用EDCOX1×0和EDCOX1×3的C++编译器编译它。修复所有错误和警告。
现在再次使用C编译器编译。您的程序现在应该编译而不发出任何警告,并且包含更少的错误。
此过程允许您利用C++严格类型检查,从而减少错误的数量。特别是,这个程序强制您包括stdlib.h,否则您将
malloc was not declared within this scope
也迫使你投下malloc的结果,否则你会得到
invalid conversion from void* to T*
或者你的目标类型是什么。
C写的唯一好处,而不是C++我能找到的好处是
C有明确的ABI
C++可以生成更多的代码[异常,RTTI,模板,运行时多态性]
注意,在理想情况下,当使用C的公共子集和静态多态特性时,第二个缺点应该消失。
对于那些发现C++严格规则不方便的人,我们可以使用推断类型的C++ 11特征。
1
| auto memblock =static_cast <T *>(malloc(n *sizeof(T ))); //Mult may overflow... |
- 对C代码使用C编译器。使用C++编译器实现C++代码。没有假设,没有但是。在C++中改写C代码完全是另一回事,可能或不值得——时间和风险。
- 我想添加到ToByScript建议:如果你需要在C++项目中使用C代码,你通常可以将C代码编译为C(例如EDCOX1,4),C++代码作为C++(例如EDCOX1,5),然后将它们链接在一起(例如EDCOX1×6),反之亦然,这取决于项目的依赖性)。现在应该没有理由剥夺你自己任何一种语言的好特征…
- @ USS87329这是一个更明智的选择,它是为了减少代码的易读性而精心添加代码,只为了"C++兼容"。
- C优于C++的其他优点:C99可变长度数组用于非常有效地分配/解除划线空间的分配。当您无意中编写非常量初始值设定项时(例如,如果您认为自己很聪明,将static const __m128 ones = _mm_set1_ps(1.0f);放在全局范围内,这样多个函数就可以共享一个常量,那么构造函数在C中不是一件事,这一事实会阻止您生成更糟糕的代码。(这真的是找到了C限制的一线希望…)
- 在这种情况下,最主要的优点可能是C允许您编写p = malloc(sizeof(*p));,如果p更改为不同的类型名,则一开始不需要更改。Casting的建议"优势"是,如果p的类型不正确,您会得到一个编译错误,但是如果它只是工作的话,就更好了。
我更喜欢做演员,但不是手工。我最喜欢的是使用glib中的g_new和g_new0宏。如果不使用glib,我将添加类似的宏。这些宏在不影响类型安全的情况下减少了代码重复。如果您键入了错误类型,您将在非空指针之间得到隐式转换,这将导致警告(C++中的错误)。如果忘记包含定义g_new和g_new0的头,则会得到一个错误。g_new和g_new0都采用相同的论点,而malloc的论点比calloc少。只需添加0即可获得零初始化内存。代码可以用C++编译器编译而无需修改。
不,你不会投射
malloc()的结果。
一般情况下,你不会在void *上或从void *上投射。
不这样做的一个典型原因是,对#include 的失败可能会被忽视。这在很长一段时间内不再是问题,因为C99将隐式函数声明设置为非法,所以如果编译器至少符合C99,您将收到一条诊断消息。
但是有一个更强有力的理由不引入不必要的指针转换:
在C语言中,指针转换几乎总是一个错误。这是因为以下规则(§6.5 P7 in N1570,C11的最新草案):
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the
object,
— a type that is the signed or unsigned type corresponding to a qualified version of the
effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its
members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
这也被称为严格的别名规则。因此以下代码是未定义的行为:
1 2 3
| long x = 5;
double *p = (double *)&x;
double y = *p; |
而且,有时令人惊讶的是,以下也是:
1 2 3 4 5
| 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; |
有时,您确实需要强制转换指针,但是考虑到严格的别名规则,您必须非常小心。因此,在代码中出现指针强制转换时,您必须仔细检查它的有效性。因此,您永远不会编写不必要的指针转换。
DR
简而言之:因为在C语言中,指针转换的任何一次出现都应该为需要特别注意的代码升起一个红色标志,所以您不应该编写不必要的指针转换。
侧记:
在某些情况下,您实际上需要强制转换到void *,例如,如果要打印指针:
1 2 3
| int x = 5;
printf("%p
", (void *)&x ); |
这里需要强制转换,因为printf()是一个变量函数,所以隐式转换不起作用。
在C++中,情况是不同的。在处理派生类的对象时,强制转换指针类型有些常见(并且是正确的)。因此,在C++中,EDCOX1与1的转换不是隐式的,这是有意义的。C++有一整套不同的铸造风格。
- 在您的示例中,您可以避免void*。从double*转换为int*之间存在差异,反之亦然。malloc返回与最大标准类型对齐的指针,因此即使有人将此对齐的指针强制转换为其他类型,也不会破坏别名规则。
- 别名与对齐和您的评论的其余部分毫无关系——显然您没有理解这一点。
- @彼得杰:以防万一,关键是要避免不必要的指针转换,所以它看起来不像一段需要特别注意的代码。
- 严格的别名问题实际上与空指针没有任何关系。为了获得由严格的别名冲突引起的错误,必须取消引用指向的数据。而且,由于不能取消对空指针的引用,所以这些错误是根据定义产生的,与空指针无关,而是与其他东西有关。
- 相反,您必须制定一个规则来禁止所有指针强制转换。但是,您将如何编写序列化例程和与硬件相关的编程之类的东西呢?是C的力量。如果你知道自己在做什么的话,这样的演员就可以了。
- @伦丁,这正是重点。有时您需要这些强制转换,但您知道必须格外小心,因此不会编写不必要的指针强制转换。
铸造只适用于C++,而不是C。在使用C++编译器的情况下,最好将其转换为C编译器。
void指针背后的概念是它可以强制转换为任何数据类型,这就是malloc返回void的原因。此外,您必须了解自动排版。因此,尽管您必须这样做,但强制转换指针并不是必需的。它有助于保持代码的整洁并帮助调试
- "这不是强制性的——尽管你必须这么做"——我认为这里面有矛盾!
- 我认为你应该把这篇文章读给别人听,看看他们是否理解你想说的话。然后重写它,让它清楚你想说什么。我真的不明白你的答案是什么。
void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地进行类型转换。
但是,如果您希望在C++平台上完全兼容的代码工作,它不支持隐式转换,则需要进行类型转换,因此这取决于可用性。
正如其他陈述的那样,C是不需要的,但是C++是不需要的。
包括CAST可以允许C程序或函数作为C++编译。
在C中,这是不必要的,因为void*会自动安全地提升为任何其他指针类型。
但是,如果您在那时进行了强制转换,那么如果您忘记了包括,它会隐藏一个错误。这会导致崩溃(或者更糟的是,不会导致崩溃)直到后来在代码的某个完全不同的部分)。
因为stdlib.h包含malloc的原型。在缺少malloc的原型,标准要求编译器假定malloc返回int。如果没有强制转换,则当此整数分配给指针时发出警告;但是,对于强制转换,不会生成此警告,从而隐藏错误。
在C中,MALOC的铸造是不必要的,但在C++中是强制性的。
C中不需要铸造,因为:
- 在C的情况下,void *自动安全地升级为任何其他指针类型。
- 如果忘记包含,它可以隐藏错误。这会导致崩溃。
- 如果指针和整数大小不同,则通过强制转换隐藏警告,可能会丢失返回地址的位。
- 如果指针的类型在声明时发生了更改,则还可能需要更改调用和强制转换malloc的所有行。
另一方面,强制转换可能会增加程序的可移植性。也就是说,它允许C程序或函数作为C++编译。
- @ AASHISH,在C++中,通常不执行任何内存分配。甚至不用新的。附言:我也不会投反对票。