使用C++编译器编译C代码可能会遇到哪些问题?

45

如果您使用C++编译器编译现有的C代码库,会出现哪些问题?例如,在C++中将整数分配给枚举类型的值将失败,而在C中则合法(尽管有点恶心)。

如果我没有使用extern C { ... }将所有C文件包装起来,是否会在最不希望发生的地方出现名称混淆?有没有某些原因使我真的不应该这样做?

背景是我们有一个非常大的用C编写的代码库。几年来,我们一直在跳过障碍来完成C++可以自然实现的事情(例如,自定义继承)。我们想逐步开始使用C++,让我们的类CORBA框架支持它,并重构模块以利用C++提供的更自然的方法。

8个回答

41

我曾经做过类似的事情。问题的主要来源是C ++对类型更加严格,就像您所怀疑的那样。您需要在void *与其他类型的指针混合的地方添加强制转换。例如分配内存:

Foo *foo;
foo = malloc(sizeof(*foo));

以上是典型的C代码,但在C++中需要进行类型转换:

Foo *foo;
foo = (Foo*)malloc(sizeof(*foo));

在C++中有一些新的保留字,如"class"、"and"、"bool"、"catch"、"delete"、"explicit"、"mutable"、"namespace"、"new"、"operator"、"or"、"private"、"protected"、"friend"等等。这些保留字不能用作变量名。

上述问题可能是使用C++编译器编译旧C代码时最常见的问题。要获得完整的不兼容列表,请参阅ISO C和ISO C++之间的不兼容性

此外,您还问到名称修饰。如果没有extern "C"包装器,C++编译器将会修饰符号。只要您仅使用C++编译器,并且不依赖于dlsym()或类似函数从库中提取符号,这不是问题。


1
一些C++保留字在某些情况下也是关键字 - #include <iso646.h>,#include <stdbool.h>等。 - Jonathan Leffler
2
C++ 中有新的保留字。我遇到的一个是 "this"。当你有模糊的面向对象 C 代码时,很有可能有人使用它来表示在 C++ 中的含义... - Steve Jessop
2
哎呀!我忘记了这件事。我们的组件架构非常面向对象,大量使用了“this”关键字。现在我的一个开发人员可以说:“我告诉过你”,因为他在代码审查期间警告我不要使用“this”关键字。 - Chris Arguin
5
我正在使用 self 替代伪面向对象的 C 代码,原因就是如此。 - milleniumbug
链接有一段时间无法使用,但现在似乎可以正常工作了。 - Shafik Yaghmour
关于“C和C++之间的不兼容性”的注释:大多数C++编译器将支持许多C功能作为扩展,即使它们不是标准的C++,因此主要问题可能是类型。 - CoffeeTableEspresso

28

请查看ISO C和ISO C++之间的不兼容性,其中详细列出了所有不兼容性。有许多微妙的问题,包括一些不会立即在编译器错误中显现的问题。例如,一个可能成为问题的问题是字符常量的大小:

// In C, prints 4.  In C++, prints 1
printf("%d\n", sizeof('A'));

@ShafikYaghmour:对我来说它很好用。也许网站暂时挂了?或者你的上游某个过滤器阻止了该网站? - Adam Rosenfield
嗯,现在好像可以了,我也试过几次,所以不确定。也许你想在你的答案中添加一个更长的差异列表,这样它就不会太依赖于链接了。我本来想在我的答案中引用这个帖子这里,但由于当时链接失效了,我没有这样做,也许我以后会添加它。 - Shafik Yaghmour
@ShafikYaghmour:这是一个很长的列表。如果将来网站再次崩溃,总还有Wayback Machine - Adam Rosenfield
这是因为C++将其存储为UTF-8还是因为它将其识别为一个字符? - Aaron Franke
1
@AaronFranke:不是因为什么版本的问题,而是由于语言规范的(有意)变更,即在C++中,'A'的类型为char而不是C中的int类型。 C++03标准的附录C §C.1.1/2.13.2中给出了改进重载函数参数类型匹配的原理:像foo('A')这样的函数调用应该匹配foo(char)的重载,而不是foo(int)的重载。 - Adam Rosenfield

