神刀安全网

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

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++ 中并不一样, 比如下面这段代码:

void foo() {     const int N = 100;     int arr[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 之后, 简直酸爽。 那么, 你知道吗, 下面这段代码:

void foo() {     auto a = 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 语言的输出和机器有关, 很可能是 4sizeof(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>   int main() {     printf("%zu  %zu/n", sizeof(bool), sizeof(true));     return 0; } 

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

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

延伸阅读:

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 一些容易混淆的 C 和 C++ 的不兼容特性

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址