43

如果您使用现有的 C 代码库并使用 C++ 编译器对其进行编译,您预计会出现什么样的问题?例如,我认为将整数分配给具有枚举类型的值在 C++ 中将失败,而在 C 中它是合法的(如果有点讨厌)。

如果我不将所有 C 文件都包装在 中extern C { ... },我会在我最不期望的地方进行名称修改吗?我真的不应该这样做吗?

作为背景,我们有一个用 C 编写的非常大的代码库。几年来,我们一直在跳槽做一些通过 C++ 很自然的事情(例如,自制继承)。我们想开始转向 C++,但要循序渐进;让我们的类似 CORBA 的框架支持它,并在我们进行过程中重构模块以利用 C++ 提供的更自然的方法。

4

8 回答 8

39

我曾经做过一次这样的事情。正如您所怀疑的那样,问题的主要来源是 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() 或类似的东西从库中提取符号,这不是问题。

于 2009-05-14T04:29:56.410 回答
26

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

// In C, prints 4.  In C++, prints 1
printf("%d\n", sizeof('A'));
于 2009-05-14T04:19:52.410 回答
10

如果我不将所有 C 文件包装在“extern C { ... }”中,我会在我最不期望的地方进行名称修改吗?

当您尝试将 C 和 C++ 链接在一起时,它会咬您一口。

我写了很多头文件,其中包含:

#ifdef __cplusplus
    extern "C" {
#endif

// rest of file

#ifdef __cplusplus
    }
#endif

一段时间后,它会合并到现有的多包含样板中,而您将不再看到它。但是你必须小心你把它放在哪里——通常它属于你的任何包含你的标题之后。

我真的不应该这样做吗?

如果您确定您不会将 C 和 C++ 结合起来,那么据我所知,没有理由这样做。但是随着您描述的逐步迁移,对于任何具有 C 组件和 C++ 组件都需要使用的已发布接口的东西来说,这都是必不可少的。

这样做的主要原因是它可以防止您重载函数(至少在那些标题中)。一旦您将所有代码迁移到 C++ 并开始维护/重构/扩展它,您可能会发现您想要这样做。

于 2009-05-14T10:57:05.683 回答
5

一般来说,你根本不会遇到任何问题。是的,C 和 C++ 之间存在一些不兼容性,但它们似乎并不经常出现,除了上面提到的 malloc 强制转换,修复起来非常简单。

我已成功编译并使用以下开源 C 库作为 C++:

  • Expat XML 解析器
  • FreeType2 字体光栅化器
  • libjpeg:处理JPEG图像
  • libpng:处理PNG图像
  • Zlib 压缩库

最困难的部分是添加命名空间包装器,这需要几个小时,主要是因为#include 语句深埋在代码中,必须在 C++ 命名空间之外。

我为什么这样做?因为我卖的是人们直接链接到他们的应用程序的商业图书馆;有时他们的应用程序会链接到其他版本的 Expat、FreeType 等。这会导致多重定义符号错误。最干净的做法是将所有内容移到我的库中并将其隐藏在我的命名空间中。

但是,我并没有对我使用的所有开源库都这样做。有些还没有引起冲突,我还没来得及修复它们,虽然没有问题,但很乏味。有趣的例外是 SQLite,我无法用 C++ 编译它。因此,我进行了大量搜索和替换,为每个外部可见符号添加了前缀(我的产品名称)。这解决了我客户的问题。

于 2015-12-16T16:10:31.797 回答
3

另一个例子:在 C++ 中没有从整数到枚举的隐式转换,而在 C 中有一个。如果你真的想在 C++ 中进行转换,则需要强制转换。

于 2009-05-14T05:23:20.433 回答
2

我在使用 MSVC 之前已经这样做了,如果使用 MSVC 一个好的策略是:

  1. 将单个文件设置为 CPP 构建,这样您就可以逐步迁移到 CPP 编译器。
  2. 使用 ctrl+f7 逐个文件处理以构建该文件。
  3. 您可以创建模板版本,而不是强制转换所有 malloc

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

变成

foo = malloc<Foo>();

当然,对于需要 Foo+n 个字节的情况,您可以重载

我还建议在可能的情况下切换内存分配以使用 RAII,我发现某些功能非常复杂,因此切换到 RAII 风险太高,在大多数情况下它很简单。

于 2014-05-21T18:55:25.203 回答
1

C++ 具有更严格的类型检查,因此您可能需要在每次调用 malloc/realloc/calloc 时添加强制转换。

于 2009-05-14T06:47:27.767 回答
-2

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

typedef enum{ false = 0, true = 1} bool;
于 2016-04-19T06:12:49.453 回答