我期望用C ++编译器编译C代码有什么问题?

时间:2009-05-14 04:14:04

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

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

如果我没有将所有C文件都包装在extern C { ... }中,我是否会在最不期望的地方获得名称错误?有什么理由我不应该这样做吗?

对于背景,我们有一个用C语言编写的非常大的代码库。几年来,我们一直在跳过箍来做一些通过C ++(例如homebrewe继承)自然而然的事情。我们希望开始转向C ++,但是要逐步进行;获得我们类似CORBA的框架来支持它,并在我们继续使用C ++提供的更自然的方法时重构模块。

8 个答案:

答案 0 :(得分:33)

我曾经做过这样的事情。问题的主要来源是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”,“运算符“,”或“,”private“,”protected“,”friend“等。例如,这些不能用作变量名。

当您使用C ++编译器编译旧C代码时,上述可能是最常见的问题。有关不兼容性的完整列表,请参阅Incompatibilities Between ISO C and ISO C++

您还会询问名称损坏情况。如果没有extern“C”包装器,C ++编译器修改符号。只要您只使用 C ++编译器,并且不依赖于dlsym()或类似的东西从库中提取符号,这不是问题。

答案 1 :(得分:21)

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

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

答案 2 :(得分:8)

如果我没有将所有C文件包装在“extern C {...}”中,我是否会在最不期望的地方获得名称错误?

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

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

#ifdef __cplusplus
    extern "C" {
#endif

// rest of file

#ifdef __cplusplus
    }
#endif

过了一会儿,它会合并到现有的多包含样板文件中,你就会停止看到它。但是你必须要小心你把它放在哪里 - 通常它属于 之后包含你的标题。

我有什么理由不这样做吗?

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

这样做的一个重要原因是它会阻止你重载函数(至少在那些头文件中)。将所有代码迁移到C ++并开始维护/重构/扩展它之后,您可能会发现要这样做。

答案 3 :(得分:3)

另一个例子:C ++中没有从int到enums的隐式转换,而C中有一个。如果你真的想在C ++中使用它,你需要一个演员。

答案 4 :(得分:3)

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

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

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

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

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

但是,我没有使用我使用的所有开源库。一些避风港尚未引发冲突,我还没有解决它们,虽然没有麻烦,但是很麻烦。有趣的例外是SQLite,我无法用C ++编译。因此,我进行了大量搜索和替换,为每个外部可见符号添加了前缀(我的产品名称)。这解决了我客户的问题。

答案 5 :(得分:2)

我在使用MSVC之前已经完成了这个,如果使用MSVC,那么一个好的策略是:

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

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

    变为

    foo = malloc<Foo>();
    

    当然,对于你想要Foo + n字节

    的情况,你可以有一个重载

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

答案 6 :(得分:1)

C ++有更严格的类型检查,所以你可能需要为每次调用malloc / realloc / calloc添加一个强制转换。

答案 7 :(得分:-1)

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

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