一些容易混淆的 C 和 C++ 的不兼容特性

来源:伯乐在线专栏作者 - Yu_Hao

链接:http://blog.jobbole.com/102632/


C 和 C++ 是两种不同的编程语言, 特别的, C 并不是 C++ 的子集。 但二者又高度相关。 C++ 自诞生以来, 一直以能够兼容C作为自己的目标之一。 在两种语言的不断演化中, C 和 C++ 都互相从对方身上吸收了不少内容。 举个例子, C99 标准开始支持 C++ 风格的//注释, C++11 标准支持 C99 的 long long 整型, 等等。

C 和 C++ 的不兼容大致可以分为三个方面:

  1. C++ 支持而 C 不支持的功能

  2. C 支持而 C++ 不支持的功能

  3. C 和 C++ 都支持, 但语法/语意细节不同的功能

第一类非常多, 比如各类 OOP 功能, template 功能。 第二类在 C99 推出时也有不少, 但随着 C++11 的推出, 很多 C99 引入的特性也被加入 C++ 了。 而第三类, 是本文的重点, 因为这类特性是最容易混淆的。  

以下讨论仅针对标准的 C/C++, 不包括各种编译器扩展。


const修饰符

C 和 C++ 都有一个重要的概念, 叫做常量表达式(constant expression), 特点是可以在编译时就得到值, 而不需要运行时。 有些语法要求只能使用常量表达式, 比如数组的长度, case 语句的表达式, 等等。

那么, const 变量是否可以用作常量表达式呢? 答案在 C 和 C++ 中并不一样, 比如下面这段代码:


voidfoo(){

    constintN = 100;

    intarr[N];

}


在 C++ 中, 这段代码是合法的, 因为 N 可以当做常量 100 一样使用。 (在 C++11 中, 这里还可以用 constexpr)。 但在 C89 中, 这段代码是非法的, 因为即使变量声明为const, 它仍然不是常量表达式。  

但可能有人会问, 我试过这段代码, 可以编译的啊。 那是因为, C99中支持可变长度数组(variable length array, 经常缩写为VLA), arr 这里被解析为一个VLA, 所以虽然这段代码在 C99 中变成合法的, 但 arr 仍然不是一个普通的(固定长度的)数组, 因为 N 仍然不是常量表达式。


void *指针

void *指针在 C 语言中用作通用指针。 C++ 虽然仍然支持它, 但由于有更强大的泛型编程, void *的用处要少很多。

作为通用指针, void * 可以和其他任意类型的指针相互转换, 但要注意, C 语言中这种类型转换是隐式的(implicit conversion), 而在 C++ 中必须有显式的类型转换(explicit conversion)。  


看下面的代码:


void *ptr;

int *a = ptr;

int *b = (int *)ptr;


指针a的初始化在 C 语言中是合法的, 而在 C++ 中是非法的。 指针 b 的初始化在 C/C++ 中都是合法的。

这也是 C++ 比 C 的类型系统更强的一个例子。  

思考题: malloc的返回值需要做类型转换吗? 也就是说:


int *x = malloc(sizeof(*x));

int *y = (int *)malloc(sizeof(*y));


应该用哪种呢?  


auto 关键字

C++11 引入的 auto 关键字真是喜大普奔, 尤其是 STL 的迭代器类型, 改用 auto 之后, 简直酸爽。 那么, 你知道吗, 下面这段代码:


voidfoo(){

    autoa = 42;

}


在 C89 下也是可以编译成功的。 是不是 C 语言也支持 auto 呢?  

原来, auto 关键字在 C 语言中早就存在, 它用来修饰变量, 表示变量拥有自动存储 (automatic storage), 和静态存储相反。 但是呢, 在函数内, 静态存储的变量需要用 static 关键字修饰, 其他变量默认都是自动存储的, 所以 auto 这个关键字不用也可以, 结果就是,实际中基本没有人会用它。 而 C++11 里, 把 auto 关键字赋予了新的功能, 算是老树焕发了新春。

所以上面的代码在 C 语言中, 相当于 a = 42;, 而在 C89 中, 由于有隐含的 int 类型, 也等同于 int a = 42;。 注意在 C99 中, 隐含的 int 类型已经不再合法了。


一些基本类型  

下面代码的输出是什么?


printf("%zu\n",sizeof('a'));


你可能猜到了, C 和 C++ 的答案不一样。 C++ 的输出为 1, 而 C 语言的输出和机器有关, 很可能是 4。 sizeof(char) 的结果在两种语言中是一致的, 按照定义, 其值为 1。 区别在于字符常量的类型。 C++ 语言的字符常量, 如 'a', 类型是 char, 而 C 语言中其类型为 int。  

另一个基本类型 bool,由于 C 语言很长时间以来是不提供直接支持的, 很多 C 代码采用了 #define 1 TRUE 之类的定义来模拟布尔类型。 但是实际上, C99 已经提供了标准的布尔类型, 为了兼容老代码, 这个类型名称选择了 _Bool, 但在头文件 stdbool.h 中, 提供了别名 bool 和宏 true, false 来方便大家使用。 不过呢, 如果你运行下面的 C 代码:


#include <stdio.h>

#include <stdbool.h>

 

intmain(){

    printf("%zu  %zu\n",sizeof(bool),sizeof(true));

    return0;

}


很可能会发现两者大小又不一致了。 那是因为, 即使有了标准的布尔类型, true 和 false 仍然只是整形常量 1 和 0, 而不像 C++ 中是真正的 bool 类型常量。  

C 和 C++ 还有其他一些区别, 比如 const 全局变量的作用范围, inline 函数的定义范围等等。 因为相对不容易弄错, 这里就不展开了, 你还能想到什么特性可以加到这个名单之中呢?  


延伸阅读:


  • Do I cast the result of malloc?

  • Why does auto a=1; compile in C?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值