C和c++有很多不同之处,并不是所有有效的C代码都是有效的c++代码。
(这里的“有效”指的是具有定义行为的标准代码,即不是特定于实现的/未定义的/等等。)
在哪种情况下,一段在C和c++中都有效的代码在使用每种语言的标准编译器编译时会产生不同的行为?
为了做一个合理/有用的比较(我试图学习一些实际有用的东西,而不是试图在问题中找到明显的漏洞),让我们假设:
与预处理器无关(这意味着没有使用#ifdef __cplusplus、pragmas等进行hack)
在这两种语言中,任何实现定义都是相同的(例如数字限制等)。
我们比较每个标准的最新版本(例如,c++ 98和C90或更高版本)
如果版本很重要,那么请说明每个版本会产生不同的行为。
对于C++11标准:
a.逗号操作符在C中执行左值到右值的转换,但在c++中不执行:
char arr[100];
int s = sizeof(0, arr); // The comma operator is used.
在c++中,这个表达式的值是100,在C中是sizeof(char*)。
b.在c++中,枚举器的类型是它的枚举。在C语言中,枚举数的类型是int。
enum E { a, b, c };
sizeof(a) == sizeof(int); // In C
sizeof(a) == sizeof(E); // In C++
这意味着sizeof(int)可能不等于sizeof(E)。
c.在c++中,用空参数列表声明的函数不带参数。在C语言中,空参数列表意味着函数参数的数量和类型是未知的。
int f(); // int f(void) in C++
// int f(*unknown*) in C
不要忘记C和c++全局名称空间之间的区别。假设你有一个foo。cpp
#include <cstdio>
void foo(int r)
{
printf("I am C++\n");
}
和foo2.c
#include <stdio.h>
void foo(int r)
{
printf("I am C\n");
}
现在假设你有一个main.c和main.cpp,它们看起来都是这样的:
extern void foo(int);
int main(void)
{
foo(1);
return 0;
}
当编译为c++时,它将使用c++全局命名空间中的符号;在C中,它将使用C:
$ diff main.cpp main.c
$ gcc -o test main.cpp foo.cpp foo2.c
$ ./test
I am C++
$ gcc -o test main.c foo.cpp foo2.c
$ ./test
I am C
int main(void) {
const int dim = 5;
int array[dim];
}
这是相当奇怪的,因为它在c++和C99, C11和C17中是有效的(尽管在C11, C17中是可选的);但在C89中无效。
在C99+中,它创建了一个变长数组,与普通数组相比,它有自己的特性,因为它是运行时类型而不是编译时类型,并且sizeof数组在C中不是整数常量表达式。在c++中,该类型完全是静态的。
如果你尝试在这里添加初始化式:
int main(void) {
const int dim = 5;
int array[dim] = {0};
}
是有效的c++,但不是C,因为变长数组不能有初始化式。
对于c++和C90,至少有一种方法可以获得不同的行为,而不是实现定义的。C90没有单行注释。只要稍加注意,我们就可以用它来创建一个在C90和c++中具有完全不同结果的表达式。
int a = 10 //* comment */ 2
+ 3;
在c++中,从//到行尾的所有内容都是注释,所以这是:
int a = 10 + 3;
因为C90没有单行注释,所以只有/*注释*/是注释。第一个/和2都是初始化的一部分,所以结果是:
int a = 10 / 2 + 3;
因此,正确的c++编译器会给出13,而严格正确的C90编译器会给出8。当然,这里我只是随便选了几个数字,你也可以用其他合适的数字。