应该“可移植”C编译为C ++吗?

时间:2009-04-03 08:35:08

标签: c++ c compatibility portability

我收到了对an answer的评论我发布了一个C问题,评论者建议编写代码以便用C ++编译器编译,因为最初的问题提到代码应该是“可移植的”。 / p>

这是“便携式C”的常见解释吗?正如我在对该答案的进一步评论中所说的那样,对我来说完全令人惊讶,我认为可移植性意味着完全不同的东西,并且在编写也是合法C ++的C代码时看到的好处很少。

11 个答案:

答案 0 :(得分:18)

目前的C ++(1998)标准包含C(1989)标准。关于类型安全的一些精细打印放在一边,这意味着“好”C89应该在C ++编译器中编译好。

问题是当前的C标准是1999年(C99) - 它但正式成为C ++标准(AFAIK)(*)的一部分。这意味着C99(long long int,stdint.h,...)的许多“更好”特性,而许多C ++编译器支持严格兼容。

“Portable”C完全意味着其他东西,与官方的ISO / ANSI标准没什么关系。这意味着您的代码不会对主机环境做出假设。 (int,endianess,非标准函数或errno数字的大小,类似的东西。)

我曾为一个跨平台项目撰写过的编码风格指南:

跨平台DNA(不要假设)

  • 没有本机数据类型。您可能使用的唯一数据类型是在标准库中声明的数据类型。
  • char,short,int和long各有不同的大小,就像float,double和long double一样。
  • int不是32位。
  • char既没有签名也没有签名。
  • char不能包含数字,只能包含字符。
  • 将较短的类型转换为较长的类型(int - > long)会破坏CPU的对齐规则。
  • int和int *的大小不同。
  • int *和long *具有不同的大小(与任何其他数据类型的指针一样)。
  • 您是否记得本机数据类型甚至不存在?
  • 'a' - 'A'与'z' - 'Z'的结果不同。
  • 'Z' - 'A'与'z' - 'a'的结果不同,且不等于25。
  • 除了测试其值之外,你无法对NULL指针做任何事情;取消引用它会使系统崩溃。
  • 涉及签名和未签名类型的算术不起作用。
  • 数据类型的对齐规则随机更改。
  • 数据类型的内部布局随机更改。
  • 上溢和下溢的特定行为会随机变化。
  • 函数调用ABI随机更改。
  • 操作数按随机顺序进行评估。
  • 只有编译器可以解决这种随机性问题。随机性将随着下一版本的CPU / OS /编译器而改变。
  • 对于指针,==和!=仅适用于指向完全相同数据类型的指针。
  • <,>仅适用于指向同一数组的指针。它们仅适用于明确声明为unsigned的char。
  • 您还记得本机数据类型不存在吗?
  • size_t(sizeof的返回值的类型)不能转换为任何其他数据类型。
  • ptrdiff_t(从另一个指针中减去一个指针的返回值的类型)不能转换为任何其他数据类型。
  • wchar_t(“宽”字符的类型,其确切性质是实现定义的)不能转换为任何其他数据类型。
  • 任何......_ t数据类型都不能转换为任何其他数据类型

(*):在撰写本文时,情况确实如此。事情已经改变了一点C ++ 11,但我的答案的要点是正确的。

答案 1 :(得分:16)

没有。我的回复Why artificially limit your code to C?有一些符合标准的C99不能编译成C ++的例子;早期的C差异较小,但C ++具有更强的类型和(void)函数参数列表的不同处理方式。

关于将C'移植到C ++是否有好处 - 该答案中引用的特定项目是基于特征的语言的虚拟机,因此不适合C ++对象模型,并且具有很多情况下,您将拉出解释器堆栈的void*,然后转换为表示内置对象类型布局的结构。为了使代码“可移植”到C ++,它会添加大量的转换,这对类型安全没有任何作用。

答案 2 :(得分:8)

可移植性意味着编写代码,使其编译并使用不同的编译器和/或不同的平台具有相同的行为(即尽可能依赖ISO标准规定的行为)。

使用不同语言的编译器进行编译是一个很好的(也许),但我不认为这是可移植性的意思。由于C ++和C现在越来越分散,这将更难实现。

另一方面,在编写C代码时,我仍然会避免使用“class”作为标识符。

答案 3 :(得分:3)

不,“可移植”并不意味着“在C ++编译器上编译”,它意味着“在任何标准的comformant C编译器上编译”具有一致的,定义的行为。

