我inputmalloc的结果吗?
在这个问题中 ,有人在评论中提出,我不应该把malloc
的结果,即
int *sieve = malloc(sizeof(int) * length);
而不是:
int *sieve = (int *) malloc(sizeof(int) * length);
为什么会这样呢?
不 你不会投出结果,因为:
- 这是没有必要的,因为在这种情况下
void *
被自动安全地提升为任何其他的指针types。 - 它增加了代码的混乱,转换不太容易阅读(特别是如果指针types很长)。
- 它让你重复自己,这通常是不好的。
- 如果您忘记包含
<stdlib.h>
它可以隐藏一个错误。 这可能会导致崩溃(或更糟糕的是,直到稍后在代码的一些完全不同的部分) 才会导致崩溃。 考虑一下如果指针和整数的大小不同,会发生什么? 那么你通过投射隐藏了一个警告,并可能会丢失你的返回地址的位。 注意:从C11开始,隐式函数从C中消失了,这一点不再相关,因为没有自动假设未声明的函数返回int
。
作为澄清,请注意,我说“你不投”,而不是“你不需要投”。 在我看来,即使你是对的,也不能包括演员。 这样做没有任何好处,但一堆潜在的风险,包括演员表明你不知道风险。
另外请注意,正如评论员所指出的那样,上面谈到的是直C,而不是C ++。 我非常坚信C和C ++是独立的语言。
要进一步添加,您的代码不必要地重复可能导致错误的types信息( int
)。 取消引用用于存储返回值的指针会更好:将两者“locking”在一起:
int *sieve = malloc(length * sizeof *sieve);
这也将length
移动到前面以增加可视性,并且用sizeof
删除多余的括号。 只有当参数是一个types名时才需要它们。 许多人似乎不知道(或忽略)这使得他们的代码更加冗长。 记住: sizeof
不是一个函数! 🙂
虽然在一些罕见的情况下将length
移到前面可能会提高可视性,但是还应该注意的是,在一般情况下,最好将expression式写成:
int *sieve = malloc(sizeof *sieve * length);
由于首先保持sizeof
,在这种情况下,确保乘法至less用size_t
math。
比较: malloc(sizeof *sieve * length * width)
与malloc(length * width * sizeof *sieve)
第二个可能会溢出length * width
当width
和length
是小于size_t
types。
在C中,你不需要malloc
的返回值。 malloc
返回的void指针被自动转换为正确的types。 但是,如果您希望使用C ++编译器编译代码,则需要强制转换。 社区中的首选方法是使用以下方法:
int *sieve = malloc(sizeof *sieve * length);
如果您改变了sieve
的types, sieve
使您不必担心改变expression式的右边。
正如人们指出的那样,演员阵容不好。 特别指针转换。
你投了,因为:
- 它使得你的代码更易于在C和C ++之间移植 ,正如经验显示的那样,许多程序员声称他们用C ++(或C +本地编译器扩展)编写的时候用C编写的。
- 不这样做可以隐藏一个错误 :注意所有的SO例子混淆了什么时候写
type *
与type **
。 - 这个让你不注意你的想法没有
#include
一个合适的头文件,错过了森林的树木 。 这就像说“不要担心你没有要求编译器抱怨没有看到原型 – 讨厌的stdlib.h是真正重要的事情要记住! - 这迫使一个额外的认知交叉检查 。 它将(所谓的)所需的types放在您正在为该variables的原始大小进行的算术旁边。 我敢打赌,你可以做一个SO研究,表明
malloc()
错误被捕捉的时候,有一个演员。 与断言一样,揭示意图的注释可以减less错误。 - 以机器可以检查的方式重复自己往往是一个好主意。 事实上,这就是一个断言,而这种使用是一个断言。 自从图灵在多年前提出这个想法以来,断言仍然是我们获得代码正确性的最通用技术。
正如其他人所说的,C不是必需的,而是C ++。 如果你认为你要用C ++编译器编译你的C代码,出于这个原因,你可以使用一个macros来代替:
#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 ++。
在C中,你可以隐式地将一个void指针转换为任何其他types的指针,所以不需要强制转换。 用一个人可能会build议不经意的观察者,为什么需要一个人有一些原因,这可能是误导性的。
在C中,你会得到一个从void*
到任何其他(数据)指针的隐式转换。
您不会投出malloc的结果,因为这样做会给代码添加无意义的混乱。
为什么人们使用malloc的结果最常见的原因是他们不确定C语言是如何工作的。 这是一个警告信号:如果你不知道一个特定的语言机制如何工作,那么不要猜测。 查看它或询问Stack Overflow。
一些评论:
-
一个void指针可以转换为任何其他指针types,而不需要显式转换(C11 6.3.2.3)。
-
然而,C ++将不允许在
void*
和另一个指针types之间进行隐式转换。 所以在C ++中,演员们将是正确的。 但是如果你用C ++编程,你应该使用new
而不是malloc()。 而且你不应该使用C ++编译器来编译C代码。如果您需要使用相同的源代码支持C和C ++,请使用编译器开关标记差异。 不要试图用相同的代码来expression两种语言标准,因为它们不兼容。
-
如果C编译器因为忘记包含头文件而无法find某个函数,则会得到一个有关该编译器/链接器的错误。 所以,如果你忘记了包含
<stdlib.h>
,那么你将无法构build你的程序。 -
在遵循超过25年的标准版本的古代编译器上,忘记包含
<stdlib.h>
会导致危险的行为。 因为在那古老的标准中,没有可见原型的函数将返回types隐式地转换为int
。 然后从malloc中明确输出结果会隐藏这个错误。但这真的不是问题。 你没有使用一台25年的电脑,那么你为什么要使用一个25年的编译器呢?
从维基百科
铸造的优点
包括转换可能允许C程序或函数编译为C ++。
该转换允许1989年以前版本的malloc最初返回一个char *。
如果指针远离malloc()调用(尽pipe现代编译器和静态分析器可以在不需要强制转换的情况下对此类行为发出警告),Casting可以帮助开发人员确定types调整中的不一致性。
铸造的缺点
根据ANSI C标准,演员阵容是多余的。
添加转换可能会掩盖未能包含头文件stdlib.h ,在其中findmalloc的原型。 在没有malloc原型的情况下,标准要求C编译器假定malloc返回一个int。 如果没有强制转换,则将该整数分配给指针时发出警告; 然而,与演员,这个警告不产生,隐藏一个错误。 在某些体系结构和数据模型(如64位系统上的LP64,其中long和指针是64位,int是32位)上,这个错误实际上可能导致未定义的行为,因为隐式声明的malloc返回一个32-位值,而实际定义的函数返回一个64位的值。 根据调用约定和内存布局,这可能导致堆栈粉碎。 在现代编译器中,这个问题不太可能被忽视,因为它们一致地发出警告,说明已经使用了未声明的函数,所以仍然会出现警告。 例如,GCC的默认行为是显示一个警告,该警告读取“不兼容的隐含声明的内置函数”,无论是否存在转换。
如果指针的types在其声明中发生了变化,则可能还需要更改所有调用malloc的行。
尽pipemalloc没有被强制转换是最好的方法,大多数有经验的程序员都会select它 ,但是你应该使用你喜欢的任何一个来解决这个问题。
即:如果您需要将C程序编译为C ++(尽pipe这些是独立的语言),您应该使用malloc
和cast。
现在没有必要使用malloc()
返回的值,但是我想添加一个似乎没有人指出的点:
在古代,也就是说,在ANSI C提供void *
作为指针的genericstypes之前, char *
就是这种用法的types。 在这种情况下,转换可以closures编译器警告。
参考: C常见问题
不强制转换malloc
的结果,因为它返回void*
, void*
可以指向任何数据types。
加上我的经验,学习计算机工程学,我发现我看过的两三位教授总是给我写一个malloc,但是我问了一个问题(对C有一个巨大的简历和理解),告诉我这绝对没有必要,只是绝对的具体,让学生陷入绝对具体的心态。 本质上来说,施放不会改变它的工作方式,它完全按照它所说的,分配内存,并且不会影响它,获得相同的内存,即使你错误地将其转换到其他东西(并且以某种方式逃避编译器错误)C将以相同的方式访问它。
编辑:铸造有一定的意义。 当你使用数组符号的时候,生成的代码必须知道有多less内存空间需要前进才能到达下一个元素的开始,这是通过强制转换来实现的。 这样你就知道对于一个double来说,前进8个字节,而对于int,则去4,等等。 因此,如果使用指针表示法,则它不起作用。
返回的types是void *,可以将其转换为所需types的数据指针以便可解引用。
void指针是一个generics指针,C支持从void指针types到其他types的隐式转换,所以不需要显式地转换它。
但是,如果您希望在不支持隐式转换的C ++平台上完全兼容相同的代码,则需要执行types转换,所以这一切都取决于可用性。
在这里添加所有的信息; 这是GNU C库参考手册所说的:
您可以将
malloc
的结果存储到任何指针variables中,而不需要强制转换,因为ISO C会在必要时自动将typesvoid *
转换为另一种types的指针。 但是在赋值运算符之外的上下文中必须进行强制转换,或者如果您希望代码运行在传统的C中
事实上, ISO C11标准 (p347)这样说:
如果分配成功,返回的指针被适当地alignment,以便它可以被分配给具有基本alignment要求的任何types的对象的指针,然后用于访问分配的空间中的这样的对象或这样的对象的数组(直到空间被明确地释放)
这取决于编程语言和编译器。 如果在C中使用malloc
,则不需要进行types转换,因为它会自动键入cast,但是如果使用C ++,则应该键入cast,因为malloc
将返回void*
types。
在C语言中,void指针可以分配给任何指针,这就是为什么你不应该使用types转换。 如果你想“types安全”的分配,我可以推荐以下macros函数,我总是在我的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);
对于非dynamic数组,第三个必须具有的函数macros是
#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
这使得数组循环更安全,更方便:
int i, a[100]; for (i = 0; i < LEN(a); i++) { ... }
铸造仅适用于C ++而非C.如果您使用的是C ++编译器,则最好将其更改为C编译器。
我只是为了表示不赞成types系统中的丑陋洞,即使没有使用强制转换来导致错误的转换,它也允许代码(如下面的代码片段)在没有诊断的情况下进行编译:
double d; void *p = &d; int *q = p;
我希望这不存在(而不是在C + +),所以我施放。 它代表了我的口味和我的编程政治。 我不仅投了一个指针,而且还有效地投了一张选票, 赶走了愚蠢的恶魔 。 如果我实在无法摆出愚蠢的话 ,那么至less让我以抗议的姿态expression这样做的意愿吧。
实际上,一个好的做法是用函数返回unsigned char *
来包装malloc
(和朋友),并且基本上不会在代码中使用void *
。 如果你需要一个通用的指向任何对象的指针,使用char *
或unsigned char *
,并且在两个方向都进行强制转换。 可能放纵的一种放松也许是使用像memset
和memcpy
这样的memset
而不是强制转换。
关于转换和C ++兼容性的话题,如果你编写你的代码,以便编译为C和C ++(在这种情况下,你必须将 malloc
的返回值malloc
void *
以外的东西),你可以做对于你自己来说是非常有用的:你可以使用macros进行C ++编译时转换为C ++风格的types转换,但编译为C时转换为Ctypes转换:
/* 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
如果你坚持这些macros,那么对你的代码库进行一个简单的grep
search就可以看到你所有的转换,所以你可以查看它们中的任何一个是不正确的。
接下来,如果您定期使用C ++编译代码,它将强制使用适当的强制转换。 例如,如果您使用strip_qual
只是为了移除一个const
或volatile
,但是程序改变的方式会涉及一个types转换,您将得到一个诊断,并且您将不得不使用转换的组合来获取所需的转换。
为了帮助你坚持这些macros,GNU C ++(不是C!)编译器有一个很漂亮的function:为所有出现的C风格转换生成一个可选的诊断。
-Wold-style-cast(仅适用于C ++和Objective-C ++) 如果使用旧样式(C样式)转换为非空types,则发出警告 在一个C ++程序中。 新风格的演员(dynamic_cast, static_cast,reinterpret_cast和const_cast)不那么脆弱 以意想不到的效果,更容易search。
如果C代码编译为C ++,那么可以使用-Wold-style-cast
选项来查找所有可能embedded代码中的(type)
强制转换语法,并通过将其replace为适当的从上面的macros中select(如果需要的话可以组合)。
这种转换处理是在“Clean C”中工作的最大独立技术理由:C和C ++方言的组合,从而在技术上certificate转换malloc
的返回值是正确的。
void指针背后的概念是它可以被转换成任何数据types,这就是为什么malloc返回void的原因。 你也一定知道自动types转换。 所以尽pipe你必须这样做,但并不是强制性的。 它有助于保持代码清洁,并有助于debugging
People used to GCC and Clang are spoiled. It's not all that good out there.
I have been pretty horrified over the years by the staggeringly aged compilers I've been required to use. Often companies and managers adopt an ultra-conservative approach to changing compilers and will not even test if a new compiler ( with better standards compliance and code optimization ) will work in their system. The practical reality for working developers is that when you're coding you need to cover your bases and, unfortunately, casting mallocs is a good habit if you cannot control what compiler may be applied to your code.
I would also suggest that many organizations apply a coding standard of their own and that that should be the method people follow if it is defined. In the absence of explicit guidance I tend to go for most likely to compile everywhere, rather than slavish adherence to a standard.
The argument that it's not necessary under current standards is quite valid. But that argument omits the practicalities of the real world. We do not code in a world ruled exclusively by the standard of the day, but by the practicalities of what I like to call "local management's reality field". And that's bent and twisted more than space time ever was. 🙂
因人而异。
I tend to think of casting malloc as a defensive operation. Not pretty, not perfect, but generally safe. ( Honestly, if you've not included stdlib.h then you've way more problems than casting malloc ! ).
The best thing to do when programming in C whenever it is possible:
- Make your program compile through a C compiler with all warnings turned on
-Wall
and fix all errors and warnings - Make sure there are no variables declared as
auto
- Then compile it using a C++ compiler with
-Wall
and-std=c++11
. Fix all errors and warnings. - Now compile using the C compiler again. Your program should now compile without any warning and contain fewer bugs.
This procedure lets you take advantage of C++ strict type checking, thus reducing the number of bugs. In particular, this procedure forces you to include stdlib.h
or you will get
malloc
was not declared within this scope
and also forces you to cast the result of malloc
or you will get
invalid conversion from
void*
toT*
or what ever your target type is.
The only benefits from writing in C instead of C++ I can find are
- C has a well specified ABI
- C++ may generate more code [exceptions, RTTI, templates, runtime polymorphism]
Notice that the second cons should in the ideal case disappear when using the subset common to C together with the static polymorphic feature.
For those that finds C++ strict rules inconvenient, we can use the C++11 feature with inferred type
auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
I prefer to do the cast, but not manually. My favorite is using g_new
and g_new0
macros from glib. If glib is not used, I would add similar macros. Those macros reduce code duplication without compromising type safety. If you get the type wrong, you would get an implicit cast between non-void pointers, which would cause a warning (error in C++). If you forget to include the header that defines g_new
and g_new0
, you would get an error. g_new
and g_new0
both take the same arguments, unlike malloc
that takes less arguments than calloc
. Just add 0
to get zero-initialized memory. The code can be compiled with a C++ compiler without changes.
A void pointer is a generic pointer and C supports implicit conversion from a void pointer type to other types, so there is no need of explicitly typecasting it.
However, if you want the same code work perfectly compatible on a C++ platform, which does not support implicit conversion, you need to do the typecasting, so it all depends on usability.
-
As other stated, it is not needed for C, but for C++.
-
Including the cast may allow a C program or function to compile as C++.
-
In C it is unnecessary, as void * is automatically and safely promoted to any other pointer type.
-
But if you cast then, it can hide an error if you forgot to include stdlib.h . This can cause crashes (or, worse, not cause a crash until way later in some totally different part of the code).
Because stdlib.h contains the prototype for malloc is found. In the absence of a prototype for malloc, the standard requires that the C compiler assumes 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.
The casting of malloc is unnecessary in C but mandatory in C++.
- Casting is unnecessary in C because of void * is automatically and safely promoted to any other pointer type in this case.
- It can hide an error if you forgot to include
<stdlib.h>
. This can cause crashes. - If pointers and integers are differently sized, then you're hiding a warning by casting and might lose bits of your returned address.
No, you don't cast the result of malloc()
.
In general, you don't cast to or from void *
.
A typical reason given for not doing so is that failure to #include <stdlib.h>
could go unnoticed. This isn't an issue anymore for a long time now as C99 made implicit function declarations illegal, so if your compiler conforms to at least C99, you will get a diagnostic message.
But there's a much stronger reason not to introduce unnecessary pointer casts:
In C, a pointer cast is almost always an error . This is because of the following rule ( §6.5 p7 in N1570, the latest draft for 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.
This is also known as the strict aliasing rule . So the following code is undefined behavior :
long x = 5; double *p = (double *)&x; double y = *p;
And, sometimes surprisingly, the following is as well:
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;
Sometimes, you do need to cast pointers, but given the strict aliasing rule , you have to be very careful with it. So, any occurrence of a pointer cast in your code is a place you have to double-check for its validity . Therefore, you never write an unnecessary pointer cast.
TL;博士
In a nutshell: Because in C, any occurrence of a pointer cast should raise a red flag for code requiring special attention, you should never write unnecessary pointer casts.
Side notes:
-
There are cases where you actually need a cast to
void *
, eg if you want to print a pointer:int x = 5; printf("%p\n", (void *)&x);
The cast is necessary here, because
printf()
is a variadic function, so implicit conversions don't work. -
In C++, the situation is different. Casting pointer types is somewhat common (and correct) when dealing with objects of derived classes. Therefore, it makes sense that in C++, the conversion to and from
void *
is not implicit. C++ has a whole set of different flavors of casting.
Please do yourself a favor and more importantly a favor for the next person who will maintain your code, and provide as much information as possible about the data type of a program's variables.
Thus, cast
the returned pointer from malloc
. In the following code the compiler can be assured that sieve is in fact being assigned a point to an integer(s).
int *sieve = (int *) malloc(sizeof(int) * length);
This reduces the chance for a human error when/if the data type for sieve is changed.
I would be interested in knowing if there are any "pure" C compilers that would flag this statement as being in error. If so, let me know, so that I can avoid them as their lack of type checking will increase the overall expense of maintaining software.