以C语言风格编写C++代码

6
如果我们想用C语言编写一个模块,并且必须用 g++ 编译为C ++,那么是否可以使用C中的“全局/静态函数”而不是自己的类来开发C ++代码?简单地说,在C++ 中以C语言方式编码(只需更改几个系统头文件等)是否可行?

6
很多项目都可以不做修改就能编译,但C并非C++的子集。例如,任何在C中使用“class”作为变量名的程序将无法在C++中编译。 - Marcelo Cantos
在处理这些标头更改时,请考虑使用 #ifdef __cplusplus 或等效的方式。有了这个,你可以使你的代码在两者上编译而没有任何问题。 - ssube
4
A ⊂ B和B ⊃ A在本例中都是相同的,且都是错误的。 - Marcelo Cantos
1
你可能想阅读这篇关于C和C++之间差异的文章:http://david.tribble.com/text/cdiffs.htm。 - Alexey Frunze
请注意,如果您只是输入“gcc x.cpp”或“g++ x.c”,那么实际上您将使用“正确”的编译器(即对于“x.cpp”是C ++,而对于“x.c”是C)。程序的名称仅仅是方便起见。因此,我假设您有一个真正的C ++程序文件,您想要“像C一样编写”? - Kerrek SB
3
C++不是C语言的超集。 - Lightness Races in Orbit
6个回答

8

是的。实际上,这通常是个好主意,因为C++比C实施了更强的类型检查。


2
是的,强类型检查非常重要。还可以添加变长数组指针。 - iammilind
更强的类型检查?在C和C++的交集中?那是一个都市传说。唯一的区别在于,在C中,您可以更轻松地将指针转换为void *,但这就是C++更加严格的全部内容。另一方面,C对于检查指针参数的“const”性更加严格。因此,我认为这种分类并没有太大帮助。 - Jens Gustedt
@JensGustedt:尝试通过你的C编译器运行这段代码:const int a = 0; int* b = &a; float* b = &a;。如果你的编译器是由明智的人编写的,它将会发出一些警告。这只能发出警告,因为这段代码是有效的C(即使是C99,据我所知)。是的,好的编译器引起警报的事实使这不再像是一个十拍进三的事情,但是我更希望一个强制执行正确行为而不是建议正确行为的编译器。我不理解const参数的争论。如果你指的是自动从字符串字面量中删除const的参数,那就只是一个C兼容性问题。 - Marcelo Cantos
@JensGustedt:为了展示这个问题的真实情况,在测试你在回答中提到的sizeof('a')时,我不小心写成了fprintf("sizeof('a') == %d", sizeof('a'));。gcc版本给了我一个警告,但我忽略了它,然后在运行时出现了总线错误;g++则显示了error: cannot convert ‘const char*’ to ‘FILE*’ ... - Marcelo Cantos
在C语言中,将int const*赋值给float*是无效的,这是一个约束违规。看起来你对C99并不了解。而C语言的第一条规则就是不应该忽略编译器给出的警告,就这么简单。如果需要,只需使用-Werror选项即可。我的gcc版本(称为c99)在你的虚假fprintf行上会输出三个可怕的警告,你怎么能有忽略它们的想法呢? - Jens Gustedt
@JensGustedt: 首先,有大量的代码是为 C99 之前编写的,但是几个当前的编译器 -- 特别是 gcc -- 默认情况下不支持编译 C99。实际上,即使在运行 const int*float* 转换时使用 gcc -std=c99 ... 也只会产生一个警告而非错误。其次,你错过了我的观点。C(相对于你选择的 C 编译器)与 C++ 没有相同的类型安全性。因此,C++ 编译器不会编译出这样的虚假转换,而 C 编译器必须使用选项来避免出现问题。我选择了 "enforces" 这个词很仔细。 - Marcelo Cantos

5

虽然大多数C源代码可以直接编译为C++代码,但是某些语言差异使得C++不能成为C的严格超集。

C中有效,但在C++中无效

  • C++有新的关键字(class、template、virtual等),如果你打算用C++编译器编译C代码,就不应该使用它们。
  • C++的类型转换更加严格:

C中有效,但在C++中无效。

int *j = malloc(sizeof(int) * 5);

在以下两个方面有效:

int *j = (int *) malloc(sizeof(int) * 5);
  • 在 C 语言中,枚举常量(枚举值)始终为 int 类型,而在 C++ 中它们是不同的类型,并且大小可能与 int 不同。
  • C 允许在函数原型中声明结构体、联合体和枚举类型,而 C++ 不允许。

