我要转换malloc的结果吗?


2406

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

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

而不是:

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

为什么会这样呢?


221
另外,编写sieve = malloc(sizeof * sieve * length);更容易维护。
威廉·珀塞尔


3
答案是单方面的雷区。答案是“取决于”。使程序正常工作不是必需的。但是,某些编码标准需要它...例如,请参阅CERT C编码标准
Person Person II博士

奇怪的是,每个人都同意你不投NULL。(这可能就是C ++引入的原因nullptr:C ++不允许任何隐式指针强制转换)
Sapphire_Brick

可以,但是不需要。但是您需要使用C ++。
没有尸体

Answers:


2215

; 您投放结果,因为:

  • 这是不必要的,因为void *在这种情况下会自动安全地提升为任何其他指针类型。
  • 它给代码增加了混乱,强制转换不是很容易阅读(特别是如果指针类型很长的话)。
  • 它使您重复自己,这通常是不好的。
  • 如果您忘记包含,可能会隐藏错误<stdlib.h>。这可能会导致崩溃(或者,更糟糕的,没有代码的一些完全不同的部分会导致崩溃,直到后来的方式)。考虑一下如果指针和整数的大小不同会怎样?那么您将通过投射隐藏警告,并且可能会丢失返回地址的一部分。注意:从C99开始,隐式函数已从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)vs。malloc(length * width * sizeof *sieve)秒可能溢出length * widthwhen widthlength小于类型size_t


21
请考虑更新答案。角色转换不再是危险的,重复自己不一定是一件坏事(冗余可以帮助捕获错误)。
n。代词

11
编译器已更改。最新的编译器将警告您缺少malloc的声明。
n。代词

55
@nm好。我认为假设在这里阅读的任何人都有特定的编译器是很不好的。而且,由于C11的整个“隐式功能”概念都消失了,我也不知道。不过,我看不出添加毫无意义的演员表的意义。您是否也int x = (int) 12;只是为了弄清楚事情?
2016年

22
@nm如果显式转换为“ helped”的void指针可解决问题,则您更有可能遇到未定义的行为,这意味着所讨论的程序可能会遇到更差的,尚未发现的错误。有一天,在一个寒冷的冬天晚上,您将下班回家,找到充满问题报告的GitHub页面,其中抱怨着恶魔从用户的鼻子里飞出来
Braden Best

12
@unwind即使我同意你的看法,(int)12也无法比拟。12 int,中投确实根本不算什么。的检索malloc()void *,而不是强制转换为的指针类型。(如果不是void *,所以比喻(int)12(void*)malloc(…)什么没有人讨论。)
阿明Negm -阿瓦德

376

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

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

此外,如果您更改了表达式的类型,这也使您不必担心更改表达式的右侧 sieve

正如人们指出的那样,演员表很糟糕。特别是指针转换。


71
@MAKZ我认为这malloc(length * sizeof *sieve)使它看起来像是sizeof一个变量-因此我认为malloc(length * sizeof(*sieve))它更具可读性。
Michael Anderson

21
而且malloc(length * (sizeof *sieve))更具可读性。恕我直言。
Toby Speight

18
抛开@Michael Anderson的()问题,请注意,您建议的样式已切换顺序。,考虑元素计数的计算方式如length*widthsizeof在这种情况下,请保持第一个数以确保至少用size_t数学运算完成乘法。比较malloc(sizeof( *ptr) * length * width)对比 malloc(length * width * sizeof (*ptr))-第二可能溢出length*width时,width,length较小类型size_t
chux-恢复莫妮卡2015年

3
@chux并不明显,但是答案已经过编辑,因此我的评论没有那么重要-最初的建议是malloc(sizeof *sieve * length)
Michael Anderson

12
C不是C ++。假装自己会最终导致混乱和悲伤。如果您使用的是C ++,则C样式的转换也是不好的(除非您使用的是非常老的C ++编译器)。和static_cast>()(或reinterpret_cast<>())不与C的任何方言兼容
戴维·

349

