============= 文章1 ================
首先要说的是,使用 malloc 函数,请包含 stdlib.h(C++ 中能够是 cstdlib),而不是 malloc.h 。由于 malloc.h 历来没有在 C 或者 C++ 标准中出现过!所以并不是全部编译器都有 malloc.h 这个头文件。可是全部的 C 编译器都应该有 stdlib.h 这个头文件。
在 C++ 中,强制转换 malloc() 的返回值是必须的,不然不能经过编译。
在 C 中,这种强制转换倒是多余的,而且不利于代码维护。
起初,C 没有 void* 指针,那时 char* 被用做泛型指针(generic pointer),因此那时 malloc 的返回值是 char* 。所以,那时必须强制转换 malloc 的返回值。后来,ISO C 标准定义了 void* 指针做为新的泛型指针。而且 void* 指针能够不经转换,直接赋值给任何类型的指针(函数指针除外)。今后,malloc 的返回值变成了 void* 以后,便再也不须要强制转换 malloc 的返回值了。如下程序在 VC6 编译无误经过。
#include <stdlib.h>
int main( void )
{
double *p = malloc( sizeof *p ); /* 不推荐用 sizeof( double ) */
return 0;
}
固然,强制转换 malloc 的返回值并无错,但多此一举!例如,往后你有可能把 double *p 改为 int *p 。这时,你就要把全部相关的 (double *)malloc( sizeof(double) ) 改为 (int *)malloc( sizeof(int) ) 。若是改漏了,那么你的程序就会存在 bug 。就算你有把握把全部相关的语句都改掉,但这种无聊乏味的工做你不会喜欢吧!不使用强制转换能够避免这样的问题,并且书写简便,何乐而不为呢?使用如下代码,不管之后指针改为什么类型,都不用做任何修改。
double *p = malloc( sizeof *p );
值得一提的是,上述写法中 p 虽然没有指向具体的内存空间,但并不影响经过 sizeof *p 来计算占用内存的大小。
相似地,使用 calloc ,realloc 等返回值为 void* 的函数时,也不须要强制转换返回值。
============= 文章2 ================
本文归纳叙述了上文的内容,而且针对 malloc 返回值的 3 种转型方式进行总结,(相对于上文)更全面的总结其各自的应用范围。
之前有篇文章叫《C/C++ 误区 —— 强制转换 malloc() 的返回值》(即上文)。文章大体内容是:
- malloc 函数在 <stdlib.h> 或者 <cstdlib> 头文件中,而不是 <malloc.h> 中。
- 因为 C 语言最初没有 void 类型,因此是使用 char* 来表明通用指针。
/* the old declaration of malloc */
char* malloc(size_t size);
char* p = malloc( size * sizeof(*p) );
/* 能够,不须要转型 */
T1* p1 = malloc(size1 * sizeof(*p1) );
/* (T1!=char) 不能够,char*不能隐式转换成T1* */
T2* p2 = (T2*)malloc(size2 * sizeof(*p2) );
/* (T2!=char) 能够,显示类型转换 */
- C 语言后来引入了 void 类型,开始使用 void* 表明通用指针,同时规定 void* 能够隐式转换到任意指针类型。
/* the new declaration of malloc */
void* malloc(size_t size);
char* p = malloc( size * sizeof(*p) );
/* 仍然能够,void* 能够隐式转换到任意指针类型 */
T1* p1 = malloc( size1 * sizeof(*p1) );
/* 如今能够,void* 能够隐式转换到任意指针类型 */
T2* p2 = (T1*)malloc( size2 * sizeof(*p2) );
/* 仍然能够,但再也不必须 */
- 在引入了 void 以后的 C 语言中,再使用强制转换是多此一举,同时影响代码维护,而且说这是一个 C/C++ 的误区。
上面 4 点属于原文观点,下面阐述本文观点。
对 malloc 返回值的转型,大体有如下三种方式:
/* legal only in C */
/* 新头文件,具备 void 类型 */
T* p = malloc(size * sizeof(*p) ); /* T!=void */
/* 旧头文件,不具备 void 类型 */
T* p = (T*)malloc(size* sizeof(*p) ); /* T!=void */
C++ 自然支持 void ,可是不容许 void* 隐式转换到任意类型指针,须要 static_cast 。
// legal only in C++
// 新头文件
T* p = static_cast<T*>( malloc(size * sizeof(*p) ));
// 旧头文件(目前还有这种编译器吗)
T* p = reinterpret_cast<T*>( malloc(size * sizeof(*p) ));
// 固然在 C++ 中应该考虑
T* p = new T[size];
// 或者
std::vector<T> p(size);
// 但这不是文章讨论重点
/* legal in both C and C++ */
/* legal in both new and old header */
T* p = (T*)malloc(size * sizeof(*p) );
第 1 种对新头文件的转型方式,如同代码第 1 行所说,仅在 C 编译器中合法。由于 C++ 不支持 void* 到其余指针类型的隐式转换。因此,原文章说这是 C/C++ 的误区,并不许确。这仅仅是(引入 void 类型以后的)C 语言中的“非必须”的动做,是不是误区,还有待考量。
第 2 种对新旧头文件的转型方式,代码第 1 行也说了,仅在 C++ 编译器中合法。由于 C 编译器不认识 static_cast 或者 reinterpret_cast 。
第3种,是一种中庸的写法。如同代码第 1 行所说:此代码不管是在 C 仍是 C++ 编译器,不管是新头文件仍是旧头文件,都是合法的代码。是可移植性最好的代码。
由于代码中使用的(C 风格的)转型、malloc,C/C++ 都支持。
因此,这种写法并不必定是误区或者多此一举。由于代码的做者也许比原文章的做者对移植性(C 和 C++ 的新旧编译器)考虑更多。
参考资料:ISO/IEC 9899:1999 (E) Programming languages — C 7.20.3.3 The malloc function