一些容易混淆的 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++ 了。 而第三类, 是本文的重点, 因为这类特性是最容易混淆的。本人从事在线教育多年,将自己的资料整合建了一个QQ群,有兴趣一起交流学习c/c++的小伙伴可以加群:941636044,里面有大神会给予解答,也会有许多的资源可以供大家学习分享,欢迎大家前来一起学习进步!
以下讨论仅针对标准的 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 语言的输出和机器有关, 很可能是 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>
int main() {
printf("%zu %zu\n", sizeof(bool), sizeof(true));
return 0;
}
很可能会发现两者大小又不一致了。 那是因为, 即使有了标准的布尔类型, true 和 false仍然只是整形常量 1 和 0, 而不像 C++ 中是真正的 bool 类型常量。
本人从事在线教育多年,将自己的资料整合建了一个QQ群,有兴趣一起交流学习c/c++的小伙伴可以加群:941636044,里面有大神会给予解答,也会有许多的资源可以供大家学习分享,欢迎大家前来一起学习进步!
C 和 C++ 还有其他一些区别, 比如 const 全局变量的作用范围, inline 函数的定义范围等等。 因为相对不容易弄错, 这里就不展开了, 你还能想到什么特性可以加到这个名单之中呢?