在这个问题,有人建议意见,我应该不会投的结果malloc
,即
int *sieve = malloc(sizeof(int) * length);
而不是:
int *sieve = (int *) malloc(sizeof(int) * length);
为什么会这样呢?
无 ; 你没有投射结果,因为:
这是不必要的,因为void *
在这种情况下自动且安全地提升到任何其他指针类型.
它增加了代码的混乱,演员表不是很容易阅读(特别是如果指针类型很长).
它让你重复自己,这通常很糟糕.
如果您忘记包含,它可以隐藏错误
.这可能会导致崩溃(或更糟的是,不导致崩溃,直到方式在后面的代码的一些完全不同的部分).考虑如果指针和整数的大小不同会发生什么; 那么你通过强制转换隐藏了一个警告,可能会丢失你返回的地址.注意:从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
.
在C中,您不需要转换返回值malloc
.返回的void指针malloc
自动转换为正确的类型.但是,如果您希望使用C++编译器编译代码,则需要进行强制转换.社区中的首选替代方案是使用以下内容:
int *sieve = malloc(sizeof *sieve * length);
如果你改变了类型,你还可以免去担心改变表达式的右侧sieve
.
人们已经指出,演员阵容很糟糕.特别是指针转换.
你做演员,因为:
它使您的代码在C和C++之间更具可移植性,并且正如SO经验所示,许多程序员声称他们在用C++(或C加本地编译器扩展)编写时正在用C语言编写.
如果不这样做可以隐藏错误:注意所有的混乱的SO例子,当写type *
对type **
.
它让你不会注意到你没有找到#include
合适的头文件的想法错过了森林的树木.这就像说"不要担心你没有要求编译器抱怨没有看到原型这一事实 - 那令人讨厌的stdlib.h是真正重要的事情要记住!"
它强制进行额外的认知交叉检查.它将(声称的)所需类型放在您正在为该变量的原始大小进行的算术旁边.我敢打赌你可以做一个SO研究,它表明malloc()
当有一个演员时,bug会被抓得更快.与断言一样,显示意图的注释可以减少错误.
以机器可以检查的方式重复自己通常是一个好主意.事实上,这就是一个断言,并且使用强制转换是一种断言.断言仍然是我们获得正确代码的最常用技术,因为图灵在很多年前提出了这个想法.
正如其他人所说,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++编译.
来自维基百科
铸造的优点
包括强制转换可能允许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++(虽然这些是单独的语言),你应该使用malloc
cast.
在C中,您可以隐式地将void指针转换为任何其他类型的指针,因此不需要强制转换.使用一个人可能会向不经意的观察者建议,有一些理由需要一个,这可能会产生误导.
你没有强制转换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编译器由于忘记包含头而无法找到函数,那么您将收到编译器/链接器错误.因此,如果您忘记包含
那些没什么大不了的话,那么您将无法构建您的程序.
对于遵循超过25年的标准版本的古代编译器,忘记包含
将导致危险的行为.因为在那个古老的标准中,没有可见原型的函数隐式地将返回类型转换为int
.然后显式地从malloc转换结果将隐藏此错误.
但这确实不是问题.您没有使用25年的计算机,那么为什么要使用25年的编译器呢?
在C中,您可以从void*
任何其他(数据)指针进行隐式转换.
malloc()
现在没有必要转换返回的值,但我想补充一点,似乎没有人指出:
在古代,也就是说,在ANSI C提供void *
指针的泛型类型之前,char *
就是这种用法的类型.在这种情况下,强制转换可以关闭编译器警告.
参考:C FAQ
malloc
由于它返回void*
,因此不必强制转换结果,并且void*
可以指向任何数据类型.
只是添加我的经验,学习计算机工程我看到我在C中写过的两三位教授总是施放malloc,但是我问过的那个(有着巨大的简历和对C的理解)告诉我这绝对是不必要的但是以前只是绝对具体,并让学生进入绝对具体的心态.基本上,转换不会改变它的工作方式,它完全按照它所说的方式,分配内存,而且转换不会影响它,你得到相同的内存,即使你错误地将它转换为别的东西(并以某种方式规避编译器)错误)C将以相同的方式访问它.
编辑:施法有一定的意义.当您使用数组表示法时,生成的代码必须知道它必须前进多少内存位置才能到达下一个元素的开头,这是通过强制转换实现的.通过这种方式,你知道对于一个双倍,你前进8个字节而对于一个int你去4个,依此类推.因此,如果使用指针表示法则无效,在数组表示法中它变得必要.
void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地对其进行类型转换.
但是,如果您希望相同的代码在C++平台上完全兼容(不支持隐式转换),则需要进行类型转换,因此这一切都取决于可用性.
这就是GNU C Library Reference手册所说的:
您可以将结果存储
malloc
到任何指针变量中而不进行强制转换,因为ISO C会void *
在必要时自动将类型转换为另一种类型的指针.但是,在赋值运算符以外的情况下,或者您可能希望代码在传统C中运行时,强制转换是必需的.
事实上,ISO C11标准(p347)是这样说的:
如果分配成功,则返回指针,以便可以将其分配给指向具有基本对齐要求的任何类型对象的指针,然后用于在分配的空间中访问此类对象或此类对象的数组(直到空间被明确释放)
返回的类型为void*,可以将其强制转换为所需类型的数据指针,以便可以解除引用.
在C语言中,可以将void指针分配给任何指针,这就是您不应该使用类型转换的原因.如果你想要"类型安全"分配,我可以推荐以下宏函数,我总是在我的C项目中使用它们:
#include#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
在C中使用,则不需要输入强制转换,因为它会自动键入强制转换,但是如果你使用C++,那么你应该输入强制转换,因为malloc
它将返回一个void*
类型.
习惯于GCC和Clang的人都被宠坏了.那里并不是那么好.
多年来,我一直被我需要使用的令人咋舌的老化编译器吓坏了.通常,公司和管理人员采用极端保守的方法来更改编译器,甚至不会测试新的编译器(具有更好的标准兼容性和代码优化)是否适用于他们的系统.工作开发人员的实际情况是,当你编写代码时,你需要覆盖你的基础,不幸的是,如果无法控制编译器可能应用于你的代码,那么铸造mallocs是一个好习惯.
我还建议许多组织应用自己的编码标准,这应该是人们在定义时遵循的方法.在没有明确指导的情况下,我倾向于最有可能在任何地方进行编译,而不是盲目遵守标准.
根据现行标准,不必要的论点是非常有效的.但是这个论点忽略了现实世界的实用性.我们不是在一个完全由当时标准统治的世界中编码,而是在我喜欢称之为"本地管理的现实领域"的实际情况中.这比以往任何时候都更加弯曲和扭曲.:-)
因人而异.
我倾向于将malloc视为防御性操作.不漂亮,不完美,但一般都很安全.(老实说,如果你不包含stdlib.h中那么你的方式比铸造的malloc更多的问题!).
我只是为了表示不赞成类型系统中的丑陋洞而放入演员表,这允许在没有诊断的情况下编译下面的代码片段,即使没有使用强制转换来导致错误的转换:
double d; void *p = &d; int *q = p;
我希望这不存在(并且它不在C++中),所以我投了.它代表了我的品味和我的编程政治.我不仅投了一个指针,而且还有效地投了一张选票,然后抛弃了愚蠢的恶魔.如果我实际上 无法摆脱愚蠢,那么至少让我以抗议的姿态表达希望这样做.
事实上,一个好的做法是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(EXPR)) #define convert(TYPE, EXPR) (static_cast (EXPR)) #define coerce(TYPE, EXPR) (reinterpret_cast (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
.
我喜欢做演员,但不是手动.我最喜欢的是使用g_new
并g_new0
从油嘴滑舌宏.如果没有使用glib,我会添加类似的宏.这些宏减少了代码重复,而不会影响类型安全性.如果你得到错误的类型,你会得到非void指针之间的隐式转换,这会引起警告(C++中的错误).如果你忘了,包括定义页眉g_new
和g_new0
,你会得到一个错误.g_new
并且g_new0
两者都采用相同的参数,不像malloc
参数少calloc
.只需添加0
即可获得零初始化内存.代码可以使用C++编译器进行编译而无需更改.
malloc()
.一般来说,你不会投射或投射void *
.
不这样做的一个典型原因是未能#include
被忽视.这已经不再是一个问题,因为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++有一整套不同的演员风格.
只要有可能,在C语言编程时最好的事情是:
使您的程序通过C编译器进行编译,-Wall
并打开所有警告并修复所有错误和警告
确保没有声明变量 auto
然后用-Wall
和C++编译器编译它-std=c++11
.修复所有错误和警告.
现在再次使用C编译器进行编译.您的程序现在应该在没有任何警告的情况下编译并包含更少的错
此过程允许您利用C++严格类型检查,从而减少错误的数量.特别是,此程序会强制您包含stdlib.h
或者您将获得
malloc
未在此范围内声明
并且还强迫你施放结果malloc
或者你会得到
无效转换
void*
为T*
或者你的目标类型是什么.
使用C语言而不是C++编写的唯一好处是我能找到的
C具有明确指定的ABI
C++可能会生成更多代码[异常,RTTI,模板,运行时多态]
请注意,当使用C的公共子集和静态多态特征时,理想情况下的第二个缺点应该消失.
对于那些发现C++严格规则不方便的人,我们可以使用推断类型的C++ 11特性
auto memblock=static_cast(malloc(n*sizeof(T))); //Mult may overflow...
转换仅适用于C++而不是C.如果您使用的是C++编译器,最好将其更改为C编译器.
void指针背后的概念是它可以转换为任何数据类型,这就是malloc返回void的原因.您还必须了解自动类型转换.因此,必须执行此操作并不强制转换指针.它有助于保持代码清洁并有助于调试
void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地对其进行类型转换.
但是,如果您希望相同的代码在C++平台上完全兼容(不支持隐式转换),则需要进行类型转换,因此这一切都取决于可用性.
正如其他人所说,C不是必需的,而是C++.
包括强制转换可能允许C程序或函数编译为C++.
在C中它是不必要的,因为void*被自动且安全地提升到任何其他指针类型.
但是如果你强制转换,那么如果忘记包含stdlib.h,它就会隐藏错误 .这可能会导致崩溃(或者更糟糕的是,直到稍后在代码的某些完全不同的部分中才会导致崩溃).
因为stdlib.h包含找到malloc的原型.在没有malloc原型的情况下,该标准要求C编译器假定malloc返回一个int.如果没有强制转换,则在将此整数分配给指针时会发出警告; 然而,对于演员,这个警告不会产生,隐藏一个错误.
malloc的强制转换在C中是不必要的,但在C ++中是强制性的。
由于以下原因,无需在C中进行强制转换:
void *
对于C,会自动安全地提升为任何其他指针类型。
如果您忘记包含,可能会隐藏错误
。这可能导致崩溃。
如果指针和整数的大小不同,那么您将通过强制转换隐藏警告,并且可能丢失返回地址的位。
如果指针的类型在其声明处更改,则可能还需要更改malloc
调用并强制转换的所有行。
另一方面,强制转换可以增加程序的可移植性。即,它允许C程序或函数作为C ++进行编译。