在this question中,有人在comment中建议我 不 投放malloc
的结果,即
int *sieve = malloc(sizeof(int) * length);
而不是:
int *sieve = (int *) malloc(sizeof(int) * length);
为什么会出现这种情况?
答案 0 :(得分:2070)
否强>;你不投射结果,因为:
void *
会自动安全地提升为任何其他指针类型。<stdlib.h>
,则可以隐藏错误。这可能会导致崩溃(或者更糟糕的是,不导致崩溃,直到稍后在代码的某些完全不同的部分中)。考虑如果指针和整数的大小不同会发生什么;那么你通过强制转换隐藏了一个警告,可能会丢失你返回的地址。注意:从C11开始,隐式函数从C中消失,并且这一点不再相关,因为没有自动假设未声明的函数返回int
。作为澄清,请注意我说“你不投”,而不是“你不需要投下”。在我看来,即使你做对了也没有包括演员。这样做没有任何好处,但是一堆潜在的风险,包括演员表明你不知道风险。
另请注意,正如评论员指出的那样,上面谈的是直C,而不是C ++。我非常坚信C和C ++是不同的语言。
要进一步添加,您的代码会不必要地重复可能导致错误的类型信息(int
)。最好取消引用用于存储返回值的指针,将两者“锁定”在一起:
int *sieve = malloc(length * sizeof *sieve);
这也会将length
移到前面以提高可见度,并使用sizeof
删除多余的括号;当参数是类型名称时,只需要 。许多人似乎不知道(或忽略)这一点,这使得他们的代码更加冗长。请记住:sizeof
不是函数! :)
虽然在某些极少数情况下将length
移到前面可能可以提高可见性,但是在一般情况下,应该注意将表达式编写为:< / p>
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
。< / p>
答案 1 :(得分:350)
在C中,您不需要转换malloc
的返回值。 malloc
返回的void指针自动转换为正确的类型。但是,如果您希望使用C ++编译器编译代码,则需要进行强制转换。社区中的首选替代方案是使用以下内容:
int *sieve = malloc(sizeof *sieve * length);
除了更改sieve
的类型外,还可以让您不必担心更改表达式的右侧。
答案 2 :(得分:324)
您执行强制转换,因为:
type *
与type **
时混淆的SO示例。#include
一个合适的头文件而忽略了树木的森林。这就像说“不要担心你没有要求编译器抱怨没有看到原型 - 这个讨厌的stdlib.h是真正重要的事情要记住!”malloc()
错误被抓得更快。与断言一样,显示意图的注释可以减少错误。 答案 3 :(得分:159)
正如其他人所说的那样,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 ++编译。
答案 4 :(得分:119)
投射的优势
包含强制转换可能允许C程序或函数以C ++编译。
演员阵容允许最初返回char *的1989年之前版本的malloc。
如果目标指针类型发生变化,Casting可以帮助开发人员识别类型大小调整的不一致性,特别是如果指针远离malloc()调用(尽管现代编译器和静态分析器可以警告这种行为而不需要)演员)。
投射的缺点
根据ANSI C标准,演员阵容是多余的。
添加强制转换可能会掩盖无法包含标题 stdlib.h , 找到了malloc的原型。没有了 对于malloc的原型,标准要求C编译器 假设malloc返回一个int。如果没有演员表,则会发出警告 将此整数赋给指针时发出;然而,有了 演员,这个警告没有产生,隐藏了一个bug。一定的 体系结构和数据模型(例如64位系统上的LP64,其中 long和指针是64位,int是32位),这个错误可以 实际上会导致未定义的行为,如隐式声明的那样 malloc返回32位值,而实际定义的函数 返回64位值。取决于调用约定和内存 布局,这可能会导致堆栈粉碎。这个问题不太可能发生 在现代编译器中被忽视,因为它们统一产生 警告已使用未声明的功能,因此会发出警告 仍然出现。例如,GCC的默认行为是显示a 读取&#34;不兼容的内置隐式声明的警告 功能&#34;无论演员阵容是否存在。
如果指针的类型在其声明处被更改,则可以 另外,需要更改调用malloc和cast的所有行。
虽然没有强制转换的malloc是首选方法,而且大多数有经验的程序员选择,但您应该使用您想知道的问题。
即:如果您需要将C程序编译为C ++(尽管这些是单独的语言),您应该使用malloc
进行强制转换。
答案 5 :(得分:98)
在C中,您可以隐式地将void指针转换为任何其他类型的指针,因此不需要强制转换。使用一个可能会向不经意的观察者建议,有一些理由需要一个,这可能会产生误导。
答案 6 :(得分:90)
你没有投射malloc的结果,因为这样做会给你的代码带来无意义的混乱。
人们投射malloc结果的最常见原因是因为他们不确定C语言的工作原理。这是一个警示标志:如果您不知道特定语言机制的工作原理,那么不要进行猜测。查找或询问Stack Overflow。
一些意见:
无需显式转换(C11 6.3.2.3和6.5.16.1)即可将void指针转换为任何其他指针类型/从其中转换。
但是,C ++不允许在void*
和另一个指针类型之间进行隐式转换。所以在C ++中,演员阵容是正确的。但是如果你用C ++编程,你应该使用new
而不是malloc()。而且你永远不应该使用C ++编译器编译C代码。
如果需要使用相同的源代码支持C和C ++,请使用编译器开关标记差异。不要尝试使用相同的代码来区分两种语言标准,因为它们不兼容。
如果C编译器由于忘记包含标题而无法找到函数,则会出现编译器/链接器错误。因此,如果您忘记包含<stdlib.h>
没有大事,那么您将无法构建您的计划。
对于遵循25年以上标准版本的古代编译器,忘记包含<stdlib.h>
会导致危险的行为。因为在那个古老的标准中,没有可见原型的函数隐式地将返回类型转换为int
。然后显式地从malloc中转换结果将隐藏这个错误。
但这确实不是问题。你没有使用25年的计算机,为什么你会使用25年的编译器呢?
答案 7 :(得分:85)
在C语言中,您将获得从void*
到任何其他(数据)指针的隐式转换。
答案 8 :(得分:66)
现在没有必要转换malloc()
返回的值,但我想补充一点,似乎没有人指出:
在古代,也就是说,在 ANSI C 提供void *
作为通用类型的指针之前,char *
是此类用法的类型。在这种情况下,强制转换可以关闭编译器警告。
参考:C FAQ
答案 9 :(得分:49)
不必强制转换malloc
的结果,因为它返回void*
,并且void*
可以指向任何数据类型。
答案 10 :(得分:48)
只是添加我的经验,学习计算机工程我看到我在C中写过的两三位教授总是投写malloc,但是我问的那个(有着巨大的简历和对C的理解)告诉我它是绝对不必要,但过去只是绝对具体,并让学生进入绝对具体的心态。基本上,转换不会改变它的工作方式,它完全按照它所说的方式,分配内存,而且转换不会影响它,你得到相同的内存,即使你错误地将它转换为别的东西(并以某种方式规避编译器)错误)C将以相同的方式访问它。
编辑:投射有一定的意义。当您使用数组表示法时,生成的代码必须知道它必须前进多少内存位置才能到达下一个元素的开头,这是通过强制转换实现的。通过这种方式,你知道对于一个双倍,你前进8个字节而对于一个int你去4个,依此类推。因此,如果使用指针表示法则无效,在数组表示法中则必须使用。
答案 11 :(得分:30)
这是The GNU C Library Reference手册所说的:
您可以将
malloc
的结果存储到任何没有a的指针变量中 强制转换,因为ISO C会自动将类型void *
转换为另一个类型 必要时指针的类型。但演员阵容在上下文中是必要的 除了赋值运算符或者您可能希望运行代码 在传统的C中。
事实上ISO C11 standard(p347)是这样说的:
如果分配成功,则返回指针 它可以被分配给指向任何类型的对象的指针 基本对齐要求,然后用于访问这样的 对象或分配的空间中的此类对象的数组(直到 空间明确解除分配)
答案 12 :(得分:29)
void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地对其进行类型转换。
但是,如果您希望相同的代码在C ++平台上完全兼容(不支持隐式转换),则需要进行类型转换,因此这一切都取决于可用性。
答案 13 :(得分:28)
返回的类型为void *,可以转换为所需类型的数据指针,以便可以解除引用。
答案 14 :(得分:26)
在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++) {
...
}
答案 15 :(得分:25)
这取决于编程语言和编译器。如果你在C中使用malloc
,则不需要输入强制转换,因为它会自动输出强制转换,但是如果你使用C ++,那么你应该输入强制转换,因为malloc
将返回void*
类型。
答案 16 :(得分:15)
过去GCC和Clang的人都被宠坏了。它并不是那么好。
多年来,我一直被我需要使用的令人咋舌的老化编译器吓坏了。公司和经理通常会采用极端保守的方法来更改编译器,如果新的编译器(具有更好的标准兼容性和代码优化)在他们的系统中工作,他们甚至不会测试。工作开发人员的实际情况是,当您进行编码时,您需要覆盖您的基础,不幸的是,如果您无法控制可能对您的代码应用的编译器,那么铸造mallocs是一个好习惯。
我还建议许多组织应用自己的编码标准, 在没有明确指导的情况下,我倾向于最有可能在任何地方进行编译,而不是盲目遵守标准。
根据现行标准,不必要的论点是非常有效的。但是这个论点忽略了现实世界的实用性。我们不会在一个完全由当时标准统治的世界中编码,而是根据我喜欢称之为“本地管理的现实领域”的实际情况进行编码。而且,它比空间时间更加弯曲和扭曲。 : - )
因人而异。
我倾向于将malloc视为防御性操作。不漂亮,不完美,但一般都很安全。 (老实说,如果你还没有包括stdlib.h,那么你比其他方式更多的问题而不是施放malloc!)。
答案 17 :(得分:12)
尽可能在C语言编程时最好的事情:
-Wall
并修复所有错误和警告auto
-Wall
和-std=c++11
的C ++编译器进行编译。修复所有错误和警告。此过程允许您利用C ++严格类型检查,从而减少错误数量。特别是,此程序会强制您包含stdlib.h
或者您将获得
malloc
未在此范围内宣布
并且还会强制您投射malloc
的结果,否则您将获得
从
的无效转换void*
到T*
或者你的目标类型是什么。
我可以找到用C而不是C ++编写的唯一好处是
请注意,当使用C的公共子集和 static 多态特征时,理想情况下的第二个缺点应该消失。
对于那些发现C ++严格规则不方便的人,我们可以使用推断类型的C ++ 11特性
auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
答案 18 :(得分:12)
我放入演员只是为了表示不赞成类型系统中的丑陋漏洞,这允许在没有诊断的情况下编译下面的代码片段,即使没有使用强制转换来导致错误的转换:
double d;
void *p = &d;
int *q = p;
我希望它不存在(并且它不会在C ++中存在),所以我投了。它代表了我的品味和我的编程政治。我不仅要投掷指针,还要有效地投票,casting out demons of stupidity。如果我不能实际 cast out stupidity,那么至少让我以抗议的姿态表达希望这样做。
事实上,一个好的做法是将malloc
(和朋友)包含在返回unsigned char *
的函数中,并且基本上永远不会在代码中使用void *
。如果您需要通用指针指向任意对象,请使用char *
或unsigned char *
,并在两个方向上进行转换。可能放纵的一种放松方式是使用memset
和memcpy
等函数而不使用强制转换。
关于转换和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)
转换语法,并通过替换来跟进这些诊断从上述宏中选择一个适当的选择(或必要时组合)。
这种对转换的处理是使用&#34; Clean C&#34;:C和C ++组合方言的单一最大独立技术理由,这反过来在技术上证明了转换malloc
的返回值
答案 19 :(得分:11)
我更喜欢演员,但不是手动。我最喜欢的是使用glib中的g_new
和g_new0
个宏。如果没有使用glib,我会添加类似的宏。这些宏减少了代码重复,而不会影响类型安全性。如果你得到错误的类型,你会得到非void指针之间的隐式转换,这会引起警告(C ++中的错误)。如果您忘记包含定义g_new
和g_new0
的标头,则会出现错误。 g_new
和g_new0
都使用相同的参数,而malloc
的参数少于calloc
。只需添加0
即可获得零初始化内存。代码可以使用C ++编译器进行编译而无需更改。
答案 20 :(得分: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;
有时候,你做需要转换指针,但鉴于严格别名规则,你必须非常小心。因此,在您的代码中出现任何指针都是必须仔细检查其有效性的地方。因此,您永远不会编写不必要的指针。
简而言之:因为在C中,任何出现的指针转换应该为需要特别注意的代码引发一个红色标记,所以你永远不应该写不必要的指针演员。
附注:
在某些情况下,您实际上需要转换为void *
,例如如果你想打印一个指针:
int x = 5;
printf("%p\n", (void *)&x);
此处需要强制转换,因为printf()
是一个可变函数,因此隐式转换不起作用。
在C ++中,情况有所不同。在处理派生类的对象时,转换指针类型有点常见(也是正确的)。因此,在C ++中,void *
之间的转换是不是是隐式的,这是有道理的。 C ++有一整套不同的演员风格。
答案 21 :(得分:9)
void指针背后的概念是它可以转换为任何数据类型,这就是malloc返回void的原因。您还必须了解自动类型转换。因此,必须执行此操作并不强制转换指针。它有助于保持代码清洁并有助于调试
答案 22 :(得分:6)
正如其他所说,C不需要,但C ++不需要。
包含强制转换可能允许C程序或函数以C ++编译。
在C中没有必要,因为void *会自动安全地提升为任何其他指针类型。
但是如果你投射,那么如果忘记包含它,它可以隐藏错误 的 stdlib.h中即可。这可能会导致崩溃(或者更糟糕的是,不会导致崩溃 直到稍后在代码的一些完全不同的部分)。
因为 stdlib.h 包含找到malloc的原型。在里面 没有malloc的原型,标准要求C 编译器假定malloc返回一个int。如果没有演员,a 将此整数分配给指针时发出警告; 然而,对于演员表,这个警告不会产生,隐藏了一个错误。
答案 23 :(得分:6)
void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地对其进行类型转换。
但是,如果您希望相同的代码在C ++平台上完全兼容(不支持隐式转换),则需要进行类型转换,因此这一切都取决于可用性。
答案 24 :(得分:5)
有时我会注意到这样的评论:
或
有关OP使用强制转换的问题。评论本身包含指向该问题的超链接。
这在任何可能的方式上都是不恰当和不正确的。当它确实是一个人自己的编码样式时,没有对与错。
它基于两个原因:
这个问题确实是基于观点的。从技术上讲,这个问题应该早在几年前就已经结束了。 “ 我”或“ 不是我”还是等效的“ 我应该”或“ 我应该”问题,如果没有自己的见解,您就无法集中注意力。结束问题的原因之一是因为它“可能导致基于意见的答案”,如此处所示。
许多答案(包括answer中最明显和被接受的@unwind)完全或几乎完全基于意见(例如会添加到您的代码中的神秘“杂物”)如果您进行强制转换或重复自己会很糟糕),并且表现出明显且专注的趋势来忽略强制转换。他们一方面争论演员表的冗余,但更糟糕的是,他们试图解决由程序本身的错误/失败引起的错误-如果要使用#include <stdlib.h>
,则不要malloc()
。 / p>
我想用一些我个人的观点减少对所讨论观点的真实了解。需要特别注意以下几点:
这样一个很容易被人接受的问题需要以中立的利弊来回答。不仅利弊。
此答案列出了优缺点的概述:
https://stackoverflow.com/a/33047365/12139179
(由于这个原因,我个人认为这是迄今为止最好的答案。)
最多会导致忽略强制转换的一个原因是强制转换可能会隐藏错误。
如果有人使用隐式声明的malloc()
返回int
(自C99以来隐式函数已从标准中删除)和sizeof(int) != sizeof(int*)
,如本问题所示
Why does this code segfault on 64-bit architecture but work fine on 32-bit?
演员表会隐藏一个错误。
虽然这是事实,但只显示了一半的故事,因为省略演员表只会是对更大错误的向前解决方案-使用stdlib.h
时不包括malloc()
。
如果您愿意,这将永远不是一个严重的问题
使用兼容C99或更高版本的编译器(建议并且必须是强制性的),并且
当您想在代码中使用stdlib.h
时,并不会忘记忘记包含malloc()
,这本身就是一个巨大的错误。
有人认为C代码符合C ++,因为强制转换必须使用C ++。
首先要说的是:用C ++编译器编译C代码不是一个好习惯。
C和C ++实际上是两种完全不同的语言,具有不同的语义。
但是,如果您确实希望/需要使C代码与C ++兼容,反之亦然,请使用编译器开关,而不要使用任何强制转换。
由于强制转换有被宣布为多余甚至有害的趋势,因此我想重点关注这些问题,这些问题给出了为什么强制转换有用或必要的充分理由:
事实是,如果分配的指针指向基本对齐要求的对象(包括所有对象中的大多数),则强制转换对于C标准(自ANSI-C(C89 / C90)起)是多余的。 / p>
在这种情况下,由于指针会自动对齐,因此您无需进行强制转换:
“未指定通过连续调用aligned_alloc,calloc,malloc和realloc函数分配的存储的顺序和连续性。如果分配成功,返回的指针将适当对齐,以便可以将其分配给指针到具有基本对齐要求的任何类型的对象,然后用于在分配的空间中访问此类对象或此类对象的数组(直到明确释放该空间)。“
来源:C18,§7.22.3/ 1
“ 基本对齐是小于或等于
_Alignof (max_align_t)
的有效对齐。对于所有存储持续时间的对象,实现应支持基本对齐。以下对齐要求类型应为基本对齐方式:-所有基本,限定或不限定的基本类型;
-所有原子,限定或不限定的枚举类型;
-所有原子,合格或不合格的指针类型;
-元素类型具有基本对齐要求的所有数组类型; 57)
-第7条中指定为完整对象类型的所有类型;
-所有结构或联合类型,其所有元素均具有基本对齐要求的类型,并且所有元素均未具有指定非基本对齐方式的对齐方式的对齐说明符。
- 按照6.2.1的规定,后面的声明可能会隐藏前面的声明。”
来源:C18,第6.2.8 / 2条
但是,如果您为扩展对齐要求的实现定义的对象分配内存,则需要进行强制转换。
扩展的比对由大于
_Alignof (max_align_t)
的比对表示。由实现定义,是否支持任何扩展的比对及其支持的存储期限。具有扩展对齐要求的类型是过度对齐类型。58)来源。 C18,§6.2.8/ 3
其他所有事情都取决于特定用例和个人看法。
请注意如何进行自我教育。
我建议您先仔细阅读到目前为止的所有答案(以及可能会导致失败的评论),然后再根据自己的想法发表自己的看法(如果您决定是否发布) malloc()
在特定情况下的结果。
请注意:
该问题没有对与错的答案。这是一个风格问题,您可以自己决定选择哪种方式(如果您当然不受教育或工作所迫)。请注意这一点,不要欺骗您。
最后一点:我最近投票决定以基于观点的观点结束这个问题,多年来确实需要这样做。如果您拥有关闭/重新打开权限,我也想邀请您这样做。
答案 25 :(得分:3)
在C中不需要转换malloc,但在C ++中是必需的。
由于以下原因,C中不需要进行强制转换:
void *
会自动安全地提升为任何其他指针类型
<stdlib.h>
,则可以隐藏错误。这可能会导致崩溃。malloc
并投射的所有行。另一方面,强制转换可能会增加程序的可移植性。即,它允许C程序或函数编译为C ++。
答案 26 :(得分:0)
对我来说,这里的结论是完全不需要在C中强制转换malloc
,但是如果您强制转换,它将不会影响malloc
,因为malloc
仍会分配给您所请求的祝福内存空间。
另一个原因是人们进行强制转换的原因或原因之一,这是使他们能够使用C或C ++编译同一程序。
可能还有其他原因,但几乎可以肯定,其他原因迟早会给您带来严重麻烦。
答案 27 :(得分:0)
您可以但不必强制转换为C。如果该代码被编译为C ++,则必须强制转换。