C与C++的兼容性

✍ dations ◷ 2024-12-23 04:36:40 #C与C++的兼容性

C语言和C++的关系十分密切,但是也有许多显著的差异。C++标准起源于早期C标准, 并被设计为与当时的C语言在源代码编写和链接方面很大程度上兼容. 因此,两种语言的开发工具(例如IDE和编译器)通常被集成到单个产品中,程序员可以自己选择编写的是C还是C ++,开发工具通常会根据程序员的选择使用不同的编译器和链接器或不同的库。

即便如此,C并不是C++的子集, 一般的C语言代码不经修改很难被一些严格符合C++标准的C++编译器成功编译;同样,几乎所有的C++代码都无法被C语言编译器编译。 在这篇文章中,我们主要讨论的是它们在公共部分的差异,比如在C语言中合法的代码到了C++中成为了不合法的代码,或一段代码在C和C++中表现出不同的行为。

C++的创始人Bjarne Stroustrup建议 C和C++应该尽可能减小差异,以提高这两种语言的兼容性; 而另一些人则认为C和C++毕竟是两种不同的语言——虽然C++起源于C——因此它们之间的兼容性并不是那么重要。 而ANSI的看法:“我们赞同保持C与C++的最大公共子集原则”,同时“保持它们的差别,使这两种语言继续独立发展”,“......委员会希望C++成为重要的和强有力的语言”。

