C和c++有很多不同之处,并不是所有有效的C代码都是有效的c++代码。 (这里的“有效”指的是具有定义行为的标准代码,即不是特定于实现的/未定义的/等等。)

在哪种情况下,一段在C和c++中都有效的代码在使用每种语言的标准编译器编译时会产生不同的行为?

为了做一个合理/有用的比较(我试图学习一些实际有用的东西,而不是试图在问题中找到明显的漏洞),让我们假设:

与预处理器无关(这意味着没有使用#ifdef __cplusplus、pragmas等进行hack) 在这两种语言中,任何实现定义都是相同的(例如数字限制等)。 我们比较每个标准的最新版本(例如,c++ 98和C90或更高版本) 如果版本很重要,那么请说明每个版本会产生不同的行为。


#include <stdio.h>

int main(void)
{
    printf("%d\n", (int)sizeof('a'));
    return 0;
}

在C语言中,这将打印当前系统中sizeof(int)的值,在目前使用的大多数系统中,这通常是4。

在c++中,这必须打印1。


一个依赖于C编译器的老栗子,不识别c++的行尾注释……

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...

c++编程语言(第三版)给出了三个例子:

sizeof('a'),正如@Adam Rosenfield提到的; //注释被用来创建隐藏代码: Int f(Int a, Int b) { 返回a //* blah */ b ; } 结构等隐藏的东西在外部作用域,如在你的例子。


struct abort
{
    int x;
};

int main()
{
    abort();
    return 0;
}

在c++中返回退出码为0,在C中返回退出码为3。

这个技巧可能可以用来做一些更有趣的事情,但我想不出一个好方法来创建一个构造函数,让c满意。我试着用复制构造函数做了一个同样无聊的例子,让一个参数被传递,尽管以一种相当不可移植的方式:

struct exit
{
    int x;
};

int main()
{
    struct exit code;
    code.x=1;

    exit(code);

    return 0;
}

vc++ 2005拒绝在c++模式下编译,但是,抱怨如何重新定义“退出代码”。(我认为这是一个编译器错误,除非我突然忘记了如何编程。)但是当它被编译成C语言时,进程退出码为1。


c++标准中列出的另一个:

#include <stdio.h>

int x[1];
int main(void) {
    struct x { int a[2]; };
    /* size of the array in C */
    /* size of the struct in C++ */
    printf("%d\n", (int)sizeof(x)); 
}

以下语句在C和c++中有效,(很可能)在C和c++中导致i的值不同:

int i = sizeof('a');

请参阅C/ c++中的字符大小('a')以了解差异的解释。

这篇文章中的另一个:

#include <stdio.h>

int  sz = 80;

int main(void)
{
    struct sz { char c; };

    int val = sizeof(sz);      // sizeof(int) in C,
                               // sizeof(struct sz) in C++
    printf("%d\n", val);
    return 0;
}

下面是一个例子,它利用了C和c++中函数调用和对象声明之间的差异,以及C90允许调用未声明的函数的事实:

#include <stdio.h>

struct f { int x; };

int main() {
    f();
}

int f() {
    return printf("hello");
}

在c++中,这不会打印任何东西,因为创建和销毁了一个临时f,但在C90中,它会打印hello,因为函数可以在没有声明的情况下被调用。

如果你想知道名称f被使用了两次,C和c++标准明确地允许这样做,并且要创建一个对象,如果你想要结构,你必须说struct f来消除歧义,或者如果你想要函数,就不要struct。


对于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

C90 vs. c++ 11 (int vs. double)

#include <stdio.h>

int main()
{
  auto j = 1.5;
  printf("%d", (int)sizeof(j));
  return 0;
}

在C语言中,auto表示局部变量。在C90中,可以省略变量或函数类型。它默认为int。在c++ 11中,auto的意思完全不同,它告诉编译器从用于初始化变量的值推断变量的类型。


对于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。当然,这里我只是随便选了几个数字,你也可以用其他合适的数字。


C中的内联函数默认为外部作用域,而c++中的内联函数则不是。

在GNU C中,将下面两个文件编译在一起将打印“I am inline”,而在c++中则没有。

文件1

#include <stdio.h>

struct fun{};

int main()
{
    fun();  // In C, this calls the inline function from file 2 where as in C++
            // this would create a variable of struct fun
    return 0;
}

文件2

#include <stdio.h>
inline void fun(void)
{
    printf("I am inline\n");
} 

此外,c++隐式地将任何const全局变量视为静态的,除非显式地声明了extern,不像C中extern是默认值。


另一个sizeof陷阱:布尔表达式。

#include <stdio.h>
int main() {
    printf("%d\n", (int)sizeof !0);
}

它在C中等于sizeof(int),因为表达式是int类型,但在c++中通常是1(尽管它不是必需的)。实际上,它们几乎总是不同的。


另一个例子,我还没有看到提到,这个突出了预处理器的差异:

#include <stdio.h>
int main()
{
#if true
    printf("true!\n");
#else
    printf("false!\n");
#endif
    return 0;
}

这在C中打印“false”,在c++中打印“true”——在C中,任何未定义的宏的计算结果为0。在c++中,有一个例外:"true"的值为1。


这个程序在c++中输出1,在C中输出0:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int d = (int)(abs(0.6) + 0.5);
    printf("%d", d);
    return 0;
}