在 C 和 C++ 中有不同的行为

  • 在 C 中,如 'a' 这样的字符字面量是 int 类型,而在 C++ 中是 char 类型
  • static 关键字用于将函数或全局变量限制在文件作用域(内部链接)。虽然这种用法在 C++ 中仍然有效,但 C++ 已经弃用了这种用法,而使用匿名命名空间代替它(该命名空间在 C 中不可用)。此外,在 C++ 中,任何 const 全局都被隐式地视为文件作用域,除非它明确声明为 extern,而在 C 中则是默认的。相反,在 C 中,内联函数是文件作用域的,而在 C++ 中,默认情况下它们具有外部链接。

您可以在这里找到详尽的不同之处列表:here


4
除了仅使用函数外,您还需要做一些其他的事情,特别是您应将所有函数标记为“extern “C” ”以避免名称重整和执行C调用约定(并防止过载)。如果您想要能够在C中编译它,那么在声明变量时必须使用struct限定类型(枚举使用enum),或者提供相应的typedefs。另外,您可以将-x c添加到编译器选项中,告诉g++将代码编译为C(如果您无法将命令行从g++更改为gcc,则可能无法添加编译器标志...)。

严格来说,只有在您打算与使用C编译器编译的代码进行交互时才需要这样做。系统库通常已经很好地将它们的内容包装在extern "C"{...}中了。 - Marcelo Cantos

3
你可以用许多风格来“写C ++”——这是该语言的基本优势之一。其中包括严格的过程式、扁平化编程风格,这种风格与C程序常见。你仍将编写C ++,但代码应该看起来非常熟悉于任何C程序员。
严格来说,你必须使用C++头文件等,而所有C库函数都在std命名空间中。也许这是你应该使用using namespace std;的少数合法情况之一! :-)

1
你不一定需要使用 <cstdio>。C++库保证提供 <stdio.h>(D.5)。好吧,它已经被弃用了,但在C++中,弃用意味着“当前标准要求存在,但未来标准可能不再需要”。相反,未被弃用的功能是当前标准要求存在,但未来标准可能不再需要的;-) - Steve Jessop
@SteveJessop:有趣——.h头文件是否也保证将符号放在全局命名空间中? - Kerrek SB
1
是的,而且不确定它们是否也进入了std - Steve Jessop

1

我不认为有任何理由这样做,g++和gcc只是同一个编译器的不同前端。因此,就效率、字节兼容性等方面而言,混合使用由两者产生的.o文件应该没有问题。

C和C++之间存在许多微妙的差异,可能会给你带来麻烦,从sizeof 'a'sizeof c不同(如果cchar)开始,到bool在其中一个中是类型,在另一个中是宏,true在C++中是bool类型,在C中是int类型,C不允许在for中进行static声明......

即使C和C++有很大的交集,如果你限制自己只使用两个社区都认为是良好编码实践的内容,你很快就会发现交集几乎为空。这涉及指针转换、使用mallocnew进行分配、复合初始化程序与构造函数、可变长度数组与向量类......

别这么做了。你所需要的只是创建一个适用于两者的良好接口。


你提到的问题都是红鱼。考虑到在 C 程序中 sizeof('a') 是多么不直观(出于我的无知,我不得不运行一个测试才能明白你在说什么),这很可能是一个错误。任何定义了 bool 类型的 C 库都应该有良好的判断 #ifdef __cplusplus ...(我的 <stdbool.h> 副本就是这样)。除非有人愚蠢到使用算术运算符操纵它们(这很可能是一个错误),否则 truebool 还是 int 不应影响为 C 编写的程序的正确性。最后,在 C 程序中,静态声明不会出现在 for 循环中。 - Marcelo Cantos
更重要的是,将旨在进行 C 编程的代码库——并仅使用 C 习语——通过 C++ 编译器运行几乎没有缺点。如果您担心出现沉默的漏洞,因为有人想两次减少 bool,那么只需测试并发布 C 编译器生成的内容即可。 - Marcelo Cantos

0
不,g++gcc不是同一个编译器的两个不同前端。
它们是两个不同编译器(cc1pluscc1)的两个不同驱动程序,共享相同的中间代码和后端代码(在GCC源码树内的gcc/libbackend.a中,这个名称有误,因为它包含了中间代码(中间代码是GCC的通用部分,适用于C、C++、Ada、Fortran、Objective-C等多种目标机器;它是GCC最大的部分,工作在一组常见的内部表示中,尤其是Gimple)和后端(GCC的部分将Gimple表示转换为汇编代码,使用RTL - 一种目标相关的内部表示)。
但是cc1和cc1plus有不同的前端代码。

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