casting malloc例子 malloc库 - 我是否施放了malloc的结果?





13 Answers

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

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

如果您改变sieve的类型,这使您不必担心改变表达式的右侧。

人们已经指出,演员阵容很糟糕。 特别是指针转换。

malloc头文件 malloc用法 malloc数组

这个问题中 ,有人在comment中建议我不应该转换malloc的结果,即

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

而不是:

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

为什么会这样呢?




正如其他人所说,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 ++编译。




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




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




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




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

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




这就是GNU C Library Reference手册所说的:

您可以将malloc的结果存储到任何没有malloc转换的指针变量中,因为ISO C会在必要时自动将类型void *转换为另一种类型的指针。 但是,在赋值运算符以外的情况下,或者您可能希望代码在传统C中运行时,强制转换是必需的。

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

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




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



我只是为了表示不赞成类型系统中的丑陋洞而放入演员表,这允许在没有诊断的情况下编译下面的代码片段,即使没有使用强制转换来导致错误的转换:

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

我希望这不存在(并且它不在C ++中),所以我投了。 它代表了我的品味和我的编程政治。 我不仅投了一个指针,而且还有效地投了一张选票,然后抛弃了愚蠢的恶魔 。 如果我实际上无法摆脱愚蠢 ,那么至少让我表达希望以抗议的姿态这样做。

事实上,一个好的做法是使用返回unsigned char *函数包装malloc (和朋友),并且基本上永远不会在代码中使用void * 。 如果需要通用指针到任意对象,请使用char *unsigned char * ,并在两个方向上进行强制转换。或许可以放纵的一种放松是使用类似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搜索您的代码库以获取这些标识符,就会显示所有演员阵容的位置,以便您查看其中是否有任何错误。

然后,继续,如果您经常使用C ++编译代码,它将强制使用适当的强制转换。例如,如果你strip_qual只是用来删除一个const或者volatile,但程序改变的方式现在涉及类型转换,你将得到一个诊断,你将不得不使用一组转换来获得所需的转换。

为了帮助您遵守这些宏,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




只要有可能,在C语言编程时最好的事情是:

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

此过程允许您利用C ++严格类型检查,从而减少错误的数量。特别是,此程序会强制您包含stdlib.h或者您将获得

malloc 未在此范围内宣布

并且还会强迫你施放结果,malloc否则你会得到

无效转换void*T*

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

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

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

请注意,当使用C的公共子集和静态多态特征时,理想情况下的第二个缺点应该消失。

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

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



我喜欢做演员,但不是手动。我最喜欢的是使用g_newg_new0从油嘴滑舌宏。如果没有使用glib,我会添加类似的宏。这些宏减少了代码重复,而不会影响类型安全性。如果你得到错误的类型,你会得到非void指针之间的隐式转换,这会引起警告(C ++中的错误)。如果你忘了,包括定义页眉g_newg_new0,你会得到一个错误。g_new并且g_new0两者都采用相同的参数,不像malloc参数少于calloc。只需添加0即可获得零初始化内存。代码可以使用C ++编译器进行编译而无需更改。




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

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




在C中不需要转换malloc,但在C ++中是必需的。

由于以下原因,C中不需要进行转换:

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

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




Related