进行强制转换是因为:

  • 它使您的代码更具可移植性在C和C ++之间,并且正如SO经验所表明的那样,许多程序员声称他们实际上是在用C ++(或C加本地编译器扩展)编写时才用C编写。
  • 不这样做可能会隐藏一个错误:请注意所有SO示例,它们在编写type *和编写时会混淆type **
  • 阻止您注意#include适当的头文件失败的想法错过了树木的森林。就像说“不要担心您没有要求编译器抱怨没有看到原型的事实-讨厌的stdlib.h是要记住的真正重要的事情!”
  • 它迫使进行额外的认知交叉检查。它将(指定的)所需的类型放在要对该变量的原始大小执行的算术旁边。我敢打赌,您可以进行SO研究,表明malloc()更快地错误。与断言一样,揭示意图的注释会减少错误。
  • 以机器可以检查的方式重复自己通常是个主意。实际上,这就是一个断言,而对cast的这种使用就是一个断言。断言仍然是我们使代码正确的最通用技术,因为Turing早在多年前就提出了这个想法。

39
@ulidtko如果您不知道,可以编写同时编译为C和C ++的代码。实际上,大多数头文件都是这样的,并且它们通常包含代码(宏和内联函数)。同时使用.c/ .cpp文件进行编译不是很有用,但一种情况是throw在使用C ++编译器编译时(但return -1;在使用C编译器编译时或其他方式下)添加C ++ 支持。
海德

37
如果有人在标头中内联malloc调用,我不会被打动,#ifdef __cplusplus和extern“ C” {}用于此工作,而不添加额外的强制转换。
2013年

15
好吧,点1是无关紧要的,因为C!= C ++,如果在调用中使用变量,则其他点也很无关紧要mallocchar **foo = malloc(3*sizeof(*foo));如果非常充分证明:3个指向char指针的指针。然后循环执行foo[i] = calloc(101, sizeof(*(foo[i])));。分配101个字符的数组,整齐地初始化为零。不需要演员表。将此声明更改为unsigned char或其他任何类型,您仍然会很好
Elias Van Ootegem 2013年

34
当我坚韧起来时,它就来了!很棒的答案。这是我在StackOverflow中第一次为两个相反的答案+1!+1否,您不投射,而+1是,您确实投射!大声笑。你们真棒。而对于我和我的学生,我下定了决心:我投了。投射时更容易发现学生犯的错误。
Beco博士

15
@Leushenko:以机器或本地检查无法验证的方式重复自己是不好的。以可以通过这种方式验证的方式重复自己,情况也不错。给定struct Zebra *p; ... p=malloc(sizeof struct Zebra);,malloc不能避免复制有关p的类型的信息,但是如果一种类型改变而另一种类型没有改变,则编译器和本地代码检查都不会检测到任何问题。更改代码为p=(struct Zebra*)malloc(sizeof struct Zebra);,如果强制转换类型不匹配p,编译器将发出尖叫声,并且本地检查将显示...
supercat 2015年

170

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


17
既然仍然使用宏,为什么不在newC ++的定义中使用宏呢?
Hosam Aly

63
因为没有理由这样做。它主要用于使用C ++编译器编译的C程序。如果您要使用“新”,那么唯一遇到的就是问题。然后,您还需要一个免费的宏。而你需要一个宏来释放一个数组,即不存在C.分化
quinmars

8
更不用说释放内存的不是您,而是您正在使用的C库,等等。许多可能的问题没有任何收获。
quinmars

86
@Hosam:是的,肯定是。如果你使用new,你必须使用delete,如果你使用malloc()你必须你free()。切勿混合。
Graeme Perrow

17
如果要采用这种方法,则调用宏NEW可能不是一个好主意,因为永远不会使用delete(或DELETE)返回资源,因此您要混用词汇表。相反,命名它MALLOC(或更确切地说CALLOC在这种情况下)会更有意义。
2014年

139

维基百科

铸造的优势

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

  • 强制转换允许1989年前的malloc版本最初返回char *。

  • 如果目标指针类型发生更改,则强制转换可以帮助开发人员识别类型大小不一致的地方,尤其是如果声明的指针远离malloc()调用时(尽管现代编译器和静态分析器可以在不需要强制转换的情况下警告此类行为)。

铸造的缺点

  • 在ANSI C标准下,强制转换是多余的。

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

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

