我和朋友正在讨论,我们想知道为什么这么多开源项目决定用C而不是C++.像Apache,GTK,Gnome等项目选择了C,但为什么不用C++,因为它几乎是一样的?
我们正在寻找导致这些项目(不仅是我列出的项目,而且是所有C项目)的原因,而不是使用C而不是C++.主题可以是性能,易于编程,调试,测试,概念等.
C非常便携,远远超过10年前的C++.
此外,C在Unix传统中非常根深蒂固.阅读" Unix编程的艺术 ",关于Unix和OO的一般内容,以及关于unix(包括C和C++)的特定语言的更多内容.
有许多反例:一切都基于Qt.
另外,在我的Debian测试系统上:
edd@ron:~$ apt-cache rdepends libstdc++6|wc -l 4101
这是4101包,具体取决于基本的C++库.为了比较,我获得了大约14,982个libc6或大约3.6个.但是,如果在开源领域没有任何C++项目,那就不是了.
编辑: Thinko就我而言:由于C++包也依赖于libc6,因此比例确实如此
(14982-4101)/ 4101 = 2.65
所以在C中实现的包大约是C++中的两倍.
Eric Raymond的精彩书籍"Unix编程艺术" 对这个问题有一些反思(整本书非常值得阅读论文或免费在线版本,我只是指向相关部分 - Eric参与了创造和引入"开源"一词,总是值得一读; -0).
总结这一部分,Raymond声称"OO语言显示出一些倾向于让程序员陷入过度分层的陷阱",而Unix程序员(以及扩展的开源程序员)则抵制了"厚胶"的陷阱.
在本书的后面部分,您会发现一些特别关于C++的注意事项,例如"可能是C++对OO的实现特别容易出错".无论你是否同意,整篇文章都值得一读(我在这里很难做到这一点! - ),而且参考书目丰富,指向许多其他相关的研究和出版物.
Linus Torvalds在C++主题上多次咆哮 - 他使用C代替git,当然Linux内核主要是C:
关于C++和git(警告:首先要阻燃)
从1998年开始接受Linus的采访
你可以很容易地找到更多这些,虽然它本质上是对这些东西有点躁动,但有一些有效的观点.
其中一个更有意思的(无论如何我坐在那里)是观察到C++编译器和库(并且在某种程度上)比相应的C编译器更多的错误.鉴于两种语言的相对复杂性,这是有道理的.
它闻起来有点"不是在这里发明"(NIH)综合症,但是当你拥有整个Linux内核开发者基础时,你有时可以重新发明"正确的方法".
许多项目在C++标准化之前就开始了,所以C是明显的选择,后来的改变很难.C在C++之前大约十年被标准化,并且在更长时间内更加便携.因此,它在很大程度上是一个务实的决定,部分受到Unix大多数代码使用C的遗产的启发.
C++很乱.这是一个过于复杂的语言,如此复杂,只有少数人可以说他们知道所有的比特.并且更少的编译器真正符合C++标准.
所以我认为原因是简单性和可移植性.
如果你想要更高级和面向对象的编程,那么我认为C++只是与Python之类的其他人竞争.(请注意,我用C++编程了几年,速度很快,并且有一些来自高级语言的功能,可以加速开发,而不是冒犯.)
我在我的时间里参与了一些C++项目,所有这些项目都以某种方式流下了眼泪.在最基本的层面上,事实是人们不可信任.他们不能信任编写好的代码,他们无法信任调试它,当他们不得不回来并在几周/几个月后再次修改它们时,他们肯定无法理解它们.
C代码在C++中没有很多奇怪的东西,这使得它很难调试(构造函数/析构函数,在cpp_initialize()时间内与静态全局对象一起发生的任何事情等).这使得在开发和维护大型项目时更容易处理.
也许我是一个luddite,但每当有人在我身边说"C++"时,我就会感到沮丧.
有些人提到了可移植性,但是在这一天,C++的可移植性并不是一个大问题(它运行在任何GCC运行的基础上,基本上都是什么).但是,可移植性不仅仅是架构到架构或操作系统到操作系统.在C++的情况下,它包括编译器到编译器.
我们来讨论ABI或应用程序二进制接口.这基本上意味着"代码如何转换为汇编".在C中,当你写:
int dostuff(const char *src, char *dest);
您知道您在目标文件中创建了一个符号_dostuff
(C全局名称都在结果程序集中以下划线作为前缀).但是在C++中,当你写这个:
int dostuff(const char *src, char *dest); int dostuff(const char *src, char *dest, size_t len);
甚至:
int dostuff(std::string src, std::string dest);
所有投注立即关闭.您现在有两个不同的函数,编译器必须创建每个函数,并且必须为每个函数指定一个唯一的名称.所以C++允许(我相信C不这样做)命名mangling,这意味着这两个函数可能被转换为_dostuff_cp_cp
和_dostuff_cp_cp_s
(因此每个版本的函数使用不同数量的参数具有不同的名称).
这个问题是(我认为这是一个巨大的错误,即使它不是C++中交叉编译器可移植性的唯一问题)C++标准留下了如何将这些名称修改为编译器的细节.因此,当一个C++编译器可以做到这一点,另一个可以做_cp_cp_s_dostuff
,还有一个可以做 _dostuff_my_compiler_is_teh_coolest_char_ptr_char_ptr_size_t
.这个问题加剧了(总是找到一种方法将这个词隐藏到你说或写的任何东西),因为你不得不破坏名称而不仅仅是重载函数 - 如何处理方法和名称空间以及方法重载和运算符重载以及... (列表继续).只有一种标准方法可以确保您的函数名称实际上是您在C++中所期望的名称:
extern "C" int dostuff(const char *src, char *dest);
许多应用程序需要有(或至少发现它非常有用的)的标准ABI由C.阿帕奇提供,例如,不能几乎一样的跨平台和易于扩展的,如果它是在C++ -你必须考虑到特定编译器的名称损坏(以及特定的编译器版本 - GCC在其历史记录中已经改变了几次)或要求每个人普遍使用相同的编译器 - 这意味着,每次使用a升级C++编译器时向后兼容的名称修改方案,你必须重新编译所有的C++程序.
这个帖子变成了一个怪物的东西,但我认为这说明了一个好点,我太累了,不能试图削减它.
作为一个不喜欢C++而且会在任何时候选择C的人,我至少可以给你关于这个主题的印象.C++有几个属性使它没有吸引力:
复杂的对象.C++有很多加速OO的能力,这使得语言变得非常复杂.
非标准语法.即使在今天,大多数C++编译器都支持这样的怪癖,这些怪癖可以确保编译器之间的编译成功和正确.
非标准图书馆.与C库相比,C++库几乎没有跨系统标准化.我不得不处理与此相关的问题之前我可以告诉你,使用C可以节省大量时间.
也就是说,C++确实具有支持对象的好处.但归根结底,即使是大型项目,也可以在没有对象的情况下实现模块化.当你添加一个事实,即基本上每个可能为任何项目贡献代码的程序员都可以编写C语言时,如果你需要编写接近金属的代码,那么很难选择其他任何东西.
总而言之,许多项目跳过C++并转向Python,Java或Ruby等语言,因为它们提供了更多的抽象和更快的开发.当你添加它们支持从C代码编译/加载需要性能的部分时,C++失去了它可能拥有的优势.
如果你看看最近的开源项目,你会发现其中很多都使用C++.例如,KDE的所有子项目都是用C++编写的.但对于十年前开始的项目来说,这是一个冒险的决定.当时C正式和实际上都是标准化的(编译器实现).此外,C++依赖于更大的运行时,并且当时缺少良好的库.您知道个人偏好在此类决策中起着重要作用,当时UNIX/Linux项目中的C员工远远大于C++,因此新项目的初始开发人员更容易使用C语言.更大.此外,任何需要公开API的项目都会在C中执行此操作(以避免ABI问题),因此这将是支持C的另一个参数.最后,在智能指针变得流行之前,使用C++编程更加危险.你需要更多熟练的程序员,他们需要过分谨慎.虽然C具有相同的问题,但使用边界检查工具/库更容易调试其更简单的数据结构.
还要考虑C++只适用于高级代码(桌面应用程序等).内核,驱动程序等不适合C++开发.C++有太多"幕后"行为(构造函数/析构函数链,虚拟方法表等),在这样的项目中,您需要确保生成的机器/汇编代码不会有任何意外,并且不依赖于运行时图书馆支持工作.
毫无疑问,除了其他语言之外,还有一个重要的方面是C更容易与其他语言交互,因此,对于旨在广泛使用的库,即使是现在也可以为此目的选择C.
举个例子我熟悉,工具包GTK +(在C中)具有强大的OCaml绑定,而Qt和Cocoa(分别在C++和Objective C中)只有这种绑定的概念验证.我认为将C语言与OCaml之外的语言接口的难度是其中一部分原因.
一个原因可能是GNU编码标准特别要求您使用C.我能想到的另一个原因是自由软件工具使用C比C++更好.例如,GNU缩进不像C一样做C++,或者etags不解析C++,也解析C.