10

如果我不将所有的C文件都包裹在"extern C {...}"中,那么是否会在我最不希望的地方出现名称混淆?

当您尝试链接C和C ++时,它会咬您。

我编写了许多包含以下内容的头文件:

#ifdef __cplusplus
    extern "C" {
#endif

// rest of file

#ifdef __cplusplus
    }
#endif
一段时间后,它就会并入现有的多重包含样板文件中,你也就不再注意到它了。但是你必须小心放置它的位置——通常应该放在你的头文件任何包含之后面有没有什么理由我真的不应该这样做? 如果你确定不会结合使用C和C++,那我所知道的就没有理由不这样做。但是对于你所描述的逐渐迁移,对于任何具有公开接口需要同时被C组件和C ++组件使用的东西来说,这是必要的。 这样做的一个很大的原因是它会防止你重载函数(至少在那些头文件中)。 一旦你将所有代码迁移到C ++并开始进行维护/重构/扩展,你可能会发现你想这样做。

5
一般来说,你不会遇到任何问题。是的,C和C++之间存在一些不兼容性,但除了上面提到的malloc转换之外,它们似乎并不经常出现,而这个问题很容易解决。
我已经成功地将以下开源C库编译为C ++并使用:
- Expat XML解析器 - FreeType2字体光栅化器 - libjpeg:处理JPEG图像 - libpng:处理PNG图像 - Zlib压缩库
最困难的部分是添加命名空间包装器,这花费了几个小时,主要是因为代码中深埋的#include语句必须位于C++命名空间之外。
为什么要这样做?因为我销售一个商业库,人们直接将其链接到他们的应用程序中;有时他们的应用程序链接到其他版本的Expat、FreeType等。这导致多次定义符号错误。最干净的做法是将所有内容移动到我的库中,并将其隐藏在我的命名空间中。
然而,并非我使用的所有开源库都这样做。有些还没有引起冲突,我还没有解决它们,虽然这是无麻烦的,但相当繁琐。有趣的例外是SQLite,在C++中我无法编译它。所以我进行了大量的搜索和替换,给每个外部可见符号添加了前缀(我的产品名称)。这解决了我的客户问题。

踩负评是荒谬的。我从多个不同的代码库中获得了艰苦卓绝的商业经验,实际上很少遇到问题。我完全真诚地说,一般情况下你不会遇到问题。当然,这两种语言是不兼容的,但仍然可以肯定的是,这些不兼容性通常不会引起麻烦。 - Graham Asher

3

另一个例子:在C++中,整数到枚举类型之间没有隐式转换,而在C语言中有。如果你真的想在C++中这样做,你需要进行强制类型转换。


2

如果使用MSVC,我之前已经做过类似的事情了,一个好的策略是:

  1. 将单个文件设置为CPP格式,这样你可以逐步转向CPP编译器。
  2. 通过ctrl+f7逐个文件进行构建。
  3. 不必将所有的malloc都进行类型强制转换,而可以创建一个模板版本。

foo = (Foo*)malloc(sizeof(*foo));

则变成了

foo = malloc<Foo>();

当然,您可以针对需要Foo + n字节的情况进行重载。

我还建议在可能的情况下切换内存分配以使用RAII。我发现有些函数非常复杂,因此切换到RAII的风险太高了,但在大多数情况下,这样做相当简单。


1

C ++ 在类型检查上更加严格,因此您可能需要在每个 malloc/realloc/calloc 调用中添加一个转换。


-1

尝试使用C++编译器进行编译:

typedef enum{ false = 0, true = 1} bool;

2
您能否详细阐述一下您的答案,并对您提供的解决方案进行更多描述? - abarisone
2
抱歉我的回复很短。我想说的是,'typedef enum{ false = 0, true = 1} bool;' 可以在C编译器中编译通过。但是在C++编译器中会有问题,因为false、true和bool在C++中是保留字。 - Dimitrie D.
OP 询问了编译 C 为 C++ 的步骤,他知道这两种语言并不完全兼容,因此才会提出这个问题。 - CoffeeTableEspresso

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接