文档章节

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

摩云飞
 摩云飞
发布于 2013/05/07 16:21
字数 1380
阅读 234
收藏 0
点赞 0
评论 0

============= 文章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 返回值的转型,大致有以下三种方式: 
  • 仅在 C 中
/* legal only in C */
/* 新头文件,具有 void 类型 */
T* p = malloc(size * sizeof(*p) ); /* T!=void */
/* 旧头文件,不具有 void 类型 */
T* p = (T*)malloc(size* sizeof(*p) ); /* T!=void */
  • 仅在 C++ 中
       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);
// 但这不是文章讨论重点
  • 在 C/C++ 中
/* 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



© 著作权归作者所有

共有 人打赏支持
摩云飞
粉丝 365
博文 352
码字总数 952690
作品 0
徐汇
程序员
malloc和new有什么区别

malloc和new有以下不同: new、delete是操作符,可以重载,只能在c++中使用。 malloc、free是函数,可以覆盖,c、c++中都可以使用。 new可以调用对象的构造函数,对应的delete调用相应的析构...

夏雪冬日 ⋅ 2012/12/11 ⋅ 0

C语言/C++编程学习之指针和数组的深入理解

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界 ⋅ 05/14 ⋅ 0

C语言编程知识学习:if语句对出错的处理

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界 ⋅ 05/15 ⋅ 0

C语言编程基础学习——队列详细讲解!

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界 ⋅ 03/20 ⋅ 0

new与malloc比较,QQ农场与大草原?

前几天看到微信群里有人在讨论new与malloc的不同之处,看到有人说malloc不如new,细细看他所列举的为什么new比malloc好的原因,感觉很有道理,但是转念一想,突然间我又觉得语言这种东西为什...

loving_forever_ ⋅ 2016/06/04 ⋅ 0

[从C到C++] 1.7 C++ 强制类型转换

[toc] 在C++语言中新增了四个关键字staticcast、constcast、reinterpretcast和dynamiccast。这四个关键字都是用于强制类型转换的。我们逐一来介绍这四个关键字。 1) static_cast 在C++语言中...

wu_being ⋅ 06/16 ⋅ 0

c/c++内存机制(一)(原)

一:C语言中的内存机制 在C语言中,内存主要分为如下5个存储区: (1)栈(Stack):位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。 (2)堆(Heap):由...

xumaojun ⋅ 04/03 ⋅ 0

C/C++语言void及void指针

1.概述 许多初学者对C/C++语言中的void及void指针类型不甚理解,因此在使用上出现了一些错误。本文将对void关键字的深刻含义进行解说,并详述void及void指针类型的使用方法与技巧。 2.void的...

技术小阿哥 ⋅ 2017/11/26 ⋅ 0

计算机专业C语言编程学习重点:指针化难为易

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界 ⋅ 05/31 ⋅ 0

C语言编程新手入门基础知识学习:程序注释

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界 ⋅ 05/15 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

对于程序员的招聘问题,作为软件人的一些吐槽和建议

作为软件人,找工作有时候似乎挺苦逼的。 说真的,让我去掉前面这句中“似乎”二字吧。就是苦逼!很多人都曾抱怨处在招聘的一方很糟糕——我们没有任何可靠的方式来甄别会写代码并且写得好的...

老道士 ⋅ 28分钟前 ⋅ 0

HDFS原理学习

一、概述 1、 Hadoop整合了众多的文件系统,首先提供了一个高层的文件系统抽象org.apache.hadoop.fs.FileSystem。然后有各个文件系统的实现类。 2、Hadoop是JAVA编写的,不同文件系统之间的交...

cjxcloud ⋅ 32分钟前 ⋅ 0

Linux下MySQL表名不区分大小写的设置方法(抄袭别人的)

Linux下MySQL表名不区分大小写的设置方法 MySQL表名不区分大小写的设置方法 在用centox安装mysql后,把项目的数据库移植了过去,发现一些表的数据查不到,排查了一下问题,最后发现是表名的大...

随风而浮沉 ⋅ 37分钟前 ⋅ 0

ubuntu下安装宋体simsun

sudo cp simsun.ttc /usr/share/fonts cd /usr/share/fonts sudo chmod 644 simsun.ttc 更新字体缓存: 代码: sudo mkfontscale 代码: sudo mkfontdir 代码: sudo fc-cache -fsv 安装chrome扩......

wangxuwei ⋅ 39分钟前 ⋅ 0

利用 ssh 传输文件

Linux 下一般可以用 scp 命令通过 ssh 传送文件: #把服务器上的 /home/user/a.txt 发送到本机的 /var/www/local_dir 目录下scp username@servername:/home/user/a.txt /var/www/local_dir...

大灰狼时间 ⋅ 49分钟前 ⋅ 0

web3j教程:android和java程序员如何使用web3j开发区块链以太坊

如何使用web3j为Java应用或Android App增加以太坊区块链支持,本教程内容即涉及以太坊中的核心概念,例如账户管理包括账户的创建、钱包创建、交易转账,交易与状态、智能合约开发与交互、过滤...

智能合约 ⋅ 今天 ⋅ 0

web3j开发java或android以太坊智能合约快速入门

web3j简介 web3j是一个轻量级、高度模块化、响应式、类型安全的Java和Android类库提供丰富API,用于处理以太坊智能合约及与以太坊网络上的客户端(节点)进行集成。 可以通过它进行以太坊区块链...

笔阁 ⋅ 今天 ⋅ 0

一起读书《深入浅出nodejs》-异步I/O

异步I/O “异步”这个名词其实很早就诞生了,但它大规模流行却是在Web 2.0浪潮中,它伴随着AJAX的第一个A(Asynchronous)席卷了Web。 为什么要异步I/O 关于异步I/O为何在Node里如此重要,这与...

小草先森 ⋅ 今天 ⋅ 0

JVM各种问题

1、如果启动什么都不设,会怎样? 先来看一个命令 [root@localhost bin]# java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=29899008 -XX:MaxHeapSize=478384128 -XX:+PrintCo......

算法之名 ⋅ 今天 ⋅ 0

SAS笔记-宏2

宏是一种文本,一般来说其编译是在程序执行之前。 宏变量的创建 %let语句 %let macro_variables = text; %let是常见的宏变量建立方式,其编译就在执行前。如下例中,想要宏变量test等于数据集...

tonorth123 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部