关于C#:我要把malloc的结果投射出来吗?

Do I cast the result of malloc?

在这个问题上,有人在评论中建议我不要把malloc的结果投射出来,也就是说。

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

而不是:

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

为什么会这样?


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

  • 这是不必要的,因为在这种情况下,void *会自动安全地升级为任何其他指针类型。
  • 它增加了代码的混乱,强制转换不太容易读取(特别是如果指针类型很长的话)。
  • 它会让你重复你自己,这通常是不好的。
  • 如果忘记包含,则可能隐藏错误。这可能导致崩溃(或者更糟的是,直到后来在代码的某些完全不同的部分中才导致崩溃)。考虑一下,如果指针和整数的大小不同,会发生什么情况;然后通过强制转换隐藏一个警告,可能会丢失返回地址的位。注:从c11开始,隐式函数不再是c,这一点不再相关,因为没有自动假设未声明的函数返回int

作为澄清,请注意我说过"你不需要演员表",而不是"你不需要演员表"。在我看来,即使你做得对,也不包括演员阵容。这样做根本没有好处,但是一堆潜在的风险,包括演员表表明你不知道风险。

也请注意,正如评论者指出的,上面提到的是直通C,而不是C++。我非常相信C语言和C++语言是独立的语言。

此外,您的代码不必要地重复类型信息(int),这可能导致错误。最好取消对用于存储返回值的指针的引用,将两者"锁定"在一起:

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

这也会将length移到前面以提高可见性,并删除带有sizeof的多余括号;只有当参数是类型名时才需要这些括号。许多人似乎不知道(或忽略)这一点,这使得他们的代码更加冗长。记住:sizeof不是一个函数!:)

当把length移到前面时,在一些罕见的情况下可能会增加可见性,但也应注意,在一般情况下,最好将表达式写为:

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

由于保持sizeof优先,在这种情况下,确保至少使用size_t数学进行乘法。

比较:当widthlengthsize_t小时,malloc(sizeof *sieve * length * width)malloc(length * width * sizeof *sieve)第二个可能溢出length * width


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

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

另外,如果您更改了EDOCX1的类型(3),就不必担心更改表达式的右侧。

正如人们所指出的,石膏是不好的。特别是指针投射。


铸造:你,因为

  • 它使您的代码更便携之间的C和C + +,和一个伟大的体验节目那样,他们是在尝试写作要求写作时,他们是真的我在C(或C++中的C编译器的扩展,加上本地)。
  • 否则,错误隐藏可以这样做:注意所有这样的例子,当type *type **写到位。
  • 库,它让你从你#includenoticing无法适当的头文件,在森林的树。它同样是说"不要担心你无法买卖的事实在逆境里看到一个编译器的原型--这是真正重要的东西pesky stdlib.h难忘!"
  • 额外的部队在认知信息交叉检查。它使(光)所需的类型旁边的算术你做为原始大小是可变的。我打赌你可以做这样的研究,这是多重要的节目malloc()错误当有一个铸造速度。作为一个assertions滴注意向,减少错误。
  • 一个方法是,在重复自己的机器可以检查是经常的一大理念。事实上,这就是对这一断言,使用CAST是断言。assertions仍然是最通用的技术,因此我们有正确的代码,因为图灵与想法来这么多年前。


正如其他陈述的那样,C是不需要的,但是C++是不需要的。如果你认为你将用C++编译器编译你的C代码,不管是哪个原因,你都可以使用宏,比如:

1
2
3
4
5
#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

这样,您仍然可以非常紧凑地编写它:

1
int *sieve = NEW(int, 1);

它将编译为C和C++。


来自维基百科

Advantages to casting

  • Including the cast may allow a C program or function to compile as C++.

  • The cast allows for pre-1989 versions of malloc that originally returned a char *.

  • Casting can help the developer identify inconsistencies in type sizing should the destination pointer type change, particularly if the pointer is declared far from the malloc() call (although modern compilers and static analyzers can warn on such behaviour without requiring the cast).