尽管不进行强制转换的malloc是首选方法,并且大多数有经验的程序员都选择它,但是您应该使用任何了解问题的方法。

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


1
如果目标指针类型发生更改,尤其是如果指针被声明为远离malloc()调用,则“ 强制转换可以帮助开发人员识别类型大小不一致 ”是什么意思?你能举个例子吗?
Spikatrix '16

3
@CoolGuy:参见前面对另一个答案的评论。但是请注意,该p = malloc(sizeof(*p) * count)习惯用法会自动获取类型中的更改,因此您不必获得警告就可以进行任何更改。因此,与非广播的最佳选择相比,这并不是真正的优势。
彼得·科德斯

7
这是正确的答案:有优点也有缺点,并且归结为一个趣味性(除非代码必须作为C ++进行编译,否则强制转换)。
彼得-恢复莫妮卡

3
第3点没有意义,因为如果指针的类型在其声明处更改,则应检查malloc的每个实例,然后重新分配并释放该类型的指针。强制转换会迫使您这样做。
米歇尔·罗伊

104

在C中,您可以将void指针隐式转换为任何其他类型的指针,因此不需要强制转换。使用一个可能会向不经意的观察者暗示需要某个原因的某些原因,这可能会引起误解。


100

您不会转换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年历史的编译器呢?


9
“毫无意义的混乱”是轻描淡写的夸张,倾向于使说服任何尚未同意您的人的可能性脱轨。演员表肯定不是毫无意义的。罗恩·伯克(Ron Burk)和卡兹(Kaz)的回答提出了我非常同意的偏爱演员表的论点。这些关注点是否比您提到的关注点重,是一个合理的问题。对我而言,与他们相比,您的顾虑看起来相对较小。
唐·哈奇

6.3.2.3不支持“可以在没有显式强制转换的情况下将空指针转换为任何其他指针类型/从任何其他指针类型转换为空指针”。也许您在考虑“指向任何对象类型的指针”?“无效指针”和“函数指针”不是那么容易转换。
chux-恢复莫妮卡

确实,参考文献不完整。“隐含性”的相关部分是简单分配规则6.5.16.1。“一个操作数是指向对象类型的指针,而另一个是指向void的合格或不合格版本的指针”。我已将此参考信息添加到答案中以确保完整性。
伦丁'18

91

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


6
@Jens:好的,也许更恰当的措辞是“隐式转换”。就像在浮点表达式中使用积分变量一样。
EFraim 2012年

@EFraim实际上会导致强制转换,并在此隐式转换。
疯狂物理学家

71

投射由返回的值 malloc()现在不需要,但是我想补充一点,似乎没有人指出:

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

参考:C常见问题解答


2
关闭编译器警告不是一个好主意。
艾伯特·凡德·霍斯特

8
@AlbertvanderHorst如果您通过解决确切的问题来这样做,则警告会警告您。
Dan Bechard

@丹。如果通过解决确切的问题意味着重写子例程以返回现代ANSI C类型而不是char *,我同意。我不会称其为关闭编译器。不要屈服于那些坚持没有编译器警告的管理者,而应该在每次重新编译时都使用它们来发现可能的问题。Groetjes Albert-阿尔伯特
·

53

只是加我的经验,研究计算机工程,我发现我看到用C语言编写的两三位教授总是会转换malloc,但是我问的一位教授(具有丰富的CV和对C的理解)告诉我,这绝对是不必要的,但仅过去是绝对特定的,并且使学生陷入绝对特定的心态。本质上,强制转换不会改变其工作方式,它会完全按照其说的那样进行操作,分配内存,并且强制转换不会影响它,您将获得相同的内存,即使您错误地将其强制转换为其他内容(并以某种方式逃避了编译器)错误),C将以相同的方式访问它。

编辑:铸造有一定意义。使用数组表示法时,生成的代码必须知道要到达下一个元素的开头要前进多少个内存位置,这是通过强制转换实现的。这样一来,您就知道对于双精度而言,您要提前8个字节,而对于int而言,您要提前4个字节,依此类推。因此,如果使用指针表示法则没有效果,而在数组表示法中则很有必要。


