预计阅读本页时间:-
在很大程度上,C++就是C的超集,这意味着合法的C程序也是合法的C++程序。C++与C之间的主要不同在于C++支持很多附加特性。但是也有一些领域中C++规则与C有稍微的不同。这些不同使得C程序被作为C++程序编译时可能会以不同的方式工作或根本就不能工作。本附录讨论了这些不同。如果您使用C++而不是C编译器编译您的程序,就需要知道这些不同。尽管这些不同对本书中的例子影响很小,但是如果C代码作为C++程序进行编译的话,在有些例子中这些不同就会导致产生错误消息。
C99标准的发布使得问题更加复杂,因为有些情况下它使得C更接近于C++。例如,它允许在代码中随时进行声明,也可以识别注释指示符//。在其他方面,C99增加了与C++的差异,例如添加了变长数组和关键字restrict。C99依然还处于它的成长期,我们要面对C90和C99、C90和C++以及C99和C++之间的不同。但是C99最后要完全取代C90,本节要面对未来,所以只讨论C99与C++之间的一些不同。
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
B.9.1 函数原型
函数原型在C++中是必需的,但是在C中它是可选的。如果在声明一个函数时使圆括号为空,就可以看出这个不同。在C中,空圆括号意味着前向原型声明,但是在C++中则意味着函数没有原型。也就是说,在C++中,原型:
与下面的相同:
例如,以下的语句在C的旧风格中是可以接受的,但是在C++中就会产生一个错误:
在C中,编译器假定您在声明函数时使用的是旧的形式。在C++中,编译器假定slice()等于slice(void),并且您未能在使用slice(int, int)函数之前声明它。
C++也允许声明函数名相同的多个函数,只要它们具有不同的参数列表。
B.9.2 char常量
char常量在C中被作为int类型看待,而在C++中被作为char类型。例如,考虑下面的语句:
在C中,常量‘A’被存储在一块大小与int相同的内存中,更准确地说,它的字符编码存储为一个int值。相同的数值也被存储在变量ch中,但是在ch中它只占据内存中的一个字节。
另一方面,C++为‘A’和ch都使用一个字节。其中的不同并不影响本书中的任何例子。但是有些C程序使用字符符号来表示整数值,这样就把char常量作为int类型来使用。例如,如果系统中的int为4个字节,就可以在C中这样做:
‘ABCD’意味着一个4字节的int值,其中第一个字节存储字母A的字符编码,第二个字节存储B的字母编码,等等。注意‘ABCD’与“ABCD”有很大的不同。前者只是书写int值的一种方式,而后者是一个字符串,它对应于内存中5个字节的内存块的地址。
考虑下列代码:
在我们的系统上,它产生以下输出:
这个例子说明,如果把‘ABCD’看作一个int值,它就是一个4字节的整数值;但是如果把它看作char类型,程序就只使用最后一个字节。尝试使用%s说明符打印‘ABCD’会使程序崩溃,因为‘ABCD’(1094861636)的值是一个越界的地址。
使用像‘ABCD’这样的值是因为它提供了一种方式来单独设置int中的每个字节,因为每个字符都对应于一个字节。但是因为它要依赖于特定的字符码,而每两位十六进制数对应于一个字节,所以更好的方法是对整数常量使用十六进制值。第15章“位操作”讨论了这种技术(C的早期版本不提供十六进制符号,这可能也是多字符的字符常量技术首先得到发展的原因)。
B.9.3 const修饰符
在C中,全局const具有外部链接,但是在C++中它具有内部链接。也就是说C++中的声明:
就相当于C中的声明:
前提是这两个声明都在所有函数的外部。C++规则的意图是使得在头文件中使用const更加简单。如果常量是内部链接的,每个包含头文件的文件都会得到该常量的一份拷贝。如果常量是外部链接的,那么就必须在一个文件中进行定义声明,而在其他文件中使用关键字extern进行引用声明。
顺便说一句,C++可以使用关键字extern来使一个const值具有外部链接,所以两种语言都可以创建具有内部链接的常量和具有外部链接的常量。它们的不同只是在于默认使用哪种链接。
C++中const的一个附加属性是可以使用它来声明普通数组的大小:
可以在C99中使用相同的声明,但是在C99中这样的声明会创建一个变长数组。
在C++中,你可以使用const values来初始化const值,但在C中不可以:
B.9.4 结构和联合
在您声明了一个带有标记的结构或联合之后,就可以在C++中使用这个标记作为类型名:
结果是结构名可能与变量名相冲突。例如,以下的程序可以作为C程序进行编译,但是作为C++程序编译时会失败,因为C++把printf()语句中的duo解释为结构类型而不是外部变量:
在C和C++中都可以在一个结构内部声明另一个结构:
在C中,随后可以使用任何一个结构,但是在C++中使用嵌套结构时要求一个特殊的符号:
B.9.5 枚举
在枚举的使用中,C++比C更加严格。使用enum变量可以做的惟一有用的事是为它赋一个enum常量然后与其他值进行比较。不经过显式的类型转换,就不能把int值赋给enum变量,而且也不能递增一个enum变量。下列代码说明了这一点:
C++也允许在声明一个枚举变量时不用关键字enum:
与结构和联合的情况类似,如果一个变量和enum类型具有相同的名字就会引起冲突。
B.9.6 指向void的指针
与在C中相同,在C++中可以把任意类型的指针赋值给指向void的指针。但是与C中不同的是,除非使用了显式的类型转换,否则不能把指向void的指针赋值给其他类型的指针。下列代码说明了这一点:
C++中的另一个不同是可以把派生类对象的地址赋值给基类指针,但是这里涉及到的特性在C中不存在。
B.9.7 布尔类型
C++中的布尔类型是bool,并且true和false都是关键字。在C中,布尔类型是_Bool,但是包含了stdbool.h头文件就可以使用bool、true和false。
B.9.8 可选的拼写
C++中可以使用or来代替II,还有其他一些可选拼写,它们都是关键字。在C99中它们被定义为宏,需要包含iso646.h头文件才能使用它们。
B.9.9 宽字符支持
在C++中,wchar_t是一种内建的类型,并且wchar_t是一个关键字。在C99中,wchar_t类型是在一些头文件中进行定义的(stddef.h、stdlib.h、wchar.h和wctype.h)。
C++通过iostream头文件提供宽字符的I/O支持,而C99通过wchar.h头文件提供了一种完全不同的I/O支持包。C99也支持多字节字符及其与宽字符之间的转换,而C++不支持。
B.9.10 复数类型
C++通过在complex头文件中提供一个复数类来支持复数类型。C具有内建的复数类型并通过complex.h头文件来支持它们。这两种方法差别很大,它们是不兼容的。C版本更关心数值计算的需要和惯例。
B.9.11 内联函数
C99中添加了对内联函数的支持,这是C++已经支持的特性。但是,C99中的实现更加灵活。在C++中,内联函数默认是内部链接的。如果C++中的一个内联函数在多个文件中出现,它就必须具有相同的定义,使用相同的语言符号。例如,不允许在一个文件中的定义里使用int参量而在另一个文件中的定义里使用int32_t参量,即使使用typedef把int32_t定义为int也不能这样做。但是C中就允许这样的定义。而且像第15章中描述的那样,C也允许混合使用函数的内联和外部定义,而C++不允许。
B.9.12 C++中没有的C99特性
尽管传统上C或多或少可以看作是C++的子集,但是C99标准添加了一些C++中没有的特性。这里列出一些只存在于C99中的特性:
● 指定初始化项目;
● 复合初始化项目;
● 受限指针;
● 变长数组;
● 伸缩型数组成员;
● long long和unsigned long long类型;
● 可移植的整数类型(inttypes.h和stdint.h);
● 通用字符名;
● 附加的数学库函数;
● 通过fenv.h访问浮点数环境;
● 预定义的标识符,例如__func__;
● 具有可变数目参数的宏。
其中的一些,例如long long类型,可能会成为常用C++的扩展,还有一些可能会被加入到C++标准的下一版本中。
C运算符
运算符(优先级从高到低) | 结合性 |
---|---|
++(后缀)--(后缀)()(调用函数)[ ]{ }(组合文字).→ | 从左到右 |
++(前缀)--(前缀)-+~! sizeof *(取值)&(地址)(type)(都是一元运算符) | 从右到左 |
(type name) | 从左到右 |
*/% | 从左到右 |
+ -(二者都是二元运算符) | 从左到右 |
« » | 从左到右 |
< ><=> = | 从左到右 |
= = != | 从左到右 |
& | 从左到右 |
^ | 从左到右 |
| | 从左到右 |
&& | 从左到右 |
| | | 从左到右 |
?:(条件表达式) | 从右到左 |
=*= /= %= += - = «= » = &=|=^= | 从右到左 |
,(逗号运算符) | 从左到右 |