题 我是否施放了malloc的结果?


这个问题有人建议在 评论 我应该  投下结果 malloc,即

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

而不是:

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

为什么会这样呢?


2043
2018-03-03 10:13


起源


此外,编写sieve = malloc(sizeof * sieve * length)更易于维护; - William Pursell
stackoverflow.com/q/7545365/168175 - Flexo♦
@KarolyHorvath这不是唯一的原因。另一个(也是最重要的,恕我直言),是使代码更灵活(但不是更不安全或强大),如果类型 sieve 从int更改为,例如,浮动。 malloc( sizeof *sieve * length ); 无论何种类型都可以使用,而演员需要维护,不添加任何内容,并且可能会创建难以跟踪的错误。 - MestreLion
@MestreLion:“并且可能会创建难以跟踪的错误” - 所有你需要做的就是启用编译警告,它是 决不 将要发生。 OTOH,如果你不......你真的应该得到它。再说一遍,我不是使用它的拥护者......我只是说它真的不那么重要......它是 基本上无害 :) - Karoly Horvath
也可以看看 具体来说,铸造结果有什么危险 malloc()? - Jonathan Leffler


答案:


没有;您  投下结果,因为:

  • 这是不必要的,因为 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 什么时候 width 和 length 是比较小的类型 size_t


1917
2018-03-03 10:17



@unwind不,即使在严格的C代码中,我也使用强制转换来实现可移植性。我的意思是便携性对于旧版本的c标准是可移植的,其中void *不会被提升。我引用了K&R的“C编程语言”:“在C中,正确的方法是声明malloc返回一个指向void的指针,然后使用强制转换将指针显式强制转换为所需的类型。”我强加这样一个要求的原因是你不能总是选择你可以使用的编译器。 - chacham15
@chacham15虽然有早期版本的C 语言 演员是必要的(K&R C没有 void* 类型!),那些版本从来没有 标准。 C89(第一个标准化版本)要求 malloc 返回 void* 然后 void* 可以隐含地兑换。 - jamesdlin
@Soumen这可能太模糊了;没有包含返回类型 int 将被假设,可能小于 void *,因此赋值可能会生成一个警告,这会被演员阵容压制。看到 这个问题例如。 - unwind
@unwind大多数编译器会警告你一个隐式定义的函数......(所以我没有看到演员的危险) - DarthRubik
@牛米。好。我认为假设在这里阅读的人都有一个特定的编译器是不好的。此外,由于C11整个“隐含功能”概念消失了,我不知道。尽管如此,我还是没有看到添加无意义演员的重点。你也做 int x = (int) 12; 只是为了清楚明白? - unwind


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

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

如果您改变类型,还可以让您不必担心改变表达式的右侧 sieve

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


327
2018-03-03 10:17



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


 演员,因为:

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

292
2018-02-14 16:15



@ulidtko如果您不知道,可以编写以C和C ++编译的代码。事实上,大多数头文件都是这样的,它们通常包含代码(宏和内联函数)。有一个 .c/.cpp 要编译的文件,因为它们都不常用,但有一种情况是添加C ++ throw 用C ++编译器编译时支持(但是 return -1; 用C编译器编译时,或其他)。 - hyde
如果有人在标题中内联malloc调用我不会留下深刻印象,#ifdef __cplusplus和extern“C”{}适用于此作业,而不是添加额外的强制转换。 - paulm
好吧,第1点是无关紧要的,因为C!= C ++,如果你使用的话,其他几点也是微不足道的 变量 在你的 malloc 呼叫: char **foo = malloc(3*sizeof(*foo)); 如果相当全面证明:3个指针指向char指针。然后循环,并做 foo[i] = calloc(101, sizeof(*(foo[i])));。分配101个字符的数组,整齐地初始化为零。不需要演员表。将声明更改为 unsigned char 或者任何其他类型,就此而言,你仍然很好 - Elias Van Ootegem
我什么时候得到它,它来了!很棒的答案。这是我第一次在StackOverflow中找到两个相反的答案! +1不,你不投,+1是的,你投了!大声笑。你们太棒了对于我和我的学生,我做了我的想法:我做了演员。学生在制作时更容易发现错误。 - Dr Beco
@Leushenko:以机器或当地检查无法验证的方式重复自己是不好的。以可以通过这种方式验证的方式重复自己不那么糟糕。特定 struct Zebra *p; ... p=malloc(sizeof struct Zebra);,malloc无法避免重复关于p类型的信息,但是如果一个类型改变但另一个类型没有改变,编译器和本地代码检查都不会检测到任何问题。将代码更改为 p=(struct Zebra*)malloc(sizeof struct Zebra);如果强制转换类型不匹配,编译器会发出尖叫声 p,和 本地 检查将揭示...... - supercat


正如其他人所说,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 ++编译。


148
2018-03-03 11:17