3
除非已经提到,否则强制转换可能会隐藏错误,并使代码更难为编译器或静态分析器进行分析。
伦丁

2
“基本上,投射不会改变其工作方式”。强制转换为匹配类型不会改变任何内容,但是var的类型是否应该更改且强制转换不再匹配,是否会出现问题?在IWO中,cast和var类型应保持同步-维护工作的两倍。
chux-恢复莫妮卡2014年

我明白了为什么教授更喜欢铸造。从教育的角度讲,转换可能会很有用,因为它可以向教师传达信息,并且不需要维护学生代码-它的一次性代码。然而,从编码,同行评审和维护的角度来看,它p = malloc(sizeof *p * n);是如此简单且更好。
chux-恢复莫妮卡

53

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


34

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

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


2
将单个源代码同时编译为C和C ++是不正常的用例(例如,使用包含声明的头文件将C和C ++代码链接在一起)。malloc在C ++中使用和朋友是一个很好的警告信号,它值得特别注意(或用C重新编写)。
Toby Speight 2015年

1
“无效指针是通用指针”->“无效指针是通用对象指针”。函数指针的大小可能会超过 void *,因此void *不足以很好地存储函数指针。
chux-恢复莫妮卡

我对该行的意图是相同的,但无论如何都要感谢@chux的建议。
奋进

33

这就是《 GNU C库参考》手册所说的内容:

您可以将结果存储malloc到任何指针变量中而无需强制转换,因为ISO C void *在必要时会自动将类型转换为另一种指针类型。但是,在除赋值运算符之外的其他上下文中,或者如果您希望代码在传统C语言中运行,则强制转换是必需的。

实际上,ISO C11标准(p347)表示:

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


31

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


1
void* 可以强制转换为所需的类型,但无需这样做,因为它将被自动转换。因此,强制转换不是必需的,而实际上由于高分答案中提到的原因是不希望的。
Toby Speight 2015年

但仅当您需要“即时”取消引用它时,如果您创建变量,它将被安全且自动地转换为变量的有效类型,而无需强制转换(在C中)。
Ferrarezi

28

在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++) {
   ...
}

“可以将空指针分配给任何对象指针”功能指针是另一个问题,尽管不是一个问题malloc()
chux-恢复莫妮卡

void*在这些情况下,向函数指针分配或从函数指针分配可能会丢失信息,因此“可以将空指针分配给任何指针”是一个问题。但是,将void*from 分配malloc() 给任何对象指针都不是问题。
chux-恢复莫妮卡

do与涉及循环尚未宏想知道从标题的问题走循环评论。删除该评论。以后也将这个拆下来。
chux-恢复莫妮卡

27

这取决于编程语言和编译器。如果malloc在C语言中使用,则无需键入强制转换,因为它将自动键入强制转换。但是,如果您使用的是C ++,则应键入cast,因为malloc它将返回一个void*类型。


1
函数malloc也会在C语言中返回一个void指针,但语言规则与C ++不同。
2015

16

习惯了GCC和Clang的人都被宠坏了。那里不是很好。

多年来,我一直被要求使用的陈旧的编译器吓坏了。公司和经理经常采用超保守的方法来更改编译器,甚至不会测试是否有新的编译器(具有更好的标准合规性和代码优化功能)可以在其系统中工作。对于正在工作的开发人员来说,实际的现实情况是,在编写代码时,您需要覆盖基础知识,并且不幸的是,如果无法控制将哪种编译器应用于代码,则强制转换malloc是一个好习惯。

我还建议许多组织采用自己的编码标准, 应该是如果它被定义的方法的人跟随。在没有明确指导的情况下,我倾向于最有可能在任何地方进行编译,而不是刻意遵守标准。

在当前的标准下没有必要的论点是正确的。但是,这种说法忽略了现实世界的实用性。我们不是在一个完全由当今的标准统治的世界中进行编码,而是在我喜欢称之为“本地管理的现实领域”的实用性方面。而且这种弯曲和扭曲比空时更是如此。:-)

YMMV。

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


15

我只是为了表示不赞成使用类型系统中的丑陋漏洞而使用了强制类型转换,即使没有使用强制类型转换导致不良转换,该类型系统也允许以下代码段的代码无需诊断即可编译:

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

