何时在C ++上使用C,以及在C上使用C ++?


164

我被介绍给计算机科学已经一年多了,从我的经验看来,C和C ++都被认为是“超快速”语言,而其他人(例如Python)和此类脚本语言通常被认为较慢。

但是我也看到过很多情况,一个软件项目甚至一个很小的项目都会交织文件,其中一定数量的文件用C编写,而一定数量的文件用C ++编写。

(我还注意到,C ++文件几乎总是具有相应的头,而C文件则没有那么多)。但是我的主要研究目的是对何时在C ++上使用C合适,以及何时在C上使用C ++更好的情况有一个一般的直觉。除了(1)C ++是面向对象的,而C ++是面向对象的, C不是,并且(2)语法非常相似,并且C ++是故意创建的,在许多方面都类似于C,但我不确定它们之间的区别是什么。在我看来,它们在许多领域(几乎)是可以互换的。

因此,如果有人可以解决这种情况,我们将不胜感激!谢谢


4
在C ++代码中通常使用C内联用于某些模块,这些模块需要高度优化,在硬件附近进行非常低级的工作,或者对于数据完整性甚至人身安全至关重要,并且需要进行审核并证明是正确的。除了在C中完成所有工作外,大部分项目都可以利用C ++功能进行灵活的设计,同时在需要C的地方获得C的紧密性的好处。
kylben 2011年

30
@kylben:许多C ++专家都会告诉您:(1)性能并不是降低到C的原因(也许要避免使用virtual以及其他一些阻止优化的功能,但是例如非virtual类并不是天生就效率低下,而模板是强大的抽象工具,它实际上可以导致有效的-例如,qsortVS std::sort)。(2)正确性的高度重要性是使用C ++(类型安全性,constness,privateRAII以使资源管理可管理等)的原因。或者为此,首先使用Ada或其他东西。

11
@ Pubby8我不同意这一点。如果我正在处理.c文件,并且看到有人这样做,则我倾向于在心理上标记他们不知道他们在做什么。例如,有没有必要从投void*在C代码中的另一个指针类型,这是非常分散注意力,典型的人谁不知道C.
asveikau

4
@kylben :(您可能想学习在评论答复中正确地解决其他问题,因此他们有机会实际看到他们。)“一个非常熟悉编译器如何将C转换为asm的程序员”对于C ++一样适用。好。但这根本不相关:如果您想涉足asm,只需编写asm即可,而不是让编译器使用其他语言来创建它。毕竟,它的执行方式可能会随每次编译器更新而改变。
2011年

9
以我的愚见...对于您而言,您随时可以使用C,对我来说:C比C ++更简单易用... C ++看起来像是“带有类的C”,但现在已经不复存在了一种非常复杂的语言,其中包括虚拟构造函数和模板。
dysoco

Answers:


184

你选C的时候

  • 无论出于何种原因,您都需要可移植的汇编程序(实际上就是C的意思),
  • 您的平台不提供C ++(C编译器更容易实现),
  • 您需要与只能与C进行交互的其他语言(通常是任何平台上的最低公分母)进行交互,并且您的代码仅包含接口,因此不值得在C ++代码上放置C接口,
  • 您入侵了一个开源项目(出于各种原因,其中很多人坚持使用C语言),
  • 你不懂C ++。

在所有其他情况下,您应该选择C ++。


15
我还要补充一点,带有异常模型的C ++有时会带来更多麻烦,例如OS内核。至少那是我在阅读有关东西时得到的一般感觉。
编码器

12
@SF:C是通用语言?那是新的。或者说,很老了。也许,如果您只与过去20年没有学习新语言的人交谈,但是我会说C知识不再是很普遍的了。
DeadMG

13
@SF .:正如我在其他地方所写的那样,我参与的项目数量达到了数百万个LoC,与C项目相比,由于不可避免地存在着无处不在的宏黑客,因此看到的元数据很少。(OTOH,在需要时创建EDSL的能力可能是您盒子中非常强大的工具。)至于C是通用语言:我宁愿坚持认为它是最低的公分母。而且我也不希望那些具有中等编程技能的人攻入OS内核。
2011年

18
@Max:我完全不同意。C是一种无用的语言,除非某些无法克服的实际障碍阻止了C ++的使用。
DeadMG

17
@Buttons:是您提出了要求(“ C ++需要更多的内存”),因此应该由您来支持。而且,不,我并不是说C ++需要更少的内存。我的意思是,无论编译器是为您实现这些功能(虚拟函数)还是由您自己实现(功能指针数组),这些功能都是成本。
2012年

88

