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

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

而不是:

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

为什么会这样?


当前回答

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

其他回答

在C语言中,可以将空指针分配给任何指针,这就是为什么不应该使用类型转换的原因。如果您想要“类型安全”分配,我可以推荐以下宏函数,这些函数我总是在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++) {
   ...
}

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

正如其他人所说的,它不是C所必需的,而是C++所必需的。如果您认为要使用C++编译器编译C代码,无论出于何种原因,都可以使用宏,例如:

#ifdef __cplusplus
# define MALLOC(type) ((type *)malloc(sizeof(type)))
# define CALLOC(count, type) ((type *)calloc(count, sizeof(type)))
#else
# define MALLOC(type) (malloc(sizeof(type)))
# define CALLOC(count, type) (calloc(count, sizeof(type)))
#endif
# define FREE(pointer) free(pointer)

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

int *sieve = MALLOC(int); // allocate single int => compare to stack int sieve = ???;
int *sieve_arr = CALLOC(4, int); // allocate 4 times size of int => compare to stack (int sieve_arr[4] = {0, 0, 0, 0};
// do something with the ptr or the value
FREE(sieve);
FREE(sieve_arr);

它将为C和C++编译。

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

编辑:铸造有一定的意义。当您使用数组表示法时,生成的代码必须知道要到达下一个元素的开头,需要前进多少个内存位置,这是通过强制转换实现的。通过这种方式,您可以知道,对于double,您可以提前8个字节,而对于int,则可以提前4个字节,依此类推。因此,如果您使用指针表示法,则不会产生任何影响,在数组表示法中,这是必要的。

不,您不强制转换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++有一整套不同风格的铸造。