C(99)和C ++(11)之间有什么不兼容的区别?

这个问题是由Herb Sutter的一篇post引起的 ,他解释了MS决定不支持/编译C99编译器,而只是使用C ++(11)标准中的C(99)特性。

一位评论者回答说 :

(…)C是重要的,值得至less一点关注。

有很多现有的代码是有效的C但不是有效的C ++。 该代码不可能被重写(…)

因为我只用MS C ++编程,所以我真的不知道“纯”的C语言,也就是说,我没有准备好我所使用的C ++语言细节不在C(99)中,一些C99代码在C ++编译器中不起作用的线索。

请注意,我知道关于C99只restrict关键字,这对我来说似乎有非常狭窄的应用程序和有关变长数组(我不知道他们是多么广泛或重要)。

另外,我非常感兴趣的是,是否有任何重要的语义差异或陷阱,即C(99)代码将在C ++(11)下编译 ,但是与C编译器做了不同的事情。


快速链接:答案的外部资源:

  • 维基百科页面
  • David R. Tribble的比较 (从2001年)
  • C ++ 11标准: http : //www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3376.pdf (C.1节)

如果您从C和C ++的常见子集(有时称为clean C(它不是C90))开始,则必须考虑3种types的不兼容性:

  1. 额外的C ++特性使合法的C非法C ++

    这方面的例子是C ++关键字,它可以用作C中的标识符或者C中的隐式转换,但是需要在C ++中进行明确的转换。

    这可能是Microsoft为什么还要发布C前端的主要原因:否则,不能编译为C ++的遗留代码将不得不被重写。

  2. 额外的Cfunction不是C ++的一部分

    C语言在分叉之后并没有停止发展。 一些例子是变长数组,指定了初始化和restrict 。 这些function可以非常方便,但不是任何C ++标准的一部分,其中一些可能永远不会进入。

  3. 在C和C ++中都可用,但是具有不同的语义

    一个例子是const对象或inline函数的链接。

C99和C ++ 98之间的不兼容列表可以在这里find (Mat已经提到过)。

虽然C ++ 11和C11在某些方面更加接近(可变参数macros现在可以在C ++中使用,但是可变长度数组现在是可选的C语言function),不兼容性列表也增长了(例如C和C ++中的autotypes说明符)。

另外,虽然微软已经决定放弃C(这不是最近的一个),但据我所知,在开源社区没有人真的采取了一些措施来解决这个问题:很有可能通过C到C ++编译器提供现代C的许多特性,特别是如果你考虑到其中一些实现是微不足道的 。 现在实际上可以使用支持C99的Comeau C / C ++。

但是,这并不是一个真正的紧迫问题:就个人而言,在Windows上使用GCC和Clang相当舒服,而且还有专有的MSVC替代品,例如Pelles C或Intel的编译器。

C99和C11中有许多不兼容的东西(C90或更早的版本),以及一些非常好的function。 这些都是我的头顶。

 // Valid C int *array = malloc(sizeof(*array) * n); // Valid C and valid C++, extra typing, it's always extra typing... int *array = (int *) malloc(sizeof(*array) * n); // Valid C++ int *array = new int[n]; 

C99很好,各地的C程序员都应该使用它

C99中的新function对于一般编程非常好。 VLA和restrict并不是(在我看来)是针对一般用途的,但主要是为了使FORTRAN和数字程序员能够使用C(尽piperestrict有助于自动裁剪器)。 因为任何使用restrict符合程序仍然会以完全相同的方式工作(但可能不会那么快),如果您将#define restrict在文件的顶部,则不是什么大问题。 VLA在野外似乎很less见。

灵活的arrays成员可以很好。 请注意,这些与变长数组不一样! 人们多年来一直在使用这个技巧,但是官方的支持意味着更less的input,而且它还允许我们在编译时创build常量。 (旧的方式是有一个大小为1的数组,但是然后计算分配大小是一个真正的麻烦。)

 struct lenstr { unsigned length; char data[]; }; // compile time constant const struct lenstr hello = { 12, "hello, world" }; 

指定初始值设定项。 节省了很多打字。

 struct my_struct { int a; char *b; int c; const char *d; }; struct my_struct x = { .a = 15, .d = "hello" // implicitly sets b = NULL and c = 0 }; int hex_digits[256] = { ['0'] = 0, ['1'] = 1, ['2'] = 2, /* etc */ ['f'] = 15 }; 

inline关键字的行为有所不同,您可以select哪个翻译单元通过向该单元添加extern声明来获取以inline声明的函数的非内联版本。

复合文字。

 struct point { float x; float y; }; struct point xy_from_polar(float r, float angle) { return (struct point) { cosf(angle) * r, sinf(angle) * r }; } 

snprintf函数可能是C中最有用的10个函数库函数。它不但在C ++中不存在,而且MSVC运行时只提供了一个名为_snprintf的函数,不保证在string中添加NUL终结符。snprintf在C ++ 11中,但在MSVC C运行时仍然显着地缺失。)

匿名结构和联合 (C11,但永远GCC扩展)(匿名联合显然是在C + + 03中,在C模式中不支持MSVC):

 struct my_value { int type; union { int as_int; double as_double; }; // no field name! }; 

正如你所看到的,许多这些特性只是为你节省了大量的input(复合文字),或者使程序更容易debugging(灵活的数组成员),更容易避免错误(指定初始化/忘记初始化结构域)。 这些并不是剧烈的变化。

对于语义差异,我确定别名规则是不同的,但是现在大多数编译器都已经足够宽容了,我不知道如何构build一个testing用例来演示。 每个人都可以达到的C和C ++之间的区别是旧的sizeof('a')expression式,对于C ++来说,它总是1,但在32位C系统上通常是4。 但是,无论如何,没有人关心什么是sizeof('a') 。 但是,C99标准中有一些保证,将现有的做法编纂成文。

采取下面的代码。 它使用一个常见的技巧来定义C中的联合types,而不会浪费额外的存储空间。 我认为这是语义上有效的C99,我认为这是语义上可疑的C ++,但我可能是错的。

 #define TAG_FUNKY_TOWN 5 struct object { int tag; }; struct funky_town { int tag; char *string; int i; }; void my_function(void) { struct object *p = other_function(); if (p->tag == TAG_FUNKY_TOWN) { struct funky_town *ft = (struct funky_town *) p; puts(ft->string); } } 

这是一个耻辱,虽然。 MSVC代码生成器是好的,太糟糕了没有C99的前端。

在C ++中,设置联合的一个成员并访问不同成员的值是未定义的行为,但在C99中不是未定义的。

维基百科页面上列出了许多其他差异。

我会提到C ++ 11标准的 “C.1 C ++和ISO C”。 这份文件因各种差异的打击及其对发展的影响而受到打击。