Disadvantages to casting

  • Under the ANSI C standard, the cast is redundant.

  • Adding the cast may mask failure to include the header stdlib.h, in
    which the prototype for malloc is found. In the absence of a
    prototype for malloc, the standard requires that the C compiler
    assume 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. On certain
    architectures and data models (such as LP64 on 64-bit systems, where
    long and pointers are 64-bit and int is 32-bit), this error can
    actually result in undefined behaviour, as the implicitly declared
    malloc returns a 32-bit value whereas the actually defined function
    returns a 64-bit value. Depending on calling conventions and memory
    layout, this may result in stack smashing. This issue is less likely
    to go unnoticed in modern compilers, as they uniformly produce
    warnings that an undeclared function has been used, so a warning will
    still appear. For example, GCC's default behaviour is to show a
    warning that reads"incompatible implicit declaration of built-in
    function" regardless of whether the cast is present or not.

  • If the type of the pointer is changed at its declaration, one may
    also, need to change all lines where malloc is called and cast.

虽然不使用强制转换的malloc是首选方法,而且大多数有经验的程序员都会选择它,但是您应该使用任何您愿意知道问题的方法。

IE:如果你需要编译C程序作为C++(尽管这些是单独的语言),你应该使用EDCOX1,5,用铸造。


在C中,可以将一个空指针隐式转换为任何其他类型的指针,因此不需要强制转换。使用一个可以向不经意的观察者建议,需要一个的原因是有一些的,这可能是误导性的。


你不铸造的结果,因为malloc,这样不加杂波您的代码。

最普通的原因为什么人铸造的结果,因为他们是unsure malloc是C语言是如何工作的。这是一个警告标志:如果你不知道一个特定的语言机制的作品,然后Don’t Take a的猜测。它要求在外观上或堆栈溢出。

一些评论:

  • a void指针可以转换到/从其他任何指针类型没有显现(C11和6.5.16.1容器)。

  • 要允许不需要的C + +在灌注void*和另一个之间的隐式指针类型。所以在C++中,现已经是正确的。但如果你的程序在C + +,你不应该使用newmalloc()。你应该从来没有使用编译的C代码的C + +编译器。

    如果你需要支持这两个C和C + +和使用相同的源代码,编译一个标识该交换机等。不要试图用同样的语言标准是国家代码,因为他们是不兼容的。

  • 如果一个C编译器,因为你无法找到一个良好的功能包括一个标题,你会得到一个编译/链接错误吗。所以,如果你忘了包括这是不重要的,你不可以编译您的项目。

  • 这一古老的编译器在后续版本的标准是超过25岁,肮脏的玻璃包括会导致危险的行为。这是因为在古代的标准,没有可见的函数原型implicitly返回到int型转换。从铸造的结果明确了这是malloc然后隐藏的BUG。

    但这真的是一个非问题。你不使用计算机的25岁,那么你为什么要使用一个25岁的编译器?


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


铸造malloc()值返回的是没有必要的,但现在,我想添加一个似乎没有一个尖锐的点出:安切洛蒂

在古代的日子,这是在ANSI C提供的void *为通用型分型研究char *是搜索使用。在这样的实例,编译器警告CAN;浇下来。

参考:c FAQ


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


加上我的经验,学习计算机工程,我发现我在C语言中看到过的两三位教授总是投马洛克的票,但是我问的那位教授(有着丰富的简历和对C的理解)告诉我,这是绝对不必要的,但只不过过去是绝对具体的,并让学生进入一种存在的心态。非常具体。从本质上说,强制转换不会改变它的工作方式,它完全按照它所说的做,分配内存,强制转换也不会影响它,你得到相同的内存,即使你错误地将它强制转换成其他东西(并且以某种方式规避编译器错误),C也会以同样的方式访问它。

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


这就是GNU C库参考手册所说的:

You can store the result of malloc into any pointer variable without a
cast, because ISO C automatically converts the type void * to another
type of pointer when necessary. But the cast is necessary in contexts
other than assignment operators or if you might want your code to run
in traditional C.

实际上,ISO C11标准(p347)是这样说的:

The pointer returned if the allocation succeeds is suitably aligned so
that it may be assigned to a pointer to any type of object with a
fundamental alignment requirement and then used to access such an
object or an array of such objects in the space allocated (until the
space is explicitly deallocated)


void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地进行类型转换。

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


返回类型是void *,可以灌注到所需的数据指针的类型是可以的。


在C语言中,可以将空指针分配给任何指针,这就是为什么不应使用类型转换的原因。如果您想要"类型安全"分配,我可以推荐以下宏函数,我在我的C项目中总是使用这些函数:

