在这个问题上,有人在评论中建议我不要使用malloc的结果。即,我应该这样做:

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

而不是:

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

为什么会这样?


当前回答

malloc的主要问题是获得正确的大小。

malloc()返回的内存是非类型的,由于简单的强制转换,它不会神奇地获得有效的类型。

我想这两种方法都很好,选择应该取决于程序员的意图。

如果为类型分配内存,则使用强制转换。

ptr=(T*)malloc(sizeof(T));

如果为给定指针分配内存,则不要使用强制转换。

ptr=malloc(sizeof*ptr);

Ad 1

第一种方法通过为给定类型分配内存,然后将其强制转换以确保分配给正确的指针,从而确保正确的大小。如果使用了不正确的ptr类型,则编译器将发出警告/错误。如果ptr的类型被更改,那么编译器将指出代码需要重构的地方。

此外,第一种方法可以组合成类似于C++中新运算符的宏。

#define NEW(T) ((T*)malloc(sizeof(T)))
...
ptr = NEW(T);

此外,如果ptr为void*,则此方法有效。

Ad 2

第二个方法不关心类型,它通过从指针的类型中获取数据来确保正确的大小。这种方法的主要优点是,每当ptr的类型发生变化时,都可以自动调整存储大小。它可以在重构时节省一些时间(或错误)。

缺点是,如果ptr为空,则该方法不起作用,但它可能被认为是一件好事。而且它不适用于C++,因此不应在C++程序将要使用的头中的内联函数中使用。

就我个人而言,我更喜欢第二种选择。

其他回答

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

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

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

根据现行标准,这是不必要的,这一论点是非常有效的。但这一论点忽略了现实世界的现实性。在一个完全由当今标准统治的世界里,我们并没有编码,而是由我喜欢称之为“地方管理的现实领域”的实用性来控制。这比太空时代更加弯曲和扭曲

YMMV。

我倾向于将使用malloc作为防御操作。不漂亮,不完美,但总体安全。(老实说,如果你没有包含stdlib.h,那么你会遇到比强制执行malloc更多的问题!)。

void指针背后的概念是它可以被强制转换为任何数据类型,这就是malloc返回void的原因。此外,您必须了解自动打字。因此,虽然必须强制强制强制转换指针,但它有助于保持代码整洁,并有助于调试

正如其他人所说的,它不是C所需要的,而是C++所需要的。包括强制转换可以允许C程序或函数编译为C++。在C中,这是不必要的,因为void*会自动安全地升级为任何其他指针类型。但如果你当时就投了,如果你忘了包括,它会隐藏一个错误stdlib.h。这可能会导致崩溃(更糟的是,不会导致崩溃直到稍后在代码的某个完全不同的部分)。因为stdlib.h包含找到malloc的原型。在由于没有malloc的原型,标准要求C编译器假设malloc返回int。如果没有强制转换当该整数被分配给指针时发出警告;然而,在演员阵容中,这个警告并没有产生,隐藏了一个bug。

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

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

一般来说,你不会向虚空施法或从虚空施法。

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

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

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

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

tl;博士

简而言之:因为在C语言中,指针强制转换的任何出现都会给需要特别注意的代码带来危险,所以您不应该编写不必要的指针强制转换。


附带说明:

在某些情况下,您实际上需要转换为void*,例如,如果您想要打印指针:int x=5;printf(“%p\n”,(void*)&x);这里强制转换是必要的,因为printf()是一个可变函数,所以隐式转换不起作用。在C++中,情况不同。在处理派生类的对象时,强制转换指针类型有些常见(也是正确的)。因此,在C++中,与void*的转换不是隐式的,这是有意义的。C++有一整套不同风格的铸造。