我是否施放了malloc的结果?

Patrick McDonald 2192 c malloc casting

这个问题,有人建议意见,我应该不会投的结果malloc,即

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

而不是:

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

为什么会这样呢?

unwind.. 2035

; 你没有投射结果,因为:

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

作为澄清,请注意我说"你不投",而不是"你不需要投".在我看来,即使你做对了也没有包括演员.这样做没有任何好处,但是一堆潜在的风险,包括演员表明你不知道风险.

还要注意,正如评论员指出的那样,上面谈的是直C,而不是C++.我非常坚信C和C++是不同的语言.

要进一步添加,您的代码会不必要地重复int可能导致错误的类型信息().最好取消引用用于存储返回值的指针,将两者"锁定"在一起:

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

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


虽然移动length到前面可能会增加在某些极少数情况下的可见性,但是在一般情况下,应该注意将表达式编写为:

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

因为sizeof在这种情况下保持第一个,所以确保乘法至少用size_t数学来完成.

比较:malloc(sizeof *sieve * length * width)malloc(length * width * sizeof *sieve)第二个可能溢出的length * width时间widthlength比较小的类型size_t.

  • @chacham15虽然有早期版本的C*语言*需要强制转换(K&R C没有`void*`类型!),但这些版本从来都不是*标准*.C89(第一个标准化版本)要求`malloc`返回`void*`并且`void*`可以隐式转换. (126认同)
  • @unwind不,即使在严格的C代码中,我也使用强制转换来实现可移植性.我的意思是便携性对于旧版本的c标准是可移植的,其中void*不会被提升.我引用了K&R的"C编程语言":"在C中,正确的方法是声明malloc返回一个指向void的指针,然后使用强制转换将指针显式强制转换为所需的类型." 我强加这样一个要求的原因是你不能总是选择你可以使用的编译器. (98认同)
  • @nm好的.我认为假设在这里阅读的人都有一个特定的编译器是不好的.此外,由于C11整个"隐函数"概念已经消失,我不知道.尽管如此,我还是没有看到添加无意义演员的重点.你也做`int x =(int)12;`只是为了清楚? (25认同)
  • @unwind大多数编译器会警告你一个隐含定义的函数...(所以我没有看到演员的危险) (10认同)
  • @Soumen这可能太模糊了; 如果没有包含返回类型`int`,它可能会小于`void*`,因此赋值可能会生成一个警告,它会被强制转换抑制.例如,参见[this question](http://stackoverflow.com/questions/11796909/how-to-resolve-cast-to-pointer-from-integer-of-different-size-warning-in-c-co) . (8认同)
  • 请考虑更新答案.演员不再危险,重复自己不一定是坏事(冗余可以帮助捕捉错误). (4认同)
  • 编译器已经改变了.最新的编译器将警告您缺少malloc的声明. (4认同)
  • @nm如果显式地将一个void指针"帮助"解决了一个bug,你很可能会遇到未定义的行为,这意味着有问题的程序可能会有一个更糟糕的,未被发现的bug,你还没有遇到过.有一天,在一个寒冷的冬天的晚上,你会下班回家找到你的GitHub页面上充斥着问题报告抱怨恶魔飞出用户的鼻子 (4认同)
  • @unwind即使我同意你的意见,`(int)12`也不具有可比性.`12`*是*一个`int`,演员什么也没做.`malloc()`的retval是`void*`,而不是指向它的指针类型.(如果它不是`void*`.所以类似于`(int)12`将是`(void*)malloc(...)`没人在讨论什么.) (3认同)
  • "我认为在这里阅读的人都有一个特定的编译器是不好的.另外,自从C11以来,整个"隐式函数"概念都消失了,我不知道" - 它已经消失了,因为C99(不仅仅是C11).如果不包括`#include <stdlib.h>`,任何C99编译器都需要*来发出诊断.在4个子弹中,前3个是主观的(有足够的证据表明有些人倾向于在这个页面中使用它并且在答案之前大约10年,第4个已经用C语言"修复"了.虽然我并不主张赞成演员,但这确实是*在这里制造一个鼹鼠*的情况. (3认同)
  • @Gewure显然很难在没有代码的情况下回答,但可能是因为你没有为`malloc()`的原型`#include <stdlib.h>`,因此让编译器假设它返回`int`,你不能分配没有演员表的指针.在这种情况下,演员是不够的. (3认同)
  • @savram如果`length`和`width`是`int`s,其中`int`比`size_t`的位数少,那么`length*width`可能比最大的`int`大.如果使用`size_t`数学,这不太可能是一个问题,你可以通过首先放置`sizeof`部分来做(因为编译器从左到右读取"乘法") (3认同)
  • *我非常坚信C和C++是不同的语言.*=>你不应该坚持面对历史和现实的事情.C++因其内在的美感而没有继承C语法; 这是因为C代码库可以逐步采用C++特性.另外,使用C++编译器编译C代码库有很多价值,无论你是否最终发布二进制文件,请参阅[C++核心指南](http://isocpp.github.io/ CppCoreGuidelines/CppCoreGuidelines#S-CPL).可以为C写很多基于type_traits的静态检查. (3认同)
  • 我遇到过一个案例,malloc演员的冗余有助于找到一个bug.这不会经常发生,但仍然存在. (2认同)
  • @unwind比较`double d =(double)12;`或`unsigned int u =(unsigned int)12;`可能更有意义. (2认同)
  • @unwind"*Pre-C11,没有原型的函数被假定为返回int*"< - 这已经在C99中消失了. (2认同)

dirkgently.. 343

在C中,您不需要转换返回值malloc.返回的void指针malloc自动转换为正确的类型.但是,如果您希望使用C++编译器编译代码,则需要进行强制转换.社区中的首选替代方案是使用以下内容:

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

如果你改变了类型,你还可以免去担心改变表达式的右侧sieve.

人们已经指出,演员阵容很糟糕.特别是指针转换.

  • @MAKZ我认为`malloc(length*sizeof*sieve)`看起来像`sizeof`是一个变量 - 所以我认为`malloc(length*sizeof(*sieve))`更具可读性. (52认同)
  • 并且`malloc(长度*(sizeof*筛))`更具可读性.恕我直言. (15认同)
  • @Michael Anderson`()`问题除外,请注意你建议的样式改变了顺序.考虑当元素计数像`length*width`一样计算时,在这种情况下首先保持`sizeof`确保乘法至少完成` size_t`数学.比较`malloc(sizeof(*ptr)*length*width)`与`malloc(length*width*sizeof(*ptr))` - 当'width,length`较小时,第二个可能会溢出`length*width`类型为`size_t`. (11认同)
  • C不是C++.假装他们将最终导致混乱和悲伤.如果您使用的是C++,那么C风格的转换也很糟糕(除非您使用的是非常旧的C++编译器).并且`static_cast>()`(或`reinterpret_cast <>()`)与C的任何方言都不兼容. (10认同)
  • @chux它并不明显,但答案已被编辑,以便我的评论不太相关 - 最初的建议是`malloc(sizeof*sieve*length)` (2认同)

Ron Burk.. 311

演员,因为:

  • 它使您的代码在C和C++之间更具可移植性,并且正如SO经验所示,许多程序员声称他们在用C++(或C加本地编译器扩展)编写时正在用C语言编写.
  • 如果不这样做可以隐藏错误:注意所有的混乱的SO例子,当写type *type **.
  • 它让你不会注意到你没有找到#include合适的头文件的想法错过了森林的树木.这就像说"不要担心你没有要求编译器抱怨没有看到原型这一事实 - 那令人讨厌的stdlib.h是真正重要的事情要记住!"
  • 它强制进行额外的认知交叉检查.它将(声称的)所需类型放在您正在为该变量的原始大小进行的算术旁边.我敢打赌你可以做一个SO研究,它表明malloc()当有一个演员时,bug会被抓得更快.与断言一样,显示意图的注释可以减少错误.
  • 以机器可以检查的方式重复自己通常是一个主意.事实上,这就是一个断言,并且使用强制转换是一种断言.断言仍然是我们获得正确代码的最常用技术,因为图灵在很多年前提出了这个想法.

  • 如果有人在标题中内联malloc调用我不会留下深刻印象,#ifdef __cplusplus和extern"C"{}适用于此作业,而不是添加额外的强制转换. (36认同)
  • @ulidtko如果您不知道,可以编写将C和C++编译为同类的代码.事实上,大多数头文件都是这样的,它们通常包含代码(宏和内联函数).有一个`.c` /`.cpp`文件编译为两者并不常用,但有一种情况是在用C++编译器编译时添加C++`throw`支持(但是`返回-1;`用C编译器编译时` , 管他呢). (34认同)
  • 我什么时候得到它,它来了!很棒的答案.这是我第一次在StackOverflow中找到两个相反的答案!+1不,你不投,+1是的,你投了!大声笑.你们太棒了 对于我和我的学生,我做了我的想法:我做了演员.学生在制作时更容易发现错误. (26认同)
  • 好吧,第1点是无关紧要的,因为C!= C++,如果你在`malloc`调用中使用_the variable_,其他点也是微不足道的:`char**foo = malloc(3*sizeof(*foo));`如果完全证明:3个指针指向char指针.然后循环,并执行`foo [i] = calloc(101,sizeof(*(foo [i])));`.分配101个字符的数组,整齐地初始化为零.不需要演员表.将声明更改为`unsigned char`或任何其他类型,就此而言,你仍然很好 (11认同)
  • @Leushenko: Repeating yourself in a way that cannot be validated by machine nor by local inspection is bad. Repeating yourself in ways that can be validated by such means is less bad. Given `struct Zebra*p; ... p=malloc(sizeof struct Zebra);`, the malloc can't avoid duplciating information about p's type, but neither the compiler nor local code inspection would detect any problem if one type changed but the other didn't. Change the code to `p=(struct Zebra*)malloc(sizeof struct Zebra);` and the compiler will squawk if the cast type doesn't match `p`, and*local*inspection will reveal... (11认同)
  • 转换不会隐藏错误 - 事实上它引入了进一步错误的可能性(将int转换为指针,最明显) (9认同)
  • "以机器可以检查的方式重复自己".没有!如果你遗漏演员(一般情况下),如果你做了一些不合理,合理且不便携的事情,你会收到警告.如果你使用演员,你会对编译器说:"闭嘴!我已经调查过了,我知道的更好." 不应该教新手建立演员的习惯.malloc()是一个例子,其中添加演员阵容的伤害相对较小. (7认同)
  • @ Jean-BaptisteYunès:值得查看本文档下面的评论.'p = malloc(sizeof(gadget))`的基本原理没什么意义,因为更惯用的形式只是`p = malloc(sizeof*p)`.他们没有提到,当`<stdlib.h>`标题不存在时(和C89编译器没有义务在这种情况下进行任何诊断),演员可能是危险的,产生令人讨厌的错误,因为假设`malloc`返回一个`int`. (6认同)
  • 有趣的是,@ Leushenko是这里唯一的评论者,说明了铸造不是干的非常重要的一点.恕我直言,这是一个showstopper和我为什么**不**投在C.我用C++吸吮它,因为我必须.也就是说,"重复自己"确实有其用途:在单元测试中.但肯定不是在生产代码中.重复自己只是在寻找麻烦.我很高兴我们都同意不同意但我不能看到自己被脚轮的论点所左右.;-) (4认同)
  • 也许donotcasters可以阅读这个文件:https://www.securecoding.cert.org/confluence/display/seccode/MEM02-C.+Immediately+cast+the+result+of+a+memory+allocation+function+call+成+ A +指针+ TO +的+ +分配类型.我确信不投射比投射更危险,并且施放malloc的结果不是"重复自己". (4认同)
  • 那么,你是否也将`free()`的参数转换为`void*`? (4认同)
  • 很好的答案,谢谢你.隐式转换会导致很多问题,并且您可以很好地阐明原因.也许改写"惊人的愚蠢"部分?这可能会增加接触那些不同意你的人的机会,而不是让他们处于守势,让他们无法听到你所说的明智的事情. (4认同)
  • 还允许您与编译器兼容,默认情况下将C编译为C++.(<咳嗽> MSVC </咳嗽>) (3认同)
  • ...如果强制转换类型与`sizeof`类型不匹配.有人用类型转换检查代码版本可以知道(如果代码编译得很干净)`p`是`void*`或`struct Zebra*`而不必查看`p`的实际声明.在我看来,这是一场胜利. (3认同)
  • 这个答案中有很多谬误.基本上,唯一有效的参数是第一个:交叉编译的C/C++代码.但它只适用于交叉编译的C/C++代码,而不是在任何地方使用强制转换的理由.遗留的论证是基于未能解释一个简单事实的谬误:避免演员本身并不是一个目的,而是更广泛惯用法的一部分 - 编写与类型无关/类型不可知的代码. (3认同)
  • @MohitJain这个说法毫无意义.在Java中,你会抛出一个带有"Object"的方法的参数吗? (2认同)

quinmars.. 156

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

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

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

int *sieve = NEW(int, 1);

它将为C和C++编译.

  • @Hosam:是的,肯定是.如果你使用`new`你必须使用`delete`,如果你使用`malloc()`你必须你`free()`.切勿混合它们. (82认同)
  • 因为没有理由这样做.它主要用于使用C++编译器编译的C程序.如果你打算使用'new',你唯一得到的就是问题.然后你还需要一个免费的宏.而且你需要一个宏来释放数组,这是C中不存在的区别. (57认同)
  • 既然你还在使用宏,为什么不在C++的定义中使用`new`? (16认同)
  • 如果要采用这种方法,调用宏"NEW"可能是一个坏主意,因为资源永远不会使用`delete`(或`DELETE`)返回,所以你混合你的词汇.相反,在这种情况下命名为"MALLOC",或者更确切地说是"CALLOC",会更有意义. (15认同)
  • 更不用说如果不是你释放内存但可能是你正在使用的C库等等.许多可能的问题没有任何好处. (8认同)
  • 嗯......我没想到.使用`free()`来释放用`new`分配的内存是错误的吗? (4认同)
  • @LuisColorado此解决方案适用于C和C++之间的互操作式代码.它不是用C++做事 - 惯用的方式,它是关于使C代码编译和轻松移植到C++编译器. (3认同)

ashiquzzaman.. 111

来自维基百科

铸造的优点

  • 包括强制转换可能允许C程序或函数编译为C++.

  • 演员版允许1989年之前版本的malloc最初返回char*.

  • 如果目标指针类型发生变化,则转换可以帮助开发人员识别类型大小调整中的不一致性,特别是如果指针远离malloc()调用(尽管现代编译器和静态分析器可以在不需要强制转换的情况下警告此类行为).

铸造的缺点

  • 根据ANSI C标准,演员阵容是多余的.

  • 添加强制转换可能会掩盖包含标头stdlib.h的失败,其中找到了malloc的原型.在没有malloc原型的情况下,该标准要求C编译器假设malloc返回一个int.如果没有强制转换,则在将此整数分配给指针时会发出警告; 然而,对于演员,这个警告不会产生,隐藏了一个bug.在某些体系结构和数据模型上(例如64位系统上的LP64,其中long和指针是64位且int是32位),此错误实际上可能导致未定义的行为,因为隐式声明的malloc返回32-位值,而实际定义的函数返回64位值.根据调用约定和内存布局,这可能会导致堆栈粉碎.在现代编译器中,这个问题不太可能被忽视,因为它们统一产生了使用未声明功能的警告,所以仍然会出现警告.例如,GCC的默认行为是显示一条警告,该警告显示"内置函数的不兼容隐式声明",无论是否存在强制转换.

  • 如果指针的类型在其声明处被更改,则还可能需要更改调用malloc并进行转换的所有行.

虽然没有铸造的malloc是首选方法,而且大多数有经验的程序员选择它,你应该使用你想知道的问题.

ie:如果你需要将C程序编译为C++(虽然这些是单独的语言),你应该使用malloccast.

  • 这是正确的答案:有利有弊,归结为品味(除非代码必须编译为C++ - 然后强制转换). (7认同)
  • 第3点是没有实际意义的,因为如果指针的类型在其声明中被更改,则应该检查malloc的每个实例,realloc并自由地解析该类型.施法将迫使你做到这一点. (3认同)
  • @CoolGuy:[见另一个回答的早期评论](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc?rq=1#comment53162479_14879184).但请注意,`p = malloc(sizeof(*p)*count)`idiom会自动获取类型中的更改,因此您不必获取警告并进行任何更改.所以这不是一个真正的优势,而不是非铸造的最佳选择. (2认同)

PaulJWilliam.. 97

在C中,您可以隐式地将void指针转换为任何其他类型的指针,因此不需要强制转换.使用一个人可能会向不经意的观察者建议,有一些理由需要一个,这可能会产生误导.


Lundin.. 87

你没有强制转换malloc的结果,因为这样做会给你的代码带来无意义的混乱.

人们投射malloc结果的最常见原因是因为他们不确定C语言是如何工作的.这是一个警告信号:如果您不知道特定语言机制的工作原理,那么请不要猜测.查找或询问Stack Overflow.

一些评论:

  • 可以将void指针转换为/从任何其他指针类型转换而不进行显式转换(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年的编译器呢?

  • "毫无意义的混乱"是一种不屑一顾的夸张,它往往会破坏说服任何不同意你的人的可能性.演员当然不是没有意义的; 罗恩伯克和卡斯的回答使我有理由支持铸造,我非常赞同.这些担忧是否比你提到的问题更重要是一个合理的问题.对我来说,与他们相比,你的担忧看起来相对较小. (6认同)

EFraim.. 85

在C中,您可以从void*任何其他(数据)指针进行隐式转换.

  • @Jens:好的,也许更正确的措辞是"隐式转换".就像在浮点表达式中使用积分变量一样. (5认同)

Yu Hao.. 65

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

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

参考:C FAQ

  • @AlbertvanderHorst如果你是通过解决确切的问题而这样做,那就警告你了. (7认同)
  • 关闭编译器警告是个坏主意. (2认同)

user968000.. 48

malloc由于它返回void*,因此不必强制转换结果,并且void*可以指向任何数据类型.


user3079666.. 47

只是添加我的经验,学习计算机工程我看到我在C中写过的两三位教授总是施放malloc,但是我问过的那个(有着巨大的简历和对C的理解)告诉我这绝对是不必要的但是以前只是绝对具体,并让学生进入绝对具体的心态.基本上,转换不会改变它的工作方式,它完全按照它所说的方式,分配内存,而且转换不会影响它,你得到相同的内存,即使你错误地将它转换为别的东西(并以某种方式规避编译器)错误)C将以相同的方式访问它.

编辑:施法有一定的意义.当您使用数组表示法时,生成的代码必须知道它必须前进多少内存位置才能到达下一个元素的开头,这是通过强制转换实现的.通过这种方式,你知道对于一个双倍,你前进8个字节而对于一个int你去4个,依此类推.因此,如果使用指针表示法则无效,在数组表示法中它变得必要.

  • 除非已经提到过,否则强制转换可能会隐藏错误并使代码更难分析编译器或静态分析器. (3认同)
  • "基本上铸造不会改变它的工作方式".转换为匹配类型不应该改变任何东西,但是如果var的类型改变并且转换不再匹配,那么问题会出现吗?IWO,cast和var类型应保持同步 - 维护工作的两倍. (2认同)

Slothworks.. 29

这就是GNU C Library Reference手册所说的:

您可以将结果存储malloc到任何指针变量中而不进行强制转换,因为ISO C会void *在必要时自动将类型转换为另一种类型的指针.但是,在赋值运算符以外的情况下,或者您可能希望代码在传统C中运行时,强制转换是必需的.

事实上,ISO C11标准(p347)是这样说的:

如果分配成功,则返回指针,以便可以将其分配给指向具有基本对齐要求的任何类型对象的指针,然后用于在分配的空间中访问此类对象或此类对象的数组(直到空间被明确释放)


Endeavour.. 28

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

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


大智慧.. 27

返回的类型为void*,可以将其强制转换为所需类型的数据指针,以便可以解除引用.


August Karls.. 25

在C语言中,可以将void指针分配给任何指针,这就是您不应该使用类型转换的原因.如果你想要"类型安全"分配,我可以推荐以下宏函数,我总是在我的C项目中使用它们:

#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

有了这些,你可以简单地说

NEW_ARRAY(sieve, length);

对于非动态数组,第三个必备函数宏是

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

这使得数组循环更安全,更方便:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}


Jeyamaran.. 24

这取决于编程语言和编译器.如果你malloc在C中使用,则不需要输入强制转换,因为它会自动键入强制转换,但是如果你使用C++,那么你应该输入强制转换,因为malloc它将返回一个void*类型.


StephenG.. 14

习惯于GCC和Clang的人都被宠坏了.那里并不是那么好.

多年来,我一直被我需要使用的令人咋舌的老化编译器吓坏了.通常,公司和管理人员采用极端保守的方法来更改编译器,甚至不会测试新的编译器(具有更好的标准兼容性和代码优化)是否适用于他们的系统.工作开发人员的实际情况是,当你编写代码时,你需要覆盖你的基础,不幸的是,如果无法控制编译器可能应用于你的代码,那么铸造mallocs是一个好习惯.

我还建议许多组织应用自己的编码标准,应该是人们在定义时遵循的方法.在没有明确指导的情况下,我倾向于最有可能在任何地方进行编译,而不是盲目遵守标准.

根据现行标准,不必要的论点是非常有效的.但是这个论点忽略了现实世界的实用性.我们不是在一个完全由当时标准统治的世界中编码,而是在我喜欢称之为"本地管理的现实领域"的实际情况中.这比以往任何时候都更加弯曲和扭曲.:-)

因人而异.

我倾向于将malloc视为防御性操作.不漂亮,不完美,但一般都很安全.(老实说,如果你不包含stdlib.h中那么你的方式比铸造的malloc更多的问题!).


user877329.. 12

只要有可能,在C语言编程时最好的事情是:

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

此过程允许您利用C++严格类型检查,从而减少错误的数量.特别是,此程序会强制您包含stdlib.h或者您将获得

malloc 未在此范围内声明

并且还强迫你施放结果malloc或者你会得到

无效转换void*T*

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

使用C语言而不是C++编写的唯一好处是我能找到的

  1. C具有明确指定的ABI
  2. C++可能会生成更多代码[异常,RTTI,模板,运行时多态]

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

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

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

  • 对C代码使用C编译器.对C++代码使用C++编译器.不,如果没有,但没有.用C++重写你的C代码完全是另一回事,可能 - 或可能不是 - 值得花时间和风险. (17认同)
  • 我想添加@TobySpeight建议:如果需要在C++项目中使用C代码,通常可以将C代码编译为C(例如`gcc -c c_code.c`),将C++代码编译为C++(例如`g ++ -c cpp_code.cpp`),**然后将它们链接在一起**(例如`gcc c_code.o cpp_code.o`,反之亦然,具体取决于项目依赖性).现在应该没有理由剥夺自己任何语言的任何好的功能...... (2认同)

Kaz.. 12

我只是为了表示不赞成类型系统中的丑陋洞而放入演员表,这允许在没有诊断的情况下编译下面的代码片段,即使没有使用强制转换来导致错误的转换:

double d;
void *p = &d;
int *q = p;

我希望这不存在(并且它不在C++中),所以我投了.它代表了我的品味和我的编程政治.我不仅投了一个指针,而且还有效地投了一张选票,然后抛弃了愚蠢的恶魔.如果我实际上 无法摆脱愚蠢,那么至少让我以抗议的姿态表达希望这样做.

事实上,一个好的做法是malloc用函数返回(和朋友)返回unsigned char *,并且基本上永远不会void *在你的代码中使用.如果您需要一个通用的指向任意对象的指针,请使用char *unsigned char *,并在两个方向上进行转换.可能放纵的一种放松可能是使用类似memsetmemcpy不使用强制转换的函数.

关于转换和C++兼容性的话题,如果你编写代码使它编译为C和C++(在这种情况下你必须转换将其malloc赋值给其他东西时的返回值void *),你可以做一个非常有帮助的对你自己来说:你可以使用宏进行转换,在编译为C++时转换为C++样式转换,但在编译为C时减少为C转换:

/* 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++编译代码,它将强制使用适当的强制转换.例如,如果你strip_qual只是用来删除一个const或者volatile,但程序改变的方式现在涉及类型转换,你将得到一个诊断,你将不得不使用一组转换来获得所需的转换.

为了帮助您遵守这些宏,GNU C++(而不是C!)编译器具有一个很好的特性:为所有出现的C样式转换生成一个可选的诊断.

     -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++,您可以使用此-Wold-style-cast选项找出(type)可能蔓延到代码中的所有出现的转换语法,并通过从上述宏中适当选择替换它们来跟进这些诊断(或者组合,如有必要).

这种对转换的处理是在"清洁C"中工作的单一最大的独立技术理由:C和C++组合的方言,从而在技术上证明了铸造返回值的合理性malloc.


proski.. 11

我喜欢做演员,但不是手动.我最喜欢的是使用g_newg_new0从油嘴滑舌宏.如果没有使用glib,我会添加类似的宏.这些宏减少了代码重复,而不会影响类型安全性.如果你得到错误的类型,你会得到非void指针之间的隐式转换,这会引起警告(C++中的错误).如果你忘了,包括定义页眉g_newg_new0,你会得到一个错误.g_new并且g_new0两者都采用相同的参数,不像malloc参数少calloc.只需添加0即可获得零初始化内存.代码可以使用C++编译器进行编译而无需更改.


大智慧.. 11

不,你没有投出结果malloc().

一般来说,你不会投射或投射void *.

不这样做的一个典型原因是未能#include <stdlib.h>被忽视.这已经不再是一个问题,因为C99使隐式函数声明非法,所以如果你的编译器符合至少C99,你将得到一条诊断消息.

但是有一个更强大的理由不引入不必要的指针转换:

在C中,指针强制转换几乎总是一个错误.这是因为以下规则(N1570中的§6.5p7,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++有一整套不同的演员风格.


大智慧.. 10

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


iec2011007.. 9

void指针背后的概念是它可以转换为任何数据类型,这就是malloc返回void的原因.您还必须了解自动类型转换.因此,必须执行此操作并不强制转换指针.它有助于保持代码清洁并有助于调试

  • "_这不是强制性的 - 尽管你必须这样做" - 我认为那里存在矛盾! (10认同)
  • 我想你应该把这篇文章读给某人,看看他们是否理解你想说的话.然后重写它,明确你想说什么.我真的无法理解你的答案是什么. (5认同)

dhana govind.. 6

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

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


大智慧.. 6

  1. 正如其他人所说,C不是必需的,而是C++.

  2. 包括强制转换可能允许C程序或函数编译为C++.

  3. 在C中它是不必要的,因为void*被自动且安全地提升到任何其他指针类型.

  4. 但是如果你强制转换,那么如果忘记包含stdlib.h,它就会隐藏错误 .这可能会导致崩溃(或者更糟糕的是,直到稍后在代码的某些完全不同的部分中才会导致崩溃).

    因为stdlib.h包含找到malloc的原型.在没有malloc原型的情况下,该标准要求C编译器假定malloc返回一个int.如果没有强制转换,则在将此整数分配给指针时会发出警告; 然而,对于演员,这个警告不会产生,隐藏一个错误.


归档时间:

查看次数:

192015 次

最近记录:

11 月 前