1
2
3
#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

有了这些,你可以简单地说

1
NEW_ARRAY(sieve, length);

对于非动态数组,第三个must-have函数宏是

1
#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

这使得阵列循环更安全、更方便:

1
2
3
4
5
int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}


它取决于在编程语言和编译器。如果你使用mallocC型铸造没有需要它,它会自动为您的铸造型,但是如果使用C + +,然后你应该想,因为malloc型铸造void*返回a的类型。


习惯GCC和Clang的人被宠坏了。外面不太好。

这些年来,我一直被要求使用的那些老掉牙的编译器吓坏了。通常,公司和管理人员都采用极为保守的方法来更改编译器,甚至不会测试新的编译器(具有更好的标准遵从性和代码优化)是否能在其系统中工作。对于工作的开发人员来说,实际的情况是,当您在编码时,需要覆盖您的基础,不幸的是,如果您不能控制什么编译器可能应用到您的代码,那么抛出malloc是一个好习惯。

我还建议许多组织应用自己的编码标准,如果定义了这一标准,人们就应该遵循这一标准。在缺乏明确指导的情况下,我倾向于选择最有可能在任何地方进行编译的方法,而不是盲目地遵循标准。

在现行标准下不需要这样做的论点是非常有效的。但这一论点忽略了现实世界的实际情况。在一个完全由当时的标准所统治的世界里,我们并不编码,而是根据我喜欢称之为"地方管理的现实领域"的实用性来编码。这比时空更弯曲和扭曲。-)

YMMV。

我倾向于把malloc当作防御行动。不漂亮,不完美,但一般安全。(老实说,如果你没有包括stdlib.h,那么你有比铸造malloc更多的问题!).


我加入强制转换只是为了表示对类型系统中丑陋的漏洞的不赞成,它允许代码(如以下代码片段)在没有诊断的情况下编译,即使没有强制转换用于导致错误的转换:

1
2
3
double d;
void *p = &d;
int *q = p;

我希望它不存在(并且它不在C++中),所以我投下。它代表了我的品味和我的编程政治。我不仅投出了一个指针,而且实际上,投了一张选票,并且驱逐了愚蠢的恶魔。如果我不能真正摆脱愚蠢,那么至少让我用一种抗议的姿态来表达这样做的愿望。

实际上,一个好的实践是用返回unsigned char *的函数包装malloc(和friends),基本上不要在代码中使用void *。如果需要指向任何对象的通用指针,请使用char *unsigned char *,并在两个方向上进行强制转换。一个可以放松的方法是使用像memsetmemcpy这样的函数而不使用强制转换。

在铸造和C++兼容的主题上,如果编写代码以编译为C和C++(在这种情况下,当将EDCOX1 OR 0的值返回到EDCOX1(2)之外)时,您可以为自己做一件非常有益的事情:您可以使用宏来进行转换,将其转换为C++样式转换。编译为C++,但编译为C时编译为C:

