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

Mar*_* Ba 54 c c++ c99 c++11

这个问题是由Herb Sutter的一篇文章的回复引发的,他在那里解释了MS决定不支持/制作C99编译器,但只是采用了C++(11)标准中的C(99)功能.

一位评论者回答说:

(...)C很重要,值得至少注意一点.

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

因为我只使用MS C++编程,所以我真的不知道"纯粹的"C,即我没有准备好了解C++的细节 - 我正在使用的语言不在C(99)中,而且我几乎没有一些C99代码在C++编译器中无法正常工作的线索.

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

此外,我很感兴趣是否存在任何重要的语义差异或陷阱,即C(99)代码将在C++(11)下编译,但使用C++编译器而不是C编译器执行不同的操作.


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

Die*_*Epp 29

有许多不兼容的东西已经存在多年(C90或更早),以及C99和C11中的一些非常好的功能.这些都是我的头脑.

// 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];
Run Code Online (Sandbox Code Playgroud)

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

C99的新功能非常适合一般编程.VLA restrict并不是(在我看来)针对一般用途,但主要是为了将FORTRAN和数字程序员带到C(尽管restrict有助于自动向量).因为任何符合要求的程序restrict仍然可以以完全相同的方式工作(但可能没有那么快),如果你#define restrict在文件的顶部,这不是什么大问题.VVA在野外看起来非常罕见.

灵活的阵列成员可以很好. 请注意,这些与可变长度数组不同!人们多年来一直在使用这个技巧,但官方支持意味着更少的输入,它也允许我们在编译时制作常量.(旧的方法是使用大小为1的数组,但是计算分配大小真是太麻烦了.)

struct lenstr {
    unsigned length;
    char data[];
};
// compile time constant
const struct lenstr hello = { 12, "hello, world" };
Run Code Online (Sandbox Code Playgroud)

指定的初始化程序. 节省了很多打字.

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 };
Run Code Online (Sandbox Code Playgroud)

inline关键字的行为不同,你可以选择翻译单元中加入extern声明该单位获得内联声明的函数的非内联版本.

复合文字.

struct point { float x; float y; };
struct point xy_from_polar(float r, float angle)
{
    return (struct point) { cosf(angle) * r, sinf(angle) * r };
}
Run Code Online (Sandbox Code Playgroud)

snprintf函数可能是我在C中排名前十的最有用的库函数.它不仅缺少C++,而且MSVC运行时只提供一个被调用的函数_snprintf,不能保证将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!
};
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,许多这些功能只是为您节省了大量的输入(复合文字),或使程序更容易调试(灵活的数组成员),使其更容易避免错误(指定初始化器/忘记初始化结构字段).这些并没有太大的变化.

对于语义差异,我确信别名规则是不同的,但是现在大多数编译器都很宽容我不确定你是如何构建一个测试用例来演示的.每个人都达到的C和C++之间的区别是旧sizeof('a')表达式,对于C++,它总是1,但在32位C系统上通常是4.但sizeof('a')无论如何,没有人关心什么.但是,C99标准中有一些保证可以编纂现有做法.

请使用以下代码.它使用一种常见的技巧来定义C中的联合类型,而不会浪费额外的存储空间.我认为这是语义上有效的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这是一种耻辱. MSVC代码生成器很好,太糟糕了,没有C99前端.

  • C++也很好,各地的C程序员都应该使用它.;) (5认同)
  • @ bames53:我不会在C++论坛上走动,告诉他们使用C#,不要去讨论有关C告诉人们使用C++的讨论. (5认同)

Chr*_*oph 26

如果从C和C++的公共子集开始,有时称为clean C(不完全是C90),则必须考虑3种类型的不兼容性:

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

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

    这可能是微软仍然提供C前端的主要原因:否则,不能编译为C++的遗留代码必须重写.

  2. 其他C功能不属于C++

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

  3. 可在C和C++中使用的功能,但具有不同的语义

    这方面的一个例子是const对象或inline函数的链接.

可在此处找到 C99和C++ 98之间的不兼容性列表(Mat已经提到过).

虽然C++ 11和C11在某些方面越来越接近(变量宏现在在C++中可用,可变长度数组现在是可选的C语言特性),但不兼容性列表也在增长(例如C和C中的通用选择)autoC++中的type-specifier).

顺便说一句,虽然微软已经采取了一些措施放弃C(这不是最近的C),但据我所知,开源社区中没有人真正采取措施对此采取措施:它将是很可能通过C-to-C++编译器提供现代C的许多功能,特别是如果你认为其中一些很容易实现.这实际上现在可以使用Comeau C/C++,它支持C99.

然而,这并不是一个迫切的问题:就个人而言,我对在Windows上使用GCC和Clang非常感到满意,并且还有MSVC的专有替代品,例如Pelles C或Intel的编译器.