【问题标题】:What issues can I expect compiling C code with a C++ compiler?使用 C++ 编译器编译 C 代码会出现什么问题?
【发布时间】:2010-10-26 01:55:45
【问题描述】:

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

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

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

【问题讨论】:

    标签: c++ c compiler-construction migration language-interoperability


    【解决方案1】:

    我曾经做过这样的事情。正如您所怀疑的,问题的主要来源是 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 代码时最常见的问题。有关不兼容性的完整列表,请参阅Incompatibilities Between ISO C and ISO C++。

    您还询问有关名称修改的问题。在没有 extern "C" 包装器的情况下,C++ 编译器 会破坏符号。只要您使用 only 一个 C++ 编译器,并且不依赖 dlsym() 或类似的东西从库中提取符号,这不是问题。

    【讨论】:

    • 某些C++保留字在某些情况下也是关键字——#include 、#include 等
    • “C++ 中有新的保留字”。我摔倒的那个是“这个”。当你有模糊的 OO C 代码时,很有可能有人用它来表示,嗯,它在 C++ 中的含义......
    • 哎哟!我已经忘记了。我们有非常面向对象的代码,并且在我们的组件架构中大量使用了“this”。现在我的一位开发人员会说“我告诉过你”,因为他在代码审查期间警告我,我可能不应该使用“this”。
    • 正是出于这个原因,我在伪 OO C 代码中使用 self。
    • 链接暂时无法使用,但现在可以正常使用了。
    【解决方案2】:

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

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

    【讨论】:

    • @ShafikYaghmour:对我来说效果很好。也许该网站短暂关闭?或者,也许您在上游某处有一个过滤器阻止了该网站?
    • 嗯,它现在似乎确实有效,我也尝试过几次所以不确定。也许您可能想在答案中添加更长的差异列表,以减少对链接的依赖。我本来想在我的回答here 中引用这个帖子,但由于当时我没有链接,所以我以后可能会添加它。
    • @ShafikYaghmour:这是一个很长的列表。如果该网站将来再次出现故障,总会有Wayback Machine。
    • 是因为 C++ 将其存储为 UTF-8 还是因为它识别为一个字符?
    • @AaronFranke:两者都不是,这仅仅是由于语言规范中的(有意的)更改,即 'A' 在 C++ 中的类型为 char 而不是 C 中的类型 int。给出的基本原理在 C++03 标准的附件 C §C.1.1/2.13.2 中,用于改进重载函数参数类型匹配:像 foo('A') 这样的函数调用应该匹配 foo(char) 的重载,而不是 foo(int)。跨度>
    【解决方案3】:

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

    当你试图将 C 和 C++ 链接在一起时,它会咬你。

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

    #ifdef __cplusplus
        extern "C" {
    #endif
    
    // rest of file
    
    #ifdef __cplusplus
        }
    #endif
    

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

    我真的不应该这样做吗?

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

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

    【讨论】:

      【解决方案4】:

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

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

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

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

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

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

      【讨论】:

      • 投反对票是荒谬的。我使用许多不同的代码库从来之不易的商业经验中写作,而且我真的很少遇到问题。我完全真诚地说,一般来说你不会遇到问题。当然,这两种语言是不兼容的,但确实不兼容通常不会造成麻烦。
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

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

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

        变成

        foo = malloc<Foo>();
        

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

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            【解决方案8】:

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

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

            【讨论】:

            • 能否请您详细说明您的答案,添加更多关于您提供的解决方案的描述?
            • 对于我的简短回复,我深表歉意。我想说的是,'typedef enum{ false = 0, true = 1} bool;'将使用 C 编译器编译而不会出现问题。但是 C++ 编译器会出现问题,因为 false、true 和 bool 是 C++ 中的保留字。
            • OP 询问他应该采取哪些步骤将 C 编译为 C++。他知道这两者并不完全兼容,这就是他问的原因。
            猜你喜欢
            • 2013-09-22
            • 1970-01-01
            • 1970-01-01
            • 2011-07-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多