有几个理由偏爱C。主要的原因是,使用C ++生成真正的小型可执行文件通常会更加困难。对于非常小的系统,无论如何您几乎都不会写很多代码,并且C ++而不是C所需要的额外ROM空间可能会很大。

但是,我还应该补充一点,对于非常小的系统,C出于完全相同的原因会遇到问题,而汇编语言几乎是唯一合理的选择。C真正有意义的系统大小范围很小,并且会不断缩小(尽管我会承认,相当缓慢)。

使用C的另一个时间/原因是提供可以从实质上任何其他语言绑定到的一组函数。您可以通过将它们定义为函数来用C ++编写这些函数extern "C"但是这样做会将这些函数限制为向世界呈现本质上是C-life的“面孔”-类,重载函数,模板和成员函数等不需要应用。但这并不一定将开发限制在C中,只要内部接口看起来像C,内部使用任何方式的C ++功能都是完全合理的。

同时,我不得不说@Toll的答案(举一个明显的例子)在大多数方面都有些倒退。合理地编写C ++通常至少会和C一样快,并且通常至少快一点。可读性通常会好得多,即使是因为您即使对于最琐碎的算法和数据结构,所有错误处理等,也不会陷入所有代码的雪崩之中。

模板并不能“解决语言类型系统的问题”,它们只是添加了一些基本功能,而没有模板的C和/或C ++几乎没有这些功能。最初的目的之一是提供类型安全的容器,但实际上它们远远超出了它—本质上C根本没有提供。

自动化工具在很大程度上也很容易引起人们的注意-确实,编写C解析器比编写C ++解析器要少工作,但事实是,最终并没有什么区别。很少有人愿意或能够为任何一个编写可用的解析器。因此,无论哪种方式,合理的起点都是Clang。

碰巧的是,C和C ++经常在同一项目中由同一个人维护在一起使用。这使得东西是另有相当罕见:一个研究直接,客观的代码的人谁也同样有能力的整体(即完全一样的人)写在两种语言的可维护性比较。至少在链接研究中,一个结论是明确而明确的:“我们发现使用C ++而不是C可以提高软件质量并减少维护工作……”


12
工具支持不是一个红鲱鱼。当然,如今我们有了we。但是,即使在大型IDE中,对C ++的工具支持也确实远远落后于其他语言。这是为什么?很简单,因为直到最近还没有c(GCC从来没有替代品)。直到大约半年前,如果您需要AST的C ++代码,您基本上就不走运了,也没有几千美元(如果您购买了EDG前端)。
Konrad Rudolph

5
+1,作为记录,我经常为带有4KiB ROM的8位处理器编写C ++代码。
阿瓦卡2011年

2
+1是一个很好的总体答案。我不了解(我对此没有经验)是为什么(我想我们正在谈论嵌入式的?),给定使用的相同功能集, C编译器应产生比C ++编译器小的可执行代码?也许您可以提供一些参考?
马丁·巴

2
@Martin:主要是C ++包含异常处理,(至少通常)这会增加可执行文件大小的最小值。大多数编译器将允许您禁用异常处理,但是当您执行此操作时,结果将不再是C ++。我怀疑还有一个简单的事实,那就是许多C ++编译器供应商在产生最小的输出代码方面都没有那么努力。
杰里·科芬

3
“我们发现使用C ++代替C可以提高软件质量并减少维护工作量。”这是要记住的结论。
Stephane Rolland

24

C和C ++之间的区别已在此处详细列举。尽管有时候人们可能出于正当理由选择一个或另一个(例如,当C ++的额外功能带来了不希望的开销时,C ++用于OOP或C),但根据我的经验,这通常只是出于偏爱。处理此文件的人更了解和喜欢什么?我相信这是大多数时候的原因,因为这些语言都可以处理性能关键型应用程序。

(旁注:查看Linus Torvads对为什么他更喜欢C而不是C ++的。我不一定同意他的观点,但是它使您了解了人们为什么会选择C而不是C ++。相反,那些同意他的人出于这些原因,可以选择C。)