我希望它不存在(并且在C ++中不存在),所以我进行了转换。它代表了我的品味和我的编程政治。我不仅在投下指针,而且在有效地投下选票,并投出愚蠢的恶魔。如果我不能真正 消除愚蠢,那么至少让我表达抗议的意愿。

实际上,一个好的做法是malloc使用return的函数包装(和朋友)unsigned char *,并且基本上不要void *在您的代码中使用它们。如果需要通用的指向任何对象的指针,请使用char *unsigned char *,并进行双向转换。可以放纵的一种放松可能是使用类似memsetmemcpy不使用强制转换的功能。

关于强制转换和C ++兼容性这一主题,如果您编写代码以使其同时作为C和C ++进行编译(在这种情况下,当您将其赋值给除以外的其他值时,您必须强制转换返回值),那么您可以做一个非常有帮助的工作自己做一件事:您可以使用宏进行转换,当转换为C ++时可以转换为C ++样式转换,但是当转换为C时可以转换为C转换:mallocvoid *

/* 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仅用于删除constvolatile,但是程序更改的方式使得现在涉及类型转换,则将得到诊断,并且必须使用强制类型转换的组合来获得所需的转换。

为了帮助您坚持使用这些宏,GNU C ++(不是C!)编译器具有一个漂亮的功能:针对所有C样式强制转换生成的可选诊断程序。

     -Wold-style-cast(仅C ++和Objective-C ++)
         如果使用旧样式(C样式)强制转换为非无效类型,则发出警告
         在C ++程序中。新型的类型转换(dynamic_cast,
         static_cast,reinterpret_cast和const_cast)的脆弱性较小
         产生意想不到的效果,并且更容易搜索。

如果您的C代码编译为C ++,则可以使用此-Wold-style-cast选项找出(type)可能会渗入到代码中的所有强制转换语法,并通过在上述宏(或组合(如果需要)。

转换的处理是在“ Clean C”中工作的最大的独立技术理由:C和C ++的组合方言,这又在技术上证明了转换C的返回值是合理的malloc


正如其他指出的那样,我通常建议不要混合使用C和C ++代码。但是,如果您有充分的理由这样做,则宏可能会很有用。
Phil1970年

@ Phil1970它们全部用一种统一的方言编写,恰好可以移植到C和C ++编译器中,并利用C ++的某些功能。它必须全部编译为C ++,否则都必须编译为
C。– Kaz

即我在先前的评论中试图说的是C和C ++没有混合。目的是将代码全部编译为C或全部编译为C ++。
卡兹(Kaz)2013年

15

尽可能在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...

18
将C编译器用于C代码。将C ++编译器用于C ++代码。没有,没有。用C ++重写C代码完全是另一回事,可能值得(也可能不值得)花费时间和风险。
Toby Speight 2015年

2
我想向@TobySpeight建议:如果您需要在C ++项目中使用C代码,通常可以将C代码编译为C(例如gcc -c c_code.c),将C ++代码编译为C ++(例如g++ -c cpp_code.cpp),然后将它们链接在一起(例如gcc c_code.o cpp_code.o,反之亦然,取决于项目依赖性)。现在应该没有理由剥夺任何一种语言的任何出色功能了……
自闭症患者

1
@ user877329这是一种更明智的选择,而不是仅仅为了与“ C ++兼容”而努力地向代码添加强制类型转换以降低代码的可读性。
自闭症2015年

1
在这种情况下,主要的优势可能是C允许您编写p = malloc(sizeof(*p));,如果p更改为其他类型名称,则无需首先更改。提议的强制转换的“优势”是,如果p类型错误,则会出现编译错误,但如果“ Just Works”会更好。
彼得·科德斯

1
我想提一下,在针对缺少适当C ++编译器的平台时,可能需要用C编写。异常和模板功能,典型地帮助C ++来产生更小和/或更有效的代码,而在C ++运行时多态性是大多相当于C.
user7860670

15

不,您不会转换的结果malloc()

通常,您不会向或从进行强制转换void *

不这样做的一个典型原因是失败#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; dr

一言以蔽之:因为在C,任何一个发生指针投应提高对需要特别注意的代码红旗,你永远不应该写不必要的指针类型转换。


旁注:

  • 在某些情况下,您实际上需要强制转换为void *,例如,如果要打印指针:

    int x = 5;
    printf("%p\n", (void *)&x);

    在这里printf()强制转换是必需的,因为它是可变函数,所以隐式转换不起作用。

  • 在C ++中,情况有所不同。在处理派生类的对象时,强制转换指针类型有些普遍(并且正确)。因此,是非常有意义的在C ++中,转换和从void *隐式的。C ++具有一整套不同的铸造风格。


1
在您的示例中,请避免使用*。从double *到int *的转换和反之亦然。malloc返回指向最大标准类型的pointel,因此即使有人将对齐的指针强制转换为其他类型,也不会破坏别名规则。
P__J__

别名有什么都做比对,并为您的评论的休息-你显然没有抓住要点。

@PeterJ:以防万一,目的是避免不必要的指针转换,因此它看起来像一段代码,您不必特别注意。

严格的别名问题实际上与void指针没有任何关系。为了获取由严格的别名冲突引起的错误,必须取消引用所指向的数据。而且由于您不能取消引用void指针,因此每个定义中的错误均与void指针无关,而与其他内容无关。
伦丁

相反,您必须制定规则禁止所有指针强制转换。但是,您将如何编写序列化例程和与硬件相关的编程之类的东西呢?C的力量所在。如果您知道自己在做什么,那么这样的强制转换就可以了。
伦丁

15

我更喜欢进行演员表转换,而不是手动进行。我最喜欢的是使用glib中的g_newg_new0宏。如果不使用glib,我将添加类似的宏。这些宏在不影响类型安全的情况下减少了代码重复。如果错误输入类型,则将在非void指针之间进行隐式转换,这将导致警告(C ++中的错误)。如果忘记包括定义g_new和的标头,g_new0则会出现错误。g_new并且g_new0都接受相同的参数,不同malloc的是采用的参数少于calloc。只需添加0即可获得零初始化内存。该代码可以使用C ++编译器进行编译而无需更改。


12

仅针对C ++而不是C进行强制转换。如果您使用的是C ++编译器,则最好将其更改为C编译器。


9

void指针背后的概念是可以将其强制转换为任何数据类型,这就是malloc返回void的原因。另外,您必须注意自动类型转换。因此,尽管必须执行强制转换,但这不是强制性的。它有助于保持代码的清洁并有助于调试


11
这不是强制性的-尽管您必须这样做 ”-我认为那里存在矛盾!
Toby Speight 2015年

5
我认为您应该向某人阅读这篇文章,看看他们是否理解您要说的话。然后重写它,使其清楚您想要说的。我真的不明白你的答案是什么。
比尔·伍德杰

9

空指针是通用指针,C支持从空指针类型到其他类型的隐式转换,因此无需显式类型转换。

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


9
  1. 如前所述,C不需要,但C ++不需要。

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

  3. 在C语言中,这是不必要的,因为void *将自动安全地提升为任何其他指针类型。

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

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


7

malloc的强制转换在C中是不必要的,但在C ++中是强制性的。

由于以下原因,无需在C中进行强制转换:

  • void * 对于C,会自动安全地提升为任何其他指针类型。
  • 如果您忘记包含,可能会隐藏错误<stdlib.h>。这可能导致崩溃。
  • 如果指针和整数的大小不同,那么您将通过强制转换隐藏警告,并且可能丢失返回地址的位。
  • 如果指针的类型在其声明处更改,则可能还需要更改malloc调用并强制转换的所有行。

另一方面,强制转换可以增加程序的可移植性。即,它允许C程序或函数作为C ++进行编译。


0

对我而言,这里得出的结论是,malloc完全不需要使用C 进行强制转换,但是如果您强制转换,它不会影响,malloc因为malloc仍然会为您分配您请求的祝福内存空间。另一个原因是人们进行强制转换的原因或原因之一,这是使他们能够使用C或C ++编译同一程序。

可能有其他原因,但几乎可以肯定,其他原因迟早会给您带来严重麻烦。


0

可以,但是不需要强制转换为C。如果该代码被编译为C ++,则必须强制转换。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.