截止到C++20和C2x,C++不支持部分C语言特性,如变长数组,原生复数支持和restrict类型修饰符(英语:Type qualifier)。另一方面,与C89相比,C99通过合并C ++功能(例如//注释,允许声明出现在代码中而不只是在函数头)减少了一些其他不兼容性。

C++较C语言有更严格的类型转换和初始化规则 , 因此一些在C语言里合法的语句在C++里是不合法的。ISO C++附录C.1列出了这些区别。

void *ptr;/*从void *到int *的隐式转换*/int *i = ptr;
或类似的:
int *j = malloc(5 * sizeof *j);     /*从void *到int *的隐式转换 */
为了使代码在C和C++中同时可用, 必须使用强制类型转换, 如下所示 (然而这样在两种语言中都会报警告):
void *ptr;int *i = (int *)ptr;int *j = (int *)malloc(5 * sizeof *j);
C++中提供了其它方法来实现指针类型转换。C++不推荐继续使用老式类型转换。老式类型转换在形式上来说不是特别清晰,容易被忽略。。
void *ptr;auto i = reinterpret_cast<int *>(ptr);auto j = new int;
  • C 允许在安全的前提下隐式添加除const和volatile的关键字,而C++则不会。
  • C++为C语言的部分函数添加了一个参数带const的重载,例如strchr在C语言中的声明为char *strchr(char *),而在C++中则有一个重载函数为const char *strchr(const char *)
  • C++在枚举方面也更加严格。在C++中,int不能被隐式转换为枚举类型。因为在C++标准中并没有规定枚举的类型为int(在C语言标准中则规定了),并且大小可能与C++中int的大小不同。从C++11开始,C++允许程序员将自定义的整数类型赋值给枚举类型。
  • 在C++中,const变量必须被初始化,但在C语言中不必。也就是说,下面这个例子:
    const int var1;const int var2 = 1;
    第一行代码在C语言中是允许的(在某些编译器中可能会报告一个“Warning”),但在C++中会报错;第二行代码在C和C++中都是合法的。
  • C++不允许goto和标签之间出现初始化语句。如下示例所示,该段代码在C语言中是允许的,但在C++中不允许。
    void fn(void){    goto flack;    int i = 1;flack:    ;}
  • 虽然语法上有效,但如果跳过的堆栈帧包含具有非平凡(nontrivial)析构函数的对象(即指针),则longjmp()会在C ++中导致未定义的行为。 C++实现可以自由定义此时的行为,以便能够调用析构函数。但是,这样会使longjump()的一些用法失效, 否则就可以通过在单独的调用堆栈之间进行longjmp来实现线程或协程——在全局地址空间中从较低的调用堆栈跳转到较高的调用堆栈时,将析构函数用于较低调用堆栈中的每个对象。这个问题在C语言中不存在。
  • 在C语言中允许使用与用struct,enumunion类型名称相同的名称定义新类型。
    enum BOOL {FALSE, TRUE};typedef int BOOL;//在C语言中允许,在C++中不允许
  • 在C语言中,由struct,enumunion创建的类型在声明变量时必须在自定义的类型前加struct,enumunion(取决于定义时的类型),而在C++中不必。因为自定义类型在创建时已隐含typedef(这也是上一点区别形成的原因)。
    struct foo{        int bar;};/*在C++中,以上形式被隐式转换为:typedef struct foo{        int bar;} foo;*/struct foo foobar;//在C与C++中均可foo foobar2; //在C语言中不允许,在C++中允许
  • 在C++中不允许使用旧式K&R声明(如下所示),这种声明在C语言中仍然可用,但是标准已将这种声明方式列为“过时”(“过时”是在ISO C标准中定义的术语,它表示委员会可以考虑在未来的标准中将其删除)。
    int add(a,b)int a;int b;{    return a+b;}
  • 在C99标准出现之前,C语言允许隐式函数声明(即省略函数声明),而C++不允许;但在C99标准出现及以后,C与C++均不允许使用这种形式。
  • 在C语言中,如果函数原型中没有参数(如int foo();,表明该函数的参数不确定(这是一种标准不推荐的用法);而在C++中相同的形式等同于int foo(void);。如果想要在C语言中表示没有参数,应该使用int foo(void);
  • 在C和C++中,都可以嵌套定义struct类型,如下所示:
    struct foo{    struct bar{        int x;    };    int y;}
    然而,在使用时,C和C++采用不同的方法,假设此时已有struct foo类型变量a, 且x = 1,y = 2;若要将x赋值给变量b,c:
    //以下是C语言方法,在C++中不允许:int b = a.x;//以下是C++特有方法,在C中不允许:int c = a::x
  • C99和C11添加了一些未包含在标准C ++中的附加功能,例如复数,变长数组(值得注意的是,在C99中列为强制要求的VLA(变长数组)和复数在C11中被列为了可选项,这是由于部分特殊平台的C编译器无法有效率地实现或无法实现这两个功能导致的。目前基本所有平台都有对应的可使用VLA的编译器),柔性数组(也叫伸缩型数组成员),restrict关键字,数组参数限定词,复合文字,指定初始化项目,可变参数宏,附加数学库,预定义的标识符(如__func__)和可移植整数类型。
  • C99中通过内建的关键字_Complex和由它定义的宏complex实现了虚数类型——float complexdouble complex;而C++通过一种完全不同的方式实现了它——使用虚数库。这两种方式是不兼容的。
  • VLA可能导致sizeof的值在运行时才能确定。
    void foo(size_t x, int a);  // 使用VLA的函数void foo(size_t x, int a) {    printf("%zun", sizeof a); // 等同于sizeof(int*)    char s;    printf("%zun", sizeof s); // 将显示x*2}
  • 在C99中,如果一个结构有多个成员且它的最后一个成员为数组,则它可以是一个柔性数组成员。它与VLA类似,但VLA不能出现在定义中。它不指定数组的长度。截止到C++20,C++中没有类似的功能。下面是它的一个例子:
    struct X{    int n, m;    char bytes;}
  • 截止到C++20标准,restrict类型限定符并没有出现在C++里;但是很多编译器都提供了对它的支持(如GCC,MSVC,ICC,Clang等)。
  • 函数声明中的数组类型限定符在C++中是不允许的,下面是一个例子:
    int foo(int a);     // 相当于int foo(int *const a);int bar(char s); // 表示s的长度至少为5
  • C中的复合文字特性在C++中以一种叫做“列表初始化”的方法被实现——虽然它们在语义和作用上是不同的,但它们通常能起到相同的效果。
    struct X a = (struct X){4, 6};  // 在C++ 中等于 X{4, 6}.
  • 从C99开始,C语言支持结构的指定值初始化;在C++20以前,这是不允许的;但是从C++20标准开始,C++也支持了这个特性。
    struct X a = {.n = 4, .m = 6};  char s = { = 'a', ='g'};
  • 在C++中,可以使用“noreturn”来标记一个没有返回值的函数;在C11以前没有这样一个关键字,在C11中增加了关键字“_Noreturn”。
  • C语言允许在函数原型中声明复合数据类型,而C++不允许。
  • C++较C语言增加了一些关键字,这使得如果一段C语言代码使用了C++中新增的关键字作为标识符,它将会是非法的。
    struct template {    int new;    struct template* class;};
    以上代码在C语言中是允许的,但是在C++中不允许。
  • 在C和C++中行为不同的语句

    有一些语句在C和C ++中都有效,但是在两种语言中会产生不同的结果。

    /* Copyright (C) 1998-2020 Free Software Foundation, Inc.This file is part of GCC.GCC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 3, or (at your option)any later version.GCC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.Under Section 7 of GPL version 3, you are granted additionalpermissions described in the GCC Runtime Library Exception, version3.1, as published by the Free Software Foundation.You should have received a copy of the GNU General Public License anda copy of the GCC Runtime Library Exception along with this program;see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see<http://www.gnu.org/licenses/>.  *//* * ISO C Standard:  7.16  Boolean type and values  <stdbool.h> */#ifndef __cplusplus#define bool	_Bool#define true	1#define false	0#else...

    需要注意的是,_Bool在C语言中被储存为int类型。

    extern int T;int size(void){    struct T {  int i;  int j;  };        return sizeof(T);    /* C:   return sizeof(int)     * C++: return sizeof(struct T)     */}

    这是因为在C语言中,struct类型前需要添加“struct”,而在这个例子中,因为T前没有struct,所以它所代表的是在外部定义的int变量;而在C++中,因为可以省略struct,因此造成了歧义。然而,在C++中如果使用sizeof T这种形式,编译器会更倾向于认为T是一个表达式,因此编译会出错;而在C语言中则不会。


    相关