51
-1为莱纳斯的怒吼。:-{
sbi 2011年

12
为此,不要减去我!哈哈。我不同意Linus,但这是一个很好的例子,为什么人们可能会选择C而不是C ++(如果他们相信Linus的话)。我没有评论这些理由的合法性。
Casey Patton

10
@CaseyPatton:基本上,我会否决所有评论,这些评论无可置疑地表达了这种夸夸其谈的言辞,就好像它是一个真实的论点一样。
2011年

11
@Coder:您完全不需要了解STL实现。STL的重点是您不必了解实现,除非您依赖标准未定义的行为-在这种情况下,为什么要麻烦使用标准库?而且,由于开发人员的行为方式,不喜欢某种语言不只是一点点的疯狂。C程序员的行为就像C是上帝赐予人类的礼物,并且过于盲目以至于看不到C ++提供从根本上和本质上直接优于C的功能(例如RAII)的明显事实。
DeadMG,2011年

8
@Coder:如果最后对单个对象使用了太多shared_ptrs导致内部计数器溢出,那么您做错了。该标准将为计数器指定最小大小(可能为32位),并且对于一个对象必须有超过20亿的shared_ptrs,这是不现实的。即使对象本身的大小为0,并且您具有零开销的内存分配器,但您仍然在shared_ptrs上消耗了16GB的内存。
DeadMG

13

选择是现有答案中所缺少的主要问题(截至发稿时)。

这很简单。如果出于某种完全不合理的原因,您认为例外不值得开销,那么您就不必使用它们。您仍然可以拥有模板,RAII和Standard库,并且永远不会编写单个“ throw”。模板也是如此。如果由于某种原因,您认为它们导致不可挽回的(实际上很重要,这仅在嵌入式系统中)膨胀,那么令人惊讶-您也可以整天使用void *和sizeof(T)。没有什么可以迫使您使用C之上的任何C ++功能。

这就是为什么C ++是一种固有的高级语言的原因-您可以选择所需的功能,而在不喜欢给定功能时可以使用C风格的编程。因此,考虑到C ++是C的全部,甚至更多,显然,C ++是一种高级语言。否则建议就像试图建议4大于5一样。


1
按照您的推理,原始问题完全没有意义,因此应将其关闭。我想这个问题应该读成这样:什么时候我们应该将自己限制为C ++的C子集(使用纯C),什么时候应该使用完整的C ++?
乔治

这是正确的,但仅适用于一个人在自己的小项目上工作的情况。在现实生活中,几乎每个人都可能花费一半的时间来编写其他人的代码。不幸的是,大多数其他人对于这些完全不合理的原因“认为错误”。
2011年

1
@DeadMG:分配器如何在不引发异常的情况下报告错误?同样,添加更多功能并不一定就是增加复杂性或冗余性。
Mankarse 2011年

@Mankarse:如果您使用禁用异常的选项进行编译,则分配器要么终止程序,要么根据库的实现轻松使用空指针。
Zan Lynx 2012年

4
@Mankarse:由于我在2007年的经验,当我尝试运行具有1 GB RAM并且没有交换空间的Linux系统时,无论如何内存分配失败,几乎所有台式机软件都会以可怕的可怕方式失败。
Zan Lynx

9

有关C ++的事情使C程序员感到紧张

引擎盖下发生了很多魔术。构造函数,析构函数,虚方法,模板等可以使C ++代码比同等的C代码更容易编写和编写,但更难以理解和推理(取决于您对C ++及其关联约定的了解程度)。简单的操作Foo newFoo;可能会调用许多代码,具体取决于如何Foo定义类(及其依赖的任何类)的构造函数。这也是为什么约定是写++it而不是it++遍历容器的原因,因为后缀++通常涉及昂贵的复制操作。

根据您的工作,可能会有一些不小的开销,尤其是对于简单的任务。采取以下两个程序,第一个在C中,第二个在C ++中:

/* C version */
#include <stdio.h>
int main(void)
{
  char greeting[] = "Hello, world";
  printf("%s\n", greeting);
  return 0;
}
/* end C version */

/* C++ version */
#include <iostream>
#include <string>
int main(void)
{
  std::string greeting("Hello, world");
  std::cout << greeting << std::endl;
  return 0;
}
/* end C++ version */

行为相同,在来源方面并没有很多差异,但是在我使用gcc 4.1.2的SLES 10盒上,前者生成了〜9kb大小的可执行文件,而第二个则占用了12.5kb的文件大小(无优化) ),几乎增加了28%。string与C字符串库相比,C ++ 类型在IMO中的使用要容易得多,C ++流比C流更灵活和可自定义,但是对于像这样的脑筋急转弯的代码,它们可能不值得开销。

与C相比,C ++是一种庞大的语言,具有一些极其复杂的语义。精通C ++比C花费更长的时间,这意味着许多声称知道C ++的人并不像他们认为的那样了解它。

有关C的事情使C ++程序员感到紧张