并且不要因为保持C ++兼容性而剥夺C99的改进。

但是只要保持兼容性不会束缚你的双手,如果你能避免使用“类”和“虚拟”之类的东西,那就更好了。如果您正在编写开源代码,有人可能希望将代码移植到C ++;如果你想要雇用,你的公司/客户可能希望在将来的某个时间进行移植。嘿,也许你甚至想在将来把它移植到C ++中

作为一个“好管家”,不要在篝火旁留下垃圾,无论你做什么都只是好事。

请尽量保持标题不兼容。即使您的代码从未被移植过,人们也可能需要从C ++链接到它,所以有一个不使用任何C ++保留字的标题很酷。

答案 4 :(得分:2)

这取决于。如果你正在做一些对C ++用户有用的事情,那么这可能是一个好主意。如果您正在做一些C ++用户永远不需要但C用户可能觉得方便的东西,请不要打扰它使C ++兼容。

如果您正在编写一个可以执行许多人操作的程序,您可以考虑将其尽可能广泛使用。如果您正在编写Linux内核的附加内容,则可以将C ++兼容性抛到窗外 - 它永远不会被需要。

尝试猜测谁可能会使用您的代码,如果您认为很多C ++粉丝可能会发现您的代码有用,请考虑使其对C ++友好。但是,如果您认为大多数C ++程序员不需要它(即它是一个已经在C ++中相当标准化的功能),请不要打扰。

答案 5 :(得分:2)

使用C ++编译器编译C代码以进行更严格的类型检查绝对是常见的做法。虽然像lint这样有特定于C的工具,但使用C ++编译器会更方便。

使用C ++编译器编译C代码意味着您通常必须使用extern“C”块来包围您的包含,以告诉编译器不要破坏函数名称。但是,这不是合法的C语法。实际上,您正在使用C ++语法,而您认为是C的代码实际上是C ++。使用“C ++便利性”的趋势也开始像使用未命名的联盟一样蔓延。

如果您需要严格遵守C代码,则需要小心。

答案 6 :(得分:2)

FWIW,一旦项目获得了一定的规模和动力,它实际上不可能从C ++兼容性中获益:即使它不会直接移植到C ++,也确实存在很多与工作/处理C ++源代码相关的现代工具。

从这个意义上说,缺乏与C ++的兼容性,实际上可能意味着你可能需要拿出自己的工具来做特定的事情。我完全理解在某些平台,环境和项目中支持C over C ++背后的原因,但从长远来看,C ++兼容性通常简化了项目设计,最重要的是:它提供了选项

此外,有许多C项目最终变得如此之大,以至于它们实际上可以从C ++的功能中受益,例如使用具有访问修饰符的类改进抽象和封装。

例如,看一下linux(内核)或gcc项目,这两个项目基本上都是“仅限C”,但两个开发者社区中仍然经常讨论切换到C ++的潜在收益。

事实上,目前正在进行gcc工作( FSF树中!)将gcc源代码转换为有效的C ++语法(详情请参阅:gcc-in-cxx),以便C ++编译器可用于编译源代码。

这基本上是由长期的gcc黑客发起的:Ian Lance Taylor。

最初,这只是为了提供更好的错误检查,以及改进的兼容性(即一旦完成这一步,就意味着你不一定要有一个C编译器来编译gcc,你可以也只是使用C ++编译器,如果你恰好是'只是'一个C ++开发人员,那就是你得到的东西。)

但最终,这个分支旨在鼓励向C ++迁移,作为gcc的实现语言,这是一个真正的革命性步骤 - 这种范式转变正受到那些FSF人员的批判性认识。 / p>

另一方面,很明显gcc已经受到其内部结构的限制,并且至少有助于改善这种情况的任何事情都应该受到称赞:开始为gcc项目做出贡献是不必要的复杂和繁琐的,主要是由于内部结构复杂,已经开始使用宏和gcc特定扩展模拟C ++中的许多更高级功能。

为最终切换到C ++准备gcc代码库是最合乎逻辑的事情(不管它实际上是什么时候完成!),实际上需要它才能保持竞争力,有趣和简单相关,这尤其适用于llvm等非常有前途的努力,这些努力并没有带来所有这些困难,复杂性。

虽然在C语言中编写非常复杂的软件通常是可能的,但这样做会变得不必要地复杂化,很多项目很久以前就已经简单地过时了。这并不意味着C不再相关,恰恰相反。但考虑到一定的代码基础和复杂性,C根本不一定是工作的理想工具。