既然你正在使用宏,为什么不使用它 new 在C ++的定义? - Hosam Aly
因为没有理由这样做。它主要用于使用C ++编译器编译的C程序。如果你打算使用'new',你唯一得到的就是问题。然后你还需要一个免费的宏。而且你需要一个宏来释放数组,这是C中不存在的区别。 - quinmars
更不用说如果不是你释放内存但可能是你正在使用的C库等等。许多可能的问题没有任何好处。 - quinmars
@Hosam:是的,肯定是。如果你使用 new 你必须使用 delete 如果你使用 malloc() 你必须 free()。切勿混合它们。 - Graeme Perrow
如果要采用这种方法,请调用宏 NEW 可能是一个坏主意,因为资源永远不会返回使用 delete (要么 DELETE所以你要混合你的词汇量。相反,命名它 MALLOC, 更确切地说 CALLOC 在这种情况下,会更有意义。 - mah


来自 维基百科

铸造的优点

  • 包括强制转换可能允许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的默认行为是显示a   警告,显示“内置的不兼容的隐式声明   功能“无论演员是否存在。

  • 如果指针的类型在其声明处被更改,则可以   另外,需要更改调用malloc和cast的所有行。

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

即:如果你需要将C程序编译为C ++(虽然这些是单独的语言),你应该使用 malloc 铸造。


96
2017-10-09 21:23



什么“如果目标指针类型发生变化,则转换可以帮助开发人员识别类型大小调整中的不一致性,特别是如果指针远离 malloc() 呼叫“是吗?你能举个例子吗? - Cool Guy
@很酷的家伙: 请参阅之前对另一个答案的评论。但请注意 p = malloc(sizeof(*p) * count) 成语自动获取类型中的更改,因此您不必获取警告并进行任何更改。所以这不是一个真正的优势,而不是非铸造的最佳选择。 - Peter Cordes
这是正确的答案:有利有弊,归结为品味(除非代码必须编译为C ++ - 然后强制转换)。 - Peter A. Schneider
第3点是没有意义的,因为如果指针的类型在其声明中被更改,则应该检查malloc的每个实例,realloc并自由地解析该类型。施法将迫使你做到这一点。 - Michaël Roy


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


94
2018-03-03 10:18





你没有强制转换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年的编译器呢?


83
2018-03-20 15:53



“毫无意义的混乱”是一种不屑一顾的夸张,它往往会破坏说服任何不同意你的人的可能性。演员当然不是没有意义的;罗恩伯克和卡斯的回答使我有理由支持铸造,我非常赞同。这些担忧是否比你提到的问题更重要是一个合理的问题。对我而言,与他们相比,您的担忧看起来相对较小。 - Don Hatch
6.3.2.3不支持“void指针可以转换为任何其他指针类型,而不需要显式转换”。也许您正在考虑“指向任何对象类型的指针”? “void指针”和“指向函数的指针”不是那么容易转换的。 - chux
的确,参考文献不完整。 “隐含性”的相关部分是简单赋值的规则6.5.16.1。 “一个操作数是指向对象类型的指针,另一个是指向合格或非限定版本的void的指针”。我已将此参考文献添加到完整性的答案中。 - Lundin


在C中,您将获得隐式转换 void* 到任何其他(数据)指针。


82
2018-03-03 10:16



@Jens:好的,也许更正确的措辞是“隐式转换”。就像在浮点表达式中使用积分变量一样。 - EFraim
@EFraim这实际上会导致一个强制转换,一个隐含的转换。 - Mad Physicist


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

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

参考: C FAQ


62
2018-06-09 17:31



关闭编译器警告是个坏主意。 - Albert van der Horst
@AlbertvanderHorst如果你是通过解决确切的问题而这样做的,那就警告你了。 - Dan Bechard
@Dan。如果通过解决确切的问题意味着重写一个子程序来返回现代ANSI C类型而不是char *,我同意。我不会称之为关闭编译器。不要让那些坚持没有编译器警告的经理屈服,而不是通过每次重新编译来使用它们来查找可能的问题。 Groetjes Albert - Albert van der Horst


投射结果不是强制性的 malloc,因为它返回 void* ,和 void*可以指向任何数据类型。


48
2018-02-07 22:22





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

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


44
2018-03-28 18:21



除非已经提到过,否则强制转换可能会隐藏错误并使代码更难分析编译器或静态分析器。 - Lundin
“基本上铸造不会改变它的工作方式”。转换为匹配类型不应该改变任何东西,但是如果var的类型改变并且转换不再匹配,那么问题会出现吗? IWO,cast和var类型应保持同步 - 维护工作的两倍。 - chux
我明白为什么教授更喜欢铸造。从教育的角度来看,投射可能是有用的,它传达给教师信息并且不需要维护学生代码 - 它的丢弃代码。然而,从编码,同行评审和 保养 透视, p = malloc(sizeof *p * n); 是如此简单和更好。 - chux