无论从哪方面讲,C都不是安全的编程语言。没有界限的阵列检查导致大量的开采行为(无论是通过现在死的gets功能,或通过scanf%s%[转换说明)。如果您尝试访问超出其当前定义范围的容器,C ++至少会为您提供引发异常的容器。C给您的所有内容都是(如果您幸运的话)细分违规。

与C ++为您提供的工具相比,C中的内存管理非常耗时且容易出错。如果您要构建自己的容器,则负责对所有mallocfree调用进行匹配,确保分配成功,并在发生错误时撤消部分分配等。在C ++中,您只需向或添加项目从容器中取出物品。如果有问题,将引发异常。

类似地,与C ++提供的工具(即,异常)相比,C中的错误处理是一件麻烦事。真正有趣的是,当您分配了一堆内存,然后在处理过程中碰壁时;因为必须退出,所以必须以正确的顺序释放该内存。使用C ++和RAII原理,(相对)容易做到这一点。

那么什么时候我可以一个使用另一个呢?

如果您要编写的内容很简单,那么请阅读该应用程序/随便处理该应用程序/摆脱它的应用程序,该应用程序的行为可以用输入和输出以及性能方面的方式进行清楚地描述,那么C胜于C ++。否则,建议使用C ++


2
内存管理很复杂,在某些情况下容易出错,但是特别是在嵌入式世界中,使用完全静态的内存分配编写C程序通常是实际的。如果程序链接,则在运行时不会耗尽内存。这样的保证可以在C ++中轻松实现吗?
2014年

9

Bjarne Stroustrup维护使用C ++ 的应用程序和公司列表。您可以争论所有想要的过程式编程还是OOP编程,但是不能与过去20年的行业结果相抗衡。

C ++通常用于大型,多人,复杂的项目,这些项目需要单独的人来处理模块化组件。您当然可以用C构建和维护模块化的代码,但是C ++固有的OOP性质导致了出色的模块化,可测试性和代码重用性。

C ++标准库(STL)本身仅包含矢量和地图,足以使用C ++。

C通常用于嵌入式系统。

只有在某些库中只有C API时,我才会亲自使用C。


19
您的最后一句话不是使用C的理由。您可以从C ++调用C库。
user207421 2011年

2
我用c ++的DSP项目-不是C
BЈовић

9

我要说的是,我之所以选择C而不是C ++,主要是仅当我不得不诉诸于“ NAS必须具有1000%的稳定性”时。

当我们查看性能时,C ++的C值为〜99%,并且生产力更高。因此,即使在C语言中,您也可以编写比C ++更快的代码(您也可以使用C ++的子集,而没有例外,虚拟,流式传输,抽象等,但基本上就是C),这是优化每件事的时间尽管STL已经过测试并且已经这样做了,但是,由于STL算法是由专家组编写的,并且您可能不是所有专家,因此,付出的代价将超出您可能会达到或牺牲的微不足道的性能提升。

另一方面,C ++有大量的抽象。在某些情况下它们会泄漏,这会给您带来麻烦。很少有人知道100%的C ++陷阱,而我想知道更多的人知道所有C陷阱,因此编写解决方案以使团队的所有成员完全理解每个步骤在C语言中要容易得多。

示例:您知道什么时候shared_ptr<smthn>它将溢出其引用计数,它将引发异常吗?当航天飞机必须重新进入大气层时,这类事情并不酷,至少我猜是这样。

而且,与错误代码相比,异常处理非常非常困难。很难看出该类是否是100%异常安全的,并且容易陷入泄漏。许多高级代表都表达了这种观点。


12
以及以哪种方式手动管理内存比C ++的抽象之类“更稳定” std::string?您是否曾经尝试过指定shared_ptr计数器溢出的平台?那将是一个有趣的平台。而且,如果您认为异常处理很困难,则应该看一下一段C代码,该代码检查每个函数调用中的每个可能的错误。(我承认这样的代码很难获得,但这只是反对您的陈述的更强力论据。)对不起,但这确实是牛粪。
2011年

12
@Lundin:““必须具有1000%的稳定性”实现方式不允许首先进行动态内存分配”。是什么让您无法在C ++中做到这一点?(要发表关于我的知识和经验的
笼统

10
@Lundin:很好,您已经开始提供论据,而不是夸夸其谈。但是他们很虚弱。您已经“忘记”了C ++(模板)的主要功能之一,它使代码更安全(因为它允许算法得以执行-从而在编译时失败,从而消除了运行时错误),但这并没有说出您所要判断的语言的知识。出于充分的理由,在此之前,人们曾批评将C ++还原为OO语言。(此外,具有确定性破坏的类是一个很好的工具,并且对管理其他资源(不仅仅是内存)
很有帮助

9
@Lundin当然,std::string如果您不希望动态分配,就不会使用。你会用std::basic_string<char, std::char_traits<char>, some_allocator_here>
Luc Danton

10
@Coder:您认为您用这些证明什么?第一个简单的代码就是错误的代码(报告错误和返回值一样严重),第二个代码为RAII提供了手动清理的理由,每个半个体面的C ++开发人员都会为之欢欣鼓舞,Joel和我一样尊重他,确实说了一些我非常不同意的事情。他单进单出的塞子臭透了,一个不知情的老屁,他永远也不会同意他25年前学到的东西已经被超越了。(请记住,我 25年前编程的时候,当时SESE是最先进的技术。)
2011年

6

C是具有更好语法的可移植程序集,使程序员可以完全控制所有内容

另一方面,C ++会执行很多时髦的魔术(虚拟函数,重载,自动转换等),当您要确保自己:

  • 不要使用过多的内存
  • 不要访问内存页面willy nilly(vtable可以在任何地方)
  • 不要意外地调用太多代码

并希望您能真正简单地使用某些东西,因为您专注于性能。

只是没有惊喜,这是非常有价值的。

如果您愿意(并且我建议这样做),请阅读JSF编码指南,以了解在编写C ++用于军事航空电子控制时需要考虑的事项。您需要了解很多陷阱,它可能会吸引您。Bjarne是该文档的一部分,因此他知道该文档的内容。

而且,C就像被闪电击中的灼热巨魔一样进行编译。C ++(OTOH)可能是由投资SSD公司的人赞助的。:)

(就我个人而言,虽然我更喜欢C ++,但是我也不喜欢它……。;-P)


1
C不能控制很多事情。尝试编写高效的可移植代码,以将uint32_t与uint32_t相乘以产生uint32_t结果(产品的底部32位)。如果an int为64位,则必须强制转换至少一个操作数以uint64_t防止出现Undefined Behavior,但是为了计算32位结果而不得不转换为64位,这有点让人感到“惊讶”。
2014年

不是。编译器会为您执行诸如寄存器分配之类的事情。我不能用CI编写汇编中的可维护代码。
Nils 2014年

2

(假设您对两种语言都具有相同的熟悉程度)

除非您的平台没有C ++编译器,否则请使用C ++。您可以编写C ++代码,而无需使用您不喜欢的语言的任何部分(没有类,异常,虚拟继承,您希望应用的任何个人限制),然后在将来的某个时候决定是否需要毕竟那些功能,那么您可以轻松地使用它们。C ++中的任何内容都不能阻止您编写C风格的代码。

(考虑到等效的工具集和开发人员知识),只要您的平台具有C ++编译器,就没有理由选择C而不是C ++。您可以简单地将自己局限于今天想要使用的语言的子集,同时为以后的扩展打开方便之门。


1

两种语言都很出色。我认为许多海报详细说明了每种海报的优势和各种用途。我将简单地添加以下内容:

我认为C语言在以下四个方面是完美的:1)我认为这是首次学习任何类型的编程时最好的语言[结合一些汇编和机器代码知识],2)非常适合编写驱动程序,3)嵌入式软件,以及4)最低级别的系统软件。

