C与C++兼容性全解析:从差异到应对策略
立即解锁
发布时间: 2025-08-22 00:47:49 阅读量: 2 订阅数: 16 


C++编程语言精髓与实践
### C与C++兼容性全解析:从差异到应对策略
#### 1. 引言
在编程领域,C和C++是两门广泛使用的语言。然而,它们之间存在着一些兼容性问题,这些问题可能会给程序员带来困扰。本文将深入探讨C与C++之间的不兼容性,以及标准C++与早期版本C++之间的差异。目的在于记录那些可能导致问题的差异,并提供解决这些问题的方法。
大多数兼容性问题出现在以下几种情况:将C程序升级为C++程序、将C++程序从一个预标准版本移植到另一个版本,或者使用旧编译器编译具有现代特性的C++代码。我们的目标不是让你陷入每个兼容性问题的细节中,而是列出最常出现的问题,并给出标准的解决方案。
在考虑兼容性问题时,一个关键问题是程序需要在哪些实现环境下运行。对于学习C++,使用最完整、最有用的实现是有意义的。而对于交付产品,可能需要采取更保守的策略,以确保产品能在更多系统上运行。过去,这曾是避免使用C++新特性的一个原因(有时只是借口)。但如今,各种实现逐渐趋同,跨平台的可移植性需求不再像几年前那样需要极端谨慎。
#### 2. C/C++兼容性
除了少数例外,C++是C(指ISO/IEC 9899:1990定义的C89)的超集。大多数差异源于C++更强调类型检查。编写良好的C程序通常也是C++程序,编译器可以诊断出C++和C之间的所有差异。
##### 2.1 “沉默”的差异
除了少数情况,既是C++又是C的程序在两种语言中的含义相同。幸运的是,这些“沉默的差异”相当隐晦:
- 在C中,字符常量和枚举的大小等于`sizeof(int)`。在C++中,`sizeof('a')`等于`sizeof(char)`,并且C++实现可以为枚举选择最合适的大小。
- C++提供`//`注释,而C没有(尽管许多C实现将其作为扩展提供)。这个差异可用于构造在两种语言中行为不同的程序,例如:
```cpp
int f(int a, int b)
{
return a /* pretty unlikely */ b
;
/* unrealistic: semicolon on separate line to avoid syntax error */
}
```
C99(指ISO/IEC 9899:1999(E)定义的C)也提供`//`注释。
- 在内层作用域中声明的结构体名称可以隐藏外层作用域中对象、函数、枚举器或类型的名称。例如:
```cpp
int x[99];
void f()
{
struct x { int a; };
sizeof(x); /* size of the array in C, size of the struct in C++ */
}
```
##### 2.2 非C++的C代码
导致实际问题的C/C++不兼容性并不微妙,大多数很容易被编译器捕获。以下是一些非C++的C代码示例,其中大多数在现代C中被认为是不良风格甚至过时的:
- 在C中,大多数函数可以在没有先前声明的情况下调用。例如:
```c
main()
/* poor style C. Not C++ */
{
double sq2 = sqrt(2);
/* call undeclared function */
printf("the square root of 2 is %g\n",sq2);
/* call undeclared function */
}
```
一般建议在C中完整且一致地使用函数声明(函数原型)。当遵循这个合理建议,特别是当C编译器提供强制使用的选项时,C代码就符合C++规则。当调用未声明的函数时,你需要对函数和C规则有很好的了解,才能知道是否犯了错误或引入了可移植性问题。例如,上面的`main()`作为C程序至少包含两个错误。
- 在C中,未指定任何参数类型声明的函数可以接受任意数量、任意类型的参数。这种用法在标准C中已过时,但并不少见:
```cpp
void f();
/* argument types not mentioned */
void g()
{
f(2);
/* poor style C. Not C++ */
}
```
- 在C中,函数可以使用一种语法定义,该语法可在参数列表后可选地指定参数类型:
```c
void f(a,p,c) char *p; char c; { /* ... */ }
/* C. Not C++ */
```
这种定义必须重写为:
```cpp
void f(int a, char* p, char c) { /* ... */ }
```
- 在C和预标准版本的C++中,类型说明符默认为`int`。例如:
```c
const a = 7;
/* In C, type int assumed. Not C++ */
```
C99和C++一样,禁止“隐式`int`”。
- C允许在返回类型和参数类型声明中定义`struct`。例如:
```c
struct S { int x,y; } f();
/* C. Not C++ */
void g(struct S { int x,y; } y);
/* C. Not C++ */
```
C++的类型定义规则使这种声明无用,因此不允许使用。
- 在C中,整数可以赋值给枚举类型的变量:
```cpp
enum Direction { up, down };
enum Direction d = 1;
/* error: int assigned to Direction; ok in C */
```
- C++比C提供了更多的关键字。如果这些关键字中的一个在C程序中作为标识符出现,则必须修改该程序才能使其成为C++程序。
C++中不是C关键字的关键字如下表所示:
| 关键字 | 说明 |
| --- | --- |
| `and` | 逻辑与 |
| `and_eq` | 逻辑与赋值 |
| `asm` | 内联汇编 |
| `bitand` | 按位与 |
| `bitor` | 按位或 |
| `bool` | 布尔类型 |
| `catch` | 异常捕获 |
| `class` | 类 |
| `compl` | 按位取反 |
| `const_cast` | 常量转换 |
| `delete` | 释放内存 |
| `dynamic_cast` | 动态转换 |
| `explicit` | 显式构造 |
| `export` | 导出模板 |
| `false` | 布尔假 |
| `friend` | 友元 |
| `inline` | 内联函数 |
| `mutable` | 可变成员 |
| `namespace` | 命名空间 |
| `new` | 动态分配内存 |
| `not` | 逻辑非 |
| `not_eq` | 逻辑非等于 |
| `operator` | 运算符重载 |
| `or` | 逻辑或 |
| `or_eq` | 逻辑或赋值 |
| `private` | 私有成员 |
| `protected` | 受保护成员 |
| `public` | 公共成员 |
| `reinterpret_cast` | 重新解释转换 |
| `static_cast` | 静态转换 |
| `template` | 模板 |
| `this` | 指向当前对象的指针 |
| `throw` | 抛出异常 |
| `true` | 布尔真 |
| `try` | 异常处理块 |
| `typeid` | 类型信息 |
| `typename` | 类型名 |
| `using` | 使用命名空间 |
| `virtual` | 虚函数 |
| `wchar_t` | 宽字符类型 |
| `xor` | 逻辑异或 |
| `xor_eq` | 逻辑异或赋值 |
在C中,一些C++关键字是标准头文件中定义的宏:
| 关键字 | 说明 |
| --- | --- |
| `and` | 逻辑与 |
| `and_eq` | 逻辑与赋值 |
| `bitand` | 按位与 |
| `bitor` | 按位或 |
| `bool` | 布尔类型 |
| `compl` | 按位取反 |
| `false` | 布尔假 |
| `not` | 逻辑非 |
| `not_eq` | 逻辑非等于 |
| `or` | 逻辑或 |
| `or_eq` | 逻辑或赋值 |
| `true` | 布尔真 |
| `wchar_t` | 宽字符类型 |
| `xor` | 逻辑异或 |
| `xor_eq` | 逻辑异或赋值 |
这意味着在C中可以使用`#ifdef`测试它们,也可以重新定义它们。
- 在C中,全局数据对象可以在单个翻译单元中多次声明,而无需使用`extern`说明符。只要最多只有一个这样的声明提供初始化器,该对象就被认为只定义了一次。例如:
```c
int i; int i;
/* defines or declares a single integer ‘i’; not C++ */
```
在C++中,一个实体必须恰好定义一次。
- 在C++中,类不能与在同一作用域中声明为引用不同类型的`typedef`具有相同的名称。
- 在C中,`void*`可以用作任何指针类型变量赋值或初始化的右操作数;在C++中则不可以。例如:
```cpp
void f(int n)
{
int* p = malloc(n*sizeof(int));
/* not C++. In C++, allocate using ‘new’ */
}
```
- C允许将控制转移到带标签的语句以绕过初始化;C++则不允许。
- 在C中,全局`const`默认具有外部链接;在C++中则没有,并且必须初始化,除非显式声明为`extern`。
- 在C中,嵌套结构体的名称与它们嵌套的结构体位于同一作用域中。例如:
```c
struct S {
struct T { /* ... */ };
// ...
};
struct T x;
/* ok in C meaning ‘S::T x;’. Not C++ */
```
- 在C中,数组可以由元素数量多于数组所需的初始化器初始化。例如:
```c
char v[5] = "Oscar";
/* ok in C, the terminating 0 is not used. Not C++ */
```
##### 2.3 已弃用的特性
标准委员会通过弃用某个特性,表达了希望该特性消失的愿望。然而,委员会没有权力移除大量使用的特性,无论它多么冗余或危险。因此,弃用是对用户的一个强烈提示,应避免使用该特性。
- 关键字`static`通常表示“静态分配”,可用于表示函数或对象对翻译单元是局部的。例如:
```cpp
// file1:
static int glob;
// file2:
static int glob;
```
这个程
0
0
复制全文
相关推荐









