在这个问题上,有人在评论中建议我不要使用malloc的结果。即,我应该这样做:
int *sieve = malloc(sizeof(*sieve) * length);
而不是:
int *sieve = (int *) malloc(sizeof(*sieve) * length);
为什么会这样?
在这个问题上,有人在评论中建议我不要使用malloc的结果。即,我应该这样做:
int *sieve = malloc(sizeof(*sieve) * length);
而不是:
int *sieve = (int *) malloc(sizeof(*sieve) * length);
为什么会这样?
当前回答
不强制强制强制强制转换malloc的结果,因为它返回void*,并且void*可以指向任何数据类型。
其他回答
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++程序将要使用的头中的内联函数中使用。
就我个人而言,我更喜欢第二种选择。
TL;博士
int *sieve = (int *) malloc(sizeof(int) * length);
有两个问题。类型转换,并且您使用类型而不是变量作为sizeof的参数。相反,请这样做:
int *sieve = malloc(sizeof *sieve * length);
长版本
不您不必计算结果,因为:
这是不必要的,因为在这种情况下,void*会自动安全地升级为任何其他指针类型。它给代码增加了混乱,强制转换不太容易阅读(特别是如果指针类型很长)。这会让你重复自己,这通常是不好的。如果您忘记包含<stdlib.h>,它可能会隐藏错误。这可能会导致崩溃(或者更糟糕的是,直到稍后在代码的某个完全不同的部分发生崩溃)。考虑如果指针和整数的大小不同会发生什么;然后你通过投射来隐藏警告,可能会丢失返回地址的一部分。注意:从C99开始,隐式函数从C中消失,这一点不再相关,因为没有自动假设未声明的函数返回int。
作为澄清,请注意我说的是“你不需要投”,而不是“你不必投”。在我看来,这是一个失败的演员阵容,即使你做得对。这样做根本没有好处,但有一系列潜在的风险,包括演员阵容表明你不知道这些风险。
另外,正如评论家指出的,上面提到的是直接C,而不是C++。我非常坚信C和C++是独立的语言。
此外,代码不必要地重复类型信息(int),这可能会导致错误。最好取消引用用于存储返回值的指针,将两者“锁定”在一起:
int *sieve = malloc(length * sizeof *sieve);
这还将长度移到前面以增加可见性,并删除sizeof的多余括号;只有当参数是类型名时,才需要它们。许多人似乎不知道(或忽略)这一点,这使得他们的代码更加冗长。记住:sizeof不是函数!:)
虽然在一些罕见的情况下,将长度移到前面可能会增加可见性,但也应注意,在一般情况下,最好将表达式写成:
int *sieve = malloc(sizeof *sieve * length);
在这种情况下,由于首先保持sizeof,确保乘法至少使用size_t数学运算完成。
比较:malloc(sizeof*sive*length*width)与malloc,当width和length的类型小于size_t时,第二个可能会溢出长度*宽度。
不,您不强制转换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++有一整套不同风格的铸造。
返回的类型为void*,可以将其转换为所需类型的数据指针,以便可以取消引用。
void指针背后的概念是它可以被强制转换为任何数据类型,这就是malloc返回void的原因。此外,您必须了解自动打字。因此,虽然必须强制强制强制转换指针,但它有助于保持代码整洁,并有助于调试