1
2
3
4
5
6
7
8
9
10
/* 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++编译代码,它将强制使用适当的CAST。例如,如果您使用strip_qual只是为了删除constvolatile,但程序的更改方式现在涉及到类型转换,那么您将获得诊断,并且您将不得不使用强制转换的组合来获得所需的转换。

为了帮助你坚持这些宏,GNU C++(不是C!)编译器有一个漂亮的特性:一个可选的诊断程序,它为所有出现的C样式转换生成。

1
2
3
4
5
     -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++,你可以使用这个EDCOX1的13个选项来找出EDOCX1的14个可能的语法,这些语法可能会蠕动到代码中,并通过在上面的宏中替换一个适当的选择来替换这些诊断(或者如果需要的话,一个组合)。

这种转换处理是在"干净C"中工作的唯一最大的技术证明:组合C语言和C++语言,这反过来又证明EDCOX1〔0〕的返回值是合理的。


尽可能在C语言中编程时要做的最好的事情是:

  • 使您的程序通过C编译器编译,所有警告都打开-Wall,并修复所有错误和警告。
  • 确保没有声明为auto的变量
  • 然后使用EDCOX1×0和EDCOX1×3的C++编译器编译它。修复所有错误和警告。
  • 现在再次使用C编译器编译。您的程序现在应该编译而不发出任何警告,并且包含更少的错误。
  • 此过程允许您利用C++严格类型检查,从而减少错误的数量。特别是,这个程序强制您包括stdlib.h,否则您将

    malloc was not declared within this scope

    也迫使你投下malloc的结果,否则你会得到

    invalid conversion from void* to T*

    或者你的目标类型是什么。

    C写的唯一好处,而不是C++我能找到的好处是

  • C有明确的ABI
  • C++可以生成更多的代码[异常,RTTI,模板,运行时多态性]
  • 注意,在理想情况下,当使用C的公共子集和静态多态特性时,第二个缺点应该消失。

    对于那些发现C++严格规则不方便的人,我们可以使用推断类型的C++ 11特征。

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


    我更喜欢做演员,但不是手工。我最喜欢的是使用glib中的g_newg_new0宏。如果不使用glib,我将添加类似的宏。这些宏在不影响类型安全的情况下减少了代码重复。如果您键入了错误类型,您将在非空指针之间得到隐式转换,这将导致警告(C++中的错误)。如果忘记包含定义g_newg_new0的头,则会得到一个错误。g_newg_new0都采用相同的论点,而malloc的论点比calloc少。只需添加0即可获得零初始化内存。代码可以用C++编译器编译而无需修改。


    不,你不会投射malloc()的结果。

    一般情况下,你不会在void *上或从void *上投射。

    不这样做的一个典型原因是,对#include 的失败可能会被忽视。这在很长一段时间内不再是问题,因为C99将隐式函数声明设置为非法,所以如果编译器至少符合C99,您将收到一条诊断消息。

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

    在C语言中,指针转换几乎总是一个错误。这是因为以下规则(§6.5 P7 in N1570,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.

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

    1
    2
    3
    long x = 5;
    double *p = (double *)&x;
    double y = *p;

    而且,有时令人惊讶的是,以下也是:

    1
    2
    3
    4
    5
    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;

    有时,您确实需要强制转换指针,但是考虑到严格的别名规则,您必须非常小心。因此,在代码中出现指针强制转换时,您必须仔细检查它的有效性。因此,您永远不会编写不必要的指针转换。

    DR

    简而言之:因为在C语言中,指针转换的任何一次出现都应该为需要特别注意的代码升起一个红色标志,所以您不应该编写不必要的指针转换。

    侧记:

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

      1
      2
      3
      int x = 5;
      printf("%p
      "
      , (void *)&x);

      这里需要强制转换,因为printf()是一个变量函数,所以隐式转换不起作用。

    • 在C++中,情况是不同的。在处理派生类的对象时,强制转换指针类型有些常见(并且是正确的)。因此,在C++中,EDCOX1与1的转换不是隐式的,这是有意义的。C++有一整套不同的铸造风格。


    铸造只适用于C++,而不是C。在使用C++编译器的情况下,最好将其转换为C编译器。


    void指针背后的概念是它可以强制转换为任何数据类型,这就是malloc返回void的原因。此外,您必须了解自动排版。因此,尽管您必须这样做,但强制转换指针并不是必需的。它有助于保持代码的整洁并帮助调试


    void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地进行类型转换。

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


  • 正如其他陈述的那样,C是不需要的,但是C++是不需要的。

  • 包括CAST可以允许C程序或函数作为C++编译。

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

  • 但是,如果您在那时进行了强制转换,那么如果您忘记了包括,它会隐藏一个错误。这会导致崩溃(或者更糟的是,不会导致崩溃)直到后来在代码的某个完全不同的部分)。

    因为stdlib.h包含malloc的原型。在缺少malloc的原型,标准要求编译器假定malloc返回int。如果没有强制转换,则当此整数分配给指针时发出警告;但是,对于强制转换,不会生成此警告,从而隐藏错误。


  • 在C中,MALOC的铸造是不必要的,但在C++中是强制性的。

    C中不需要铸造,因为:

    • 在C的情况下,void *自动安全地升级为任何其他指针类型。
    • 如果忘记包含,它可以隐藏错误。这会导致崩溃。
    • 如果指针和整数大小不同,则通过强制转换隐藏警告,可能会丢失返回地址的位。
    • 如果指针的类型在声明时发生了更改,则还可能需要更改调用和强制转换malloc的所有行。

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