这是因为在c++中有双abs(double)重载,所以abs(0.6)返回0.6,而在C中它返回0,因为在调用int abs(int)之前,它是隐式的双到整型转换。在C语言中,你必须使用fab与double一起工作。


不要忘记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

#include <stdio.h>

struct A {
    double a[32];
};

int main() {
    struct B {
        struct A {
            short a, b;
        } a;
    };
    printf("%d\n", sizeof(struct A));
    return 0;
}

这个程序在使用c++编译器编译时输出128 (32 * sizeof(double)),在使用C编译器编译时输出4。

这是因为C语言没有范围解析的概念。在C语言中,包含在其他结构中的结构被放到外部结构的范围内。


这涉及到C和c++中的左值和右值。

在C编程语言中,加前和加后操作符都返回右值,而不是左值。这意味着它们不能在=赋值操作符的左边。这两个语句都会在C语言中产生编译器错误:

int a = 5;
a++ = 2;  /* error: lvalue required as left operand of assignment */
++a = 2;  /* error: lvalue required as left operand of assignment */

然而,在c++中,前自增操作符返回左值,而后自增操作符返回右值。这意味着带有预增量操作符的表达式可以放在=赋值操作符的左侧!

int a = 5;
a++ = 2;  // error: lvalue required as left operand of assignment
++a = 2;  // No error: a gets assigned to 2!

为什么会这样呢?后增量函数对变量进行递增,并返回该变量在递增发生之前的状态。这实际上就是一个右值。变量a的前一个值作为临时值复制到寄存器中,然后对a进行递增。但是a的前一个值是由表达式返回的,它是一个右值。它不再表示变量的当前内容。

The pre-increment first increments the variable, and then it returns the variable as it became after the increment happened. In this case, we do not need to store the old value of the variable into a temporary register. We just retrieve the new value of the variable after it has been incremented. So the pre-increment returns an lvalue, it returns the variable a itself. We can use assign this lvalue to something else, it is like the following statement. This is an implicit conversion of lvalue into rvalue.

int x = a;
int x = ++a;

由于预增量返回一个左值,我们也可以给它赋值。下面两种说法是相同的。在第二次赋值中,首先对a进行递增,然后用2覆盖它的新值。

int a;
a = 2;
++a = 2;  // Valid in C++.

空结构体的大小在C中为0,在c++中为1:

#include <stdio.h>

typedef struct {} Foo;

int main()
{
    printf("%zd\n", sizeof(Foo));
    return 0;
}

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,因为变长数组不能有初始化式。