C ++是一种面向对象的语言,但它也可以是过程性的(非常类似于C语言)。如果您正在从事大型项目,基于GUI的软件,游戏软件以及其他类型的图形密集型软件,那么我认为C ++,Java甚至是Objective-C是您的最佳选择。但是,在许多命令行程序或系统软件中,您可能会发现C ++的性能与C相同或更好。


0

在我看来,此讨论中缺少一点:在C语言中,从库中提供稳定的二进制接口比较简单。都可以与其他语言以及C ++一起使用。

在C ++中,不同的编译器使用不同的名称修饰,因此使用与库不同的编译器编译的库的使用者可能会遇到问题。通常,使用C将二进制接口标准化为平台。

我知道,当今的编译器经常有开关来产生与gcc兼容的东西,但这并不总是有用的。

我在Solaris上相对经常观察到这一点。发行版和不同的软件供应商通常将Sun Studio用作,尤其是在Sparc系统上,它通常可以提供更好的结果。男人开源项目是用gcc特定代码编写的。让那些人一起工作可能会有些痛苦。


0

生成C代码时(例如,在高级语言的实现中),C可能比C ++更可取。例如,有几种类似Lisp的编译器会发出C代码(例如ChickenScheme48 ...),但我不知道会发出真正的C ++代码(我的MELT工具会发出C ++代码,但我不会将其称为真正的代码) C ++代码,它使用的C ++功能很少。

C代码也更容易半自动证明。诸如Frama-C之类的静态分析器(您可以在其中使用ACSL注释注释C代码,以帮助证明代码的人证明其原因)可用于C,但对于完整的C ++ 11而言却不多。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.