事实上,您甚至可以说C ++不一定是某些项目的理想语言,但只要语言本身支持封装并提供强制执行这些抽象的方法,人们就可以解决这些限制。

只是因为显然可以用非常低级的语言编写非常复杂的软件,并不意味着我们必须这样做,或者它首先确实是有效的。

我在这里谈论的是具有数十万行代码的复杂软件,其使用寿命为几十年。在这种情况下,选项越来越重要。

答案 7 :(得分:2)

不,味道问题。 我讨厌抛出无效指针,使代码混乱没有多大好处。

char * p = (char *)malloc(100);

VS

char * p = malloc(100);

当我编写一个“面向对象”的C库模块时,我真的喜欢使用'this'作为我的对象指针,因为它是一个C ++关键字,它不能用C ++编译(这是有意的,因为这些模块是在C ++中没有意义,因为它们确实存在于stl和库中。)

答案 8 :(得分:1)

为什么你看不到什么好处?这很容易做,谁知道将来如何使用代码。

答案 9 :(得分:1)

不,可编译的C ++不是便携式的常见解释。处理非常古老的代码,K& R样式声明是高度可移植的,但无法在C ++下编译。

正如已经指出的,您可能希望使用C99增强功能。但是,我建议您考虑所有目标用户,并确保他们可以使用这些增强功能。不要只使用可变长度数组等,因为你有自由,但只有在真正合理的情况下。

是的,尽可能保持C ++兼容性是件好事 - 其他人可能有充分的理由需要将C代码编译为C ++。例如,如果他们想要将它包含在MFC应用程序中,则必须在单独的DLL或库中构建纯C,而不是仅仅将代码包含在单个项目中。

还有一种观点认为,如果采用不同的优化方式,在C ++模式下运行编译器可能会遇到一些细微的错误,具体取决于编译器。

答案 10 :(得分:1)

AFAIK经典文本中的所有代码C编程语言,第二版可以使用标准C ++编译器编译,如GCC(g ++)。如果您的C代码符合该经典文本中遵循的标准,则足够好&您已准备好使用C ++编译器编译C代码。

以Linux内核源代码为例,主要用C语言编写一些内联汇编程序代码,用C ++编译器编译Linux内核代码是一场噩梦,因为“新”用作“新”的原因最少。 linux内核代码中的变量名,其中C ++不允许使用'new'作为变量名。我在这里举一个例子。请记住,linux内核是便携式的。编译和编辑在intel,ppc,sparc等架构中运行良好。这只是为了说明可移植性在软件世界中确实有不同的含义。如果要使用C ++编译器编译C代码,则需要将代码库从C迁移到C ++。我认为它是两种不同的编程语言,最明显的原因是C程序员不喜欢C ++。但我喜欢他们两个&我经常使用它们。您的C代码是可移植的,但是在将C代码迁移到C ++代码时,应确保遵循标准技术使C ++代码可移植。请继续阅读,了解您将从哪里获得标准技术。

您必须非常小心地将C代码移植到C ++代码和我要问的下一个问题是,如果某些C代码是便携式的话,为什么还要费心去做呢?运行良好没有任何问题?我无法接受可管理性,再次将Linux内核中的一个大代码源管理得很好。

总是看到两种编程语言C& C ++作为不同的编程语言,尽管C ++确实支持C& C ++。它的基本概念是始终支持这种美妙的语言以实现向后兼容。如果你不是将这两种语言视为不同的工具,那么你就属于流行的,丑陋的C / C ++编程语言大战和战争之地。让自己变脏

选择便携性时使用以下规则:

a)您的代码(C或C ++)是否需要在可能使用本机C / C ++编译器的不同体系结构上进行编译? b)对您希望运行程序的不同体系结构的C / C ++编译器进行研究。计划代码移植。在这方面投入时间。 c)尽可能尝试在C代码和C代码之间提供一个干净的分离层。 C ++代码。如果你的C代码是可移植的,你只需要使用便携式C ++编码技术再次围绕该可移植C代码编写C ++包装器。

转向一些关于如何编写可移植C ++代码的优秀C ++书籍。我个人推荐Bjarne Stroustrup本人的C ++编程语言,来自Scott meyers& Sons的Effective C ++系列。在www.ddj.com上有流行的DDJ文章。

PS:我的文章中的Linux内核示例只是为了说明可移植性在软件编程中意味着不同的含义。并不批评Linux内核是用C& C编写的。不是在C ++中。