问题:我是否要转换malloc的结果?

这个问题中,有人在评论中建议的应该 强制转换malloc的结果,即

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

而不是:

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

为什么会这样?

标签:c,malloc,casting

回答1:

;您不要投射结果,因为:

  • 这是不必要的,因为在这种情况下,void*会自动安全地提升为任何其他指针类型。
  • 它使代码更加混乱,强制转换不是很容易阅读(特别是如果指针类型很长的话)。
  • 这会让您重复一遍,这通常很糟糕。
  • 如果您忘记包含 ,它可能会隐藏错误。这可能会导致崩溃(或更糟糕的是,直到稍后在代码的某些完全不同的部分中,不会导致崩溃)。考虑一下如果指针和整数的大小不同会怎样?那么您将通过投射隐藏警告,并可能丢失返回地址的一部分。注意:从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*筛网*长度*宽度)malloc(length*宽度*筛网*长度*宽度)与第二个代码可能溢出length* width ,如果widthlength的类型小于size_t的类型。

回答2:

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

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

另外,即使您更改筛子的类型,您也不必担心更改表达式的右侧。

正如人们指出的那样,广播是糟糕的。特别是指针转换。

回答3:

执行投射,因为:

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

回答4:

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

回答5:

来自维基百科

铸造的优势

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

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

  • 如果目标指针类型发生更改,尤其是如果声明的指针距离malloc()调用较远,则铸造可以帮助开发人员识别类型大小不一致的地方(尽管现代编译器和静态分析器可以警告此类行为,而无需演员)。

投射的缺点

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

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

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

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

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

回答6:

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

回答7:

您不强制转换malloc的结果,因为这样做会给您的代码增加毫无意义的混乱。

人们使用malloc结果的最常见原因是因为他们不确定C语言的工作方式。这是一个警告信号:如果您不知道特定语言机制的工作原理,那么不要进行猜测。查找它或询问堆栈溢出。

一些评论:

  • 可以将空指针转换为其他类型的指针,也可以不使用任何强制类型转换(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年的旧编译器?

回答8:

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

回答9:

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

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

参考: C常见问题解答

回答10:

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

回答11:

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

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

回答12:

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

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

回答13:

这是 GNU C库参考手册说:

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

ISO C11标准(p347)这样说:

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

回答14:

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

回答15:

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

回答16:

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

回答17:

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

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

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

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

YMMV。

我倾向于将malloc转换为一种防御性操作。不漂亮,不完美,但总体上是安全的。 (老实说,如果您没有包括stdlib.h,那么与强制转换malloc相比,您会遇到更多的问题。)

回答18:

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

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

我希望那不存在(并且在C ++中不存在),所以我进行了转换。它代表了我的品味和我的编程政治。我不仅要投下指针,而且要投出有效的票,然后抛弃愚蠢的魔鬼。如果我不能实际上 cast出愚蠢,那么至少让我表达一下希望以此来表示抗议。

实际上,一个好的做法是使用返回unsignedchar*的函数包装malloc(和朋友),并且基本上不要使用void* 。如果需要通用的指向任何对象的指针,请使用char*unsignedchar*,并在两个方向上进行强制转换。可以放纵的一种放松可能是使用memsetmemcpy之类的函数而无需强制转换。

关于强制转换和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搜索即可找到这些标识符,从而向您显示所有强制类型转换的位置,因此您可以查看其中是否有错误。< / p>

然后,如果您定期使用C ++编译代码,它将强制使用适当的强制转换。例如,如果使用strip_qual只是删除constvolatile,但是程序会以这样的方式更改,即现在可以进行类型转换涉及到这一点,您将得到诊断,并且必须使用多种类型的转换来获得所需的转换。

为帮助您坚持使用这些宏,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的返回值。 / p>

回答19:

在可能的情况下使用C编程时最好的做法:

  1. 使您的程序通过C编译器编译,并打开所有-Wall警告,并修复所有错误和警告
  2. 确保没有声明为auto
  3. 的变量
  4. 然后使用带有-Wall-std=c++11的C ++编译器对其进行编译。修复所有错误和警告。
  5. 现在再次使用C编译器进行编译。您的程序现在应该在没有任何警告的情况下进行编译,并且包含的​​错误更少。

此过程使您可以利用C ++严格的类型检查,从而减少错误的数量。特别是,此过程会强制您包含stdlib.h,否则您将获得

在此范围内未声明

malloc

并强制您强制转换malloc的结果,否则您将获得

void*T*

的无效转换

或您的目标类型是什么。

用C而不是C ++编写的唯一好处就是

  1. C具有明确指定的ABI
  2. C ++可能会生成更多代码[异常,RTTI,模板,运行时多态性]

请注意,将C通用的子集与 static 多态特征一起使用时,第二个缺点在理想情况下应该消失。

对于那些对C ++严格规则感到不便的人,我们可以将C ++ 11功能与推断类型一起使用

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...

回答20:

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

通常,您不要强制转换为void*

给出的不这样做的典型原因是,可能会忽略#include 失败。由于C99将隐式函数声明定为非法,所以这不再是一个问题了,因此,如果您的编译器至少符合C99,您将收到诊断消息。

但是有一个更强烈的理由不引入不必要的指针强制转换:

在C语言中,指针强制转换几乎总是错误。这是因为以下规则(N1570中的§6.5p7 ,这是C11的最新草案):

只能通过具有以下类型之一的左值表达式访问对象的存储值:
-与对象的有效类型兼容的类型,
-兼容类型的限定版本具有对象的有效类型的
—一种类型是与该对象的有效类型相对应的有符号或无符号类型,
—一种类型是与该对象的有效版本相对应的有符号或无符号的类型对象的有效类型,
-成员中包括上述类型之一的集合或联合类型(递归地包括子集合或包含的联合的成员),或者-字符类型。
/ p>

这也称为严格别名规则。因此,以下代码是未定义行为

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;

有时候,您确实需要强制转换指针,但是鉴于 strict别名规则,您必须非常小心。因此,代码中出现的任何指针转换都是您必须仔细检查其有效性的地方。因此,您永远不会编写不必要的指针强制转换。

tl; dr

简而言之:因为在C语言中,任何出现的 pointer强制转换都会为需要特别注意的代码引发红色标记,因此您永远不要编写不必要的 指针强制转换。


旁注:

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

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

    此处必须进行强制转换,因为printf()是可变函数,因此隐式转换不需要工作。

  • 在C ++中,情况有所不同。在处理派生类的对象时,强制转换指针类型有些普遍(并且正确)。因此,在C ++中,与void*之间的转换是 not 隐式的,这是有道理的。 C ++具有一整套不同的转换样式。

回答21:

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

回答22:

仅适用于C ++而不是C。如果使用的是C ++编译器,则最好将其更改为C编译器。

回答23:

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

回答24:

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

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

回答25:

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

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

  3. 在C语言中没有必要,因为void *会自动安全地提升为任何其他指针类型。

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

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

回答26:

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

由于以下原因,在C中不需要铸造

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

另一方面,强制转换可以增加程序的可移植性。也就是说,它允许C程序或函数编译为C ++。

回到顶部