天天看点

【整理】为什么在C/C++中总是对malloc的返回值进行强制转换

============= 文章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 编译无误通过。  

<a href="http://my.oschina.net/moooofly/blog/128330#">?</a>

1

2

3

4

5

6

<code>#include &lt;stdlib.h&gt;</code>

<code>int</code> <code>main(</code><code>void</code> <code>)</code>

<code>{</code>

<code>    </code><code>double</code> <code>*p =</code><code>malloc</code><code>(</code><code>sizeof</code> <code>*p );</code><code>/* 不推荐用 sizeof( double ) */</code>

<code>    </code><code>return</code> <code>0;</code>

<code>}</code>

       当然,强制转换 malloc 的返回值并没有错,但画蛇添足!例如,日后你有可能把 double *p 改成 int *p 。这时,你就要把所有相关的 (double *)malloc( sizeof(double) ) 改成 (int *)malloc( sizeof(int) ) 。如果改漏了,那么你的程序就会存在 bug 。就算你有把握把所有相关的语句都改掉,但这种无聊乏味的工作你不会喜欢吧!不使用强制转换可以避免这样的问题,而且书写简便,何乐而不为呢?使用以下代码,无论以后指针改成什么类型,都不用作任何修改。  

<code>double</code> <code>*p =</code><code>malloc</code><code>(</code><code>sizeof</code> <code>*p );</code>

       值得一提的是,上述写法中 p 虽然没有指向具体的内存空间,但并不影响通过 sizeof *p 来计算占用内存的大小。 

       类似地,使用 calloc ,realloc 等返回值为 void* 的函数时,也不需要强制转换返回值。 

============= 文章2 ================ 

      本文概括叙述了上文的内容,并且针对 malloc 返回值的 3 种转型方式进行总结,(相对于上文)更全面的总结其各自的应用范围。 

以前有篇文章叫《c/c++ 误区 —— 强制转换 malloc() 的返回值》(即上文)。文章大致内容是: 

malloc 函数在 &lt;stdlib.h&gt; 或者 &lt;cstdlib&gt; 头文件中,而不是 &lt;malloc.h&gt; 中。

由于 c 语言最初没有 void 类型,所以是使用 char* 来代表通用指针。

7

8

9

10

11

<code>/* the old declaration of malloc */</code>

<code>char</code><code>*</code><code>malloc</code><code>(</code><code>size_t</code> <code>size);</code>

<code>char</code><code>* p =</code><code>malloc</code><code>( size *</code><code>sizeof</code><code>(*p) );</code>

<code>/* 可以,不需要转型 */</code>

<code>t1* p1 =</code><code>malloc</code><code>(size1 *</code><code>sizeof</code><code>(*p1) );</code>

<code>/* (t1!=char) 不可以,char*不能隐式转换成t1*  */</code>

<code>t2* p2 = (t2*)</code><code>malloc</code><code>(size2 *</code><code>sizeof</code><code>(*p2) );</code>

<code>/* (t2!=char) 可以,显示类型转换 */</code>

c 语言后来引入了 void 类型,开始使用 void* 代表通用指针,同时规定 void* 可以隐式转换到任意指针类型。 

<code>/* the new declaration of malloc */</code>

<code>void</code><code>*</code><code>malloc</code><code>(</code><code>size_t</code> <code>size);</code>

<code>/* 仍然可以,void* 可以隐式转换到任意指针类型 */</code>

<code>t1* p1 =</code><code>malloc</code><code>( size1 *</code><code>sizeof</code><code>(*p1) );</code>

<code>/* 现在可以,void* 可以隐式转换到任意指针类型 */</code>

<code>t2* p2 = (t1*)</code><code>malloc</code><code>( size2 *</code><code>sizeof</code><code>(*p2) );</code>

<code>/* 仍然可以,但不再必须 */</code>

在引入了 void 之后的 c 语言中,再使用强制转换是画蛇添足,同时影响代码维护,并且说这是一个 c/c++ 的误区。  

上面 4 点属于原文观点,下面阐述本文观点。 

对 malloc 返回值的转型,大致有以下三种方式:  

仅在 c 中

/* legal only in c */ 

<code>/* 新头文件,具有 void 类型 */</code>

<code>t* p =</code><code>malloc</code><code>(size *</code><code>sizeof</code><code>(*p) );</code><code>/* t!=void */</code>

<code>/* 旧头文件,不具有 void 类型 */</code>

<code>t* p = (t*)</code><code>malloc</code><code>(size*</code><code>sizeof</code><code>(*p) );</code><code>/* t!=void */</code>

仅在 c++ 中

       c++ 天然支持 void ,但是不允许 void* 隐式转换到任意类型指针,需要 static_cast 。 

// legal only in c++ 

<code>// 新头文件</code>

<code>t* p =</code><code>static_cast</code><code>&lt;t*&gt;(</code><code>malloc</code><code>(size *</code><code>sizeof</code><code>(*p) ));</code>

<code>// 旧头文件(目前还有这种编译器吗)</code>

<code>t* p =</code><code>reinterpret_cast</code><code>&lt;t*&gt;(</code><code>malloc</code><code>(size *</code><code>sizeof</code><code>(*p) ));</code>

<code>// 当然在 c++ 中应该考虑</code>

<code>t* p =</code><code>new</code> <code>t[size];</code>

<code>// 或者</code>

<code>std::vector&lt;t&gt; p(size);</code>

<code>// 但这不是文章讨论重点</code>

在 c/c++ 中

<code>/* legal in both c and c++ */</code>

<code>/* legal in both new  and old header */</code>

<code>t* p = (t*)</code><code>malloc</code><code>(size *</code><code>sizeof</code><code>(*p) );</code>

       第 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 

继续阅读