人们为什么使用C这么危险?


132

我正在考虑学习C。

但是,如果人们可以“危险地”使用C(或C ++),为什么还要使用它呢?

危险是指指针和其他类似的东西。

就像堆栈溢出问题一样,为什么gets函数如此危险以至于不应该使用它?。为什么程序员不仅仅使用Java或Python或其他编译语言(如Visual Basic)?


152
如果厨师可以“危险地”使用,为什么他们要用刀呢?
oerkelens

78
拥有权利的同时也被赋予了重大的责任。
Pieter B

10
乔·布洛(Joe Blow),崇尚多大?
马修·詹姆斯·布里格斯

4
因为当C成为“选择的语言”时“ Back In The Day”,我们被期望能够处理这样的事情,因为我们必须这样做。解释型或字节编码的语言太慢,因为当天的处理器要慢得多。(今天,我可以以279美元的价格从Dell 购买具有2+ GHz多核CPU和4 GB内存的低端台式机。对于像我这样的4 MHz PC 的家伙来说,这真是太不可思议了。拥有640 KB的内存真是幸福...)。面对现实-摩尔定律获胜。游戏。 过度!
Bob Jarvis

6
@Bob Jarvis:游戏还没有结束。如果您认为自己的2 + GHz,4GB PC-或与此相关的数百台装有最新CUDA GPU的4 GHz PC集群或其他设备-足够快,那么您根本就没有解决足够困难的问题:-)
jamesqf

Answers:


246
  1. C早于您想到的许多其他语言。我们现在所知道的有关如何使编程“更安全”的很多知识都来自对C之类的语言的经验。

  2. 自C以来出现的许多安全语言都依赖更大的运行时,更复杂的功能集和/或虚拟机来实现其目标。结果,在所有流行/主流语言中,C仍然是“最低公分母”。

    • C是一种更容易实现的语言,因为它相对较小,即使在最弱的环境中也更有可能正常运行,因此许多需要开发自己的编译器和其他工具的嵌入式系统都更有可能提供功能性的编译器对于C。

    • 由于C是如此之小和如此简单,因此其他编程语言倾向于使用类似C的API进行相互通信。这可能是C永远不会真正死亡的主要原因,即使我们大多数人仅通过包装与之交互也是如此。

  3. 许多试图在C和C ++上进行改进的“安全”语言并不是试图成为“系统语言”,它们几乎可以完全控制程序的内存使用和运行时行为。的确,如今越来越多的应用程序确实不需要这种级别的控制,但是总有少数情况是有必要的(特别是在虚拟机和浏览器中,这些虚拟机和浏览器实现了所有这些美观,安全的语言)我们其余的人)。

    如今,有几种系统编程语言(Rust,Nim,D等)比C或C ++更安全。它们具有事后观察的优点,并且意识到在大多数情况下不需要这种精细控制,因此提供了一种通常安全的接口,其中包含一些在确实需要时可以切换到的不安全钩子/模式。

  4. 即使在C语言中,我们也学到了很多规则和准则,这些规则和准则往往会大大减少实践中出现的隐患。通常不可能获得追溯执行这些规则的标准,因为那样会破坏太多的现有代码,但是通常使用编译器警告,lint和其他静态分析工具来检测此类易于预防的问题。通过这些工具而获得成功的C程序子集已经比“仅仅C”要安全得多,并且如今任何有能力的C程序员都将使用其中的一些。


而且,您永远也不会像C语言混淆竞赛那样使Java语言混淆竞赛有趣。


7
“最低公分母”听起来令人失望。我想说的是,与许多其他较重的语言不同,C不会强制执行您不需要的大量额外行李,并为您提供了实现所需内容的快速基础。这是你的意思吗?
underscore_d

9
“没有其他语言,您真的不可能拥有一个。”:实际上,尝试了许多新语言(Rust,Nim,D等)。基本上可以归结为在绝对需要这种级别的控制时,将语言的“安全”子集与几个“不安全”原语进行匹配。但是,所有这些都是基于C和C ++积累的知识,因此也许应该说,在开发C和C ++时,您不能一概而论,如今,有些语言试图将其划分他们的“不安全”钻头,但尚未赶上。
Matthieu M.

6
1)不是有效点!C借鉴了Algol 68的功能,Algol 68就是这种意义上的“安全”语言。所以作者知道这种语言。其他几点很棒。
reinierpost

4
@MatthieuM。您可能还想看看Microsoft Research。在过去的一两个十年中,他们已经生产了一些托管的OS,其中有些相对于某些实际工作负载而言,与同等的非托管OS相比,速度更快(完全是偶然的-大多数此类研究OS的主要目标是安全性,而不是速度)。 。加速主要是由于安全约束-静态和动态可执行文件检查允许进行非托管代码中不存在的优化。总而言之,有很多值得一看的地方,并且包括了资源;)
六安

3
@Luaan:Midori看起来很棒。我一直很期待Joe的博客文章。
Matthieu M.

41

首先,C是一种系统编程语言。因此,例如,如果您编写Java虚拟机或Python解释器,则需要使用系统编程语言来编写它们。

其次,C提供了Java和Python之类的语言无法提供的性能。通常,Java和Python中的高性能计算将使用以诸如C之类的高性能语言编写的库来完成繁重的工作。

第三,与Java和Python等语言相比,C的占用空间要小得多。这使得它可用于嵌入式系统,而嵌入式系统可能没有支持大型运行时环境和Java和Python等语言的内存需求所需的资源。


“系统编程语言”是一种适合用来构建工业强度系统的语言;就目前而言,Java和Python并不是系统编程语言。“究竟是什么使系统编程语言产生”不在此问题的范围内,但是系统编程语言确实需要为使用基础平台提供支持。

在另一方面(在响应评论),一个系统编程语言并没有需要自我托管。之所以出现此问题,是因为最初的问题是“人们为什么使用C”,第一个评论是“当您拥有PyPy时为什么要使用像C这样的语言”,我注意到PyPy 实际上确实使用了C。最初与该问题有关,但不幸的是(令人困惑的)“自我托管”与该答案实际上无关。对不起,我提出来了。

因此,总而言之:Java和Python不适合系统编程,不是因为它们的主要实现被解释了,或者不是因为本机编译的实现不是自托管的,而是因为它们没有提供使用底层平台的必要支持。


19
“如果您编写Java虚拟机或Python解释器,则需要使用系统编程语言来编写它们。” 嗯?您如何解释PyPy?为什么需要像C这样的语言来编写编译器,解释器或虚拟机?
Vincent Savard

10
我确实相信您说“如果您编写Java虚拟机或Python解释器,则需要使用系统编程语言来编写它们”,您声称您需要一种系统编程语言来编写Java虚拟机或Python解释器。如果PyPy不满意您,您还可以查找任何用Haskell编写的解释器或编译器。或实际上,只需添加支持您的主张的参考即可。
Vincent Savard

16
即使是乌龟,如果没有用其他语言编写的Python解释器,PyPy之类的东西也将不存在。
Blrfl 2016年

18
@Birfl但这并没有说太多。没有汇编编译器就无法编写C,并且没有实现它的硬件也不能编写汇编。
gardenhead

11
如果PyPy因为某个地方使用了C编译器而不是“系统编程语言”,那么C也不是因为某个地方使用了汇编程序而也不是系统编程语言。实际上,如今将C转换为其他语言是否流行?例如LLVM
排除和冒犯

30

很抱歉添加另一个答案,但是我认为任何现有答案都不能直接解决您的第一句话:

“我正在考虑学习C”

为什么?您是否想做C通常用于当今的事情(例如设备驱动程序,VM,游戏引擎,媒体库,嵌入式系统,OS内核)?

如果是,那么是的,请确保根据您感兴趣的对象学习C或C ++。是否要学习它,以便对高级语言的功能有更深入的了解?

然后,您继续提及安全问题。您不一定需要对安全C 有深入的了解就可以执行安全C,就像使用高级语言的代码示例可能在没有准备好生产的情况下提供要点一样。

编写一些C代码以获得要点。然后放回架子上。除非您想编写生产 C代码,否则不必太担心安全性。


2
回答似乎是真实问题的出色工作!欣赏C / C ++和“更安全”的语言的一种好方法就是尝试编写类似于简单数据库引擎的内容。您会从每种尝试的方法中获得很好的感觉,并了解将其引向何方。您将看到两种方法都感觉很自然,并且会发现自然方法失败的地方(例如,很容易在C语言中“序列化”原始数据-只需写入数据即可;但是结果不可移植,因此它是可移植的)可能用途有限)。了解安全性非常棘手,因为大多数问题可能很难遇到。
六安2013年

1
@Luaan确切地讲,学习如何使用指针来复制字符串将为您提供一个概念,学习如何安全地进行操作是另一个层次,并且取决于一个人的目标,这也许是不必要的。
贾里德·史密斯

2
这不是真正的问题。但这有助于进行讨论。我决定这样做。我愿意了解有关所有基础的内部工作的更多信息。我只喜欢编程。我希望以此方式更好地了解计算机的内部工作原理。这只是为了好玩。
特里斯坦(Tristan)2013年

10
我不同意最后一句话。在养成不良习惯之前,先学习如何做。
glglgl

2
@glglgl IDK,如果我在网络上读取(或编写)一个JavaScript代码段,那么我这样做的原因是它尚不具备生产准备状态:它将没有异常处理,它可能是O(n ^ 2)等。无这对于理解这一点是必要的。所有这些都是生产代码所必需的。为什么有什么不同?我可以为自己的天才编写天真的C语言,同时从智力上理解,如果我想把它发布在那里,我需要做更多的工作。
贾里德·史密斯

14

这是一个巨大的问题,有很多答案,但是简短的版本是每种编程语言都专门针对不同的情况。例如,用于Web的JavaScript,用于低级内容的C,用于Windows的C#等。当您知道编程知识以决定选择哪种编程语言后,它便会帮助您了解要执行的操作。

为了解决您的最后一点,为什么要使用C / C ++而不是Java / Python,它通常会降低速度。我制作游戏,而Java / C#最近才达到足以运行游戏的速度。毕竟,如果您希望游戏以每秒60帧的速度运行,并且希望您的游戏执行很多工作(渲染特别昂贵),那么您需要使代码尽可能快地运行。Python / Java / C#/许多其他程序都在“解释器”上运行,这是一个额外的软件层,可以处理C / C ++所不具备的所有繁琐的工作,例如管理内存和垃圾回收。额外的开销使事情变慢了,所以几乎您看到的每一个大型游戏都是在C或C ++中完成的(无论如何,在过去的十年中)。有例外:Unity游戏引擎使用C#*,而Minecraft使用Java,但这是例外,不是规则。一般来说,

*甚至Unity也不全是C#,其中很大一部分都是C ++,您只需将C#用作游戏代码即可。

编辑 为了回应我发表此文章后出现的一些评论:也许我过于简化了,我只是给出了一般的图片。使用编程,答案绝非易事。有针对C的解释器,Java语言可以在浏览器之外运行,而由于Mono,C#可以在几乎所有程序上运行。不同的编程语言专用于不同的领域,但是某个地方的某些程序员可能想出了如何使任何语言在任何上下文中运行的方法。由于OP似乎不了解太多编程知识(就我而言,这是假设,对不起,如果我错了),所以我试图将答案保持简单。

至于关于C#几乎与C ++一样快的评论,那里的关键词几乎是。当我上大学时,我们参观了许多游戏公司,而我的老师(一直鼓励我们整年从C#转向C ++)询问我们每家公司的程序员为何选择C ++而不是C#。说C#太慢了。通常,它运行速度很快,但是垃圾收集器会损害性能,因为您无法控制它的运行时间,并且如果您希望在建议的时间运行它,则它有权忽略您。如果您需要高性能的东西,那么您就不会想要如此不可预测的东西。

是的,为了回应我的“刚刚达到速度”的评论,是的,C#的速度提高大部分来自更好的硬件,但是随着.NET框架和C#编译器的改进,那里的速度有所提高。

关于“游戏使用与引擎相同的语言编写”的注释,这取决于。有些是,但许多是用多种语言编写的。虚幻引擎可以做UnrealScript和C ++,Unity可以做C#Javascript和Boo,许多其他用C或C ++编写的引擎都使用Python或Lua作为脚本语言。那里没有一个简单的答案。

仅仅因为它让我读到“谁在乎您的游戏以200fps或120fps运行”而烦恼我,如果您的游戏运行速度超过60fps,则可能是在浪费CPU时间,因为普通显示器甚至无法刷新该时间快速。一些高端和较新的设备可以,但是还不是标准的(还...)。

关于“忽略数十年的技术”的言论,我仍处于20年代初期,所以当我往后推算时,我主要是在呼应年长,经验丰富的程序员告诉我的内容。显然,这将在这样的网站上展开,但值得考虑。


4
适用于任何Windows的C# ”-哦,这是一个谬论。您甚至提供了一个示例。统一。AFAIK尚未编写它提供C#API,因为该语言是一种很好的适应性语言。设计得非常好。而且我更喜欢c ++,但是应该在应有的位置给予功劳。也许您将C#与.NET混合了?他们经常一起出去玩。
luk32

2
“即使Unity也不全是C#,其中很大一部分都是C ++”然后呢?Unity中的游戏经常广泛使用C#,并且已经存在了一段时间。暗示C#只是“最近才达到速度”需要更多的背景信息,或者冒着对数十年来的技术视而不见的风险。
NPSF3000 '16

2
几乎每个大型游戏都是使用所用引擎的语言编写的:需要复制的工作量如此之大,以至于没有其他技术方面的考虑值得考虑。渲染确实很昂贵,但是如今,所有渲染都是用着色器编写的,并且逻辑循环的语言已无关紧要。
彼得·泰勒

2
C#一直都是JIT编译的(不同于Java,您的注释是正确的),并且如果您知道自己在做什么,它从一开始就具有与C ++非常相似的执行速度。那是2003年-我最近不会考虑。原始速度并不是游戏的主要问题(尤其是GPU上的可编程着色器),还有其他一些因素使C#等语言有时或多或少地变得流行。两个主要问题是API(主要面向C,并且接口可能很昂贵)和GC(主要用于延迟问题,而不是原始吞吐量)。
a安

1
@gbjbaanb不仅仅是CPU更快-一个很大的问题是C ++和C有数十年的时间来完善其编译器和运行时,而Java基本上是从零开始的(主要是被设计为多平台平台)。随着VM的改进(例如,从解释器到JIT编译器的转换,改进的GC ...),Java应用程序的性能也得到了提高。C / C ++仍然具有许多优势,那就是“希望一切都没有中断”的方法-避免了许多被认为“不必要”的检查。但这仍然是巨大的内存消耗-实际上,CPU使用率的提高通常意味着较差的内存性能:)
Luaan

13

有趣的是,您声称C不安全,因为“它具有指针”。反之亦然:Java和C#实际上只有指针(对于非本机类型)。Java中最常见的错误可能是Null Pointer Exception(参见https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare)。第二个最常见的错误可能是隐藏着对未使用对象的隐藏引用(例如,未丢弃的封闭式对话),因此无法释放这些引用,从而导致长时间运行的程序占用越来越大的内存。

有两种基本机制可以使C#和Java更安全,并且可以通过两种不同的方式更安全:

  • 垃圾回收使程序尝试访问被丢弃对象的可能性降低。这使程序不太可能意外终止。与C相反,Java和C#默认情况下动态分配非本地数据。这实际上使程序逻辑更加复杂,但是内置的垃圾收集(要付出一定的代价)接管了最困难的部分。

最新的C ++智能指针使程序员的工作变得更加轻松。

  • Java和C#编译为中间代码,该代码由精心设计的运行时解释/执行。这增加了安全级别,因为运行时可以检测程序的非法活动。即使程序编码不安全(两种语言都可能),理论上各个运行时仍可防止“突围”进入系统。
    运行时不能防止例如尝试的缓冲区溢出,但是从理论上讲不允许使用此类程序。相比之下,使用C和C ++,程序员必须安全地编写代码,以防止受到攻击。通常这无法立即实现,但需要进行审查和迭代。

值得注意的是,精心设计的运行时间也存在安全风险。在我看来,由于新发现的安全问题,Oracle每两周更新一次JVM。当然,与单个程序相比,验证JVM 要困难得多。

因此,精心设计的运行时的安全性是模棱两可的,并且在一定程度上是骗人的:通过回顾和迭代,您的平均C程序可以变得相当安全。您的普通Java程序仅与JVM安全。也就是说,不是真的。决不。

gets()您链接到的有关该文章的内容反映了历史上的图书馆决策,而今天这些决策将有所不同,而不是核心语言。


3
我认为原始作者的意思是,在C中,您可以对这些指针采取未经检查的动作。在Java中,您会立即得到一个不错的异常-在C中,您可能直到应用程序状态损坏才意识到自己正在读取无效位置。
Sam Dufel

1
另外,stackoverflow.com / questions / 57483 /… -说java具有“实际上仅引用”是更准确的。
Sam Dufel

4
我已经看到了各种尝试避免显式指针的有趣错误。通常,基本问题是复制和引用之间的混淆。在C语言中,如果我向您传递指向某物的指针,则您知道修改它会影响原始内容。避免指针的一些尝试混淆了这种区别,导致深层副本,浅层副本和一般混乱的迷宫。
艾莉·史蒂芬斯

断言甲骨文的JVM很烂(从可利用的安全漏洞大出血的角度来看),因此,托管语言的运行时通常会比使用托管语言避免更多的安全问题,就像说Adobe Flash令人恐惧一样,是不安全和程序的源头,从网站上播放视频和动画是否必须天生就荒谬地不安全。并非所有的Java运行时都几乎像Oracle / Sun 1990年代老式的JVM可憎那样糟糕,并且并非所有的托管语言都是Java。(好吧,很明显。)
大多数是

@halfinformed好;我说的是程序仅与运行时一样安全,并且可以使“您的平均”(读取为:小码)程序比任何大型运行时(如字节码解释器)更安全。那句话似乎不可否认。特定的独立程序或特定的运行时是否比其他程序更安全,取决于它们各自的复杂性以及设计,编码和维护质量。例如,我不会说sendmail比Oracle的Java VM更安全。但是qmail可能是。
彼得·施耐德

10

因为“安全”花费了速度,所以“更安全”的语言执行速度较慢。

您问为什么使用诸如C或C ++之类的“危险”语言,为什么有人用Python或Java等为您编写视频驱动程序等,并了解您对“安全性”的看法:)

但是,严重的是,您必须尽可能接近机器的核心内存,才能处理像素,寄存器等。Java或Python不能以任何类型的性能值得的速度做到这一点……C和C ++都允许您通过指针等执行此操作...


20
总的来说,安全成本的提高并不是真的。最安全的可用语言会在编译时执行大多数检查。在平均运行速度方面,O'Caml,Ada,Haskell,Rust都不落后于C。他们,通常需要在程序大小,存储效率,延迟显著的开销,很明显编译时间。而且,是的,他们在处理接近金属的物品时遇到困难。但这并不是速度问题。

6
同样,C并没有按照您的想法做。C 是抽象机器。它不能让您直接访问任何内容-很好。您甚至无法查看现代程序集来了解隐藏了多少C语言-在开发C语言时,现代程序集(例如TASM)将被视为高级语言。如果有人用“安全”语言编写驱动程序,我将非常高兴,谢谢-这将有助于避免大量的BSOD和死机,更不用说安全漏洞了:)最重要的是,有些系统语言更加安全比C.
卢安

3
@Wintermute您真的想在对安全功能如何必然导致速度提高发表评论之前先查找Rust。地狱,C的低级类型系统实际上抑制了编译器本可以进行的许多非常有用的优化(尤其是当考虑到几乎没有大型c项目设法避免不违反某处的严格别名时)。
Voo

7
@Wintermute是的,关于不引入性能开销就无法使C / C ++变得更加安全的说法非常持久,这就是为什么我将其视为非常认真的原因(在某些领域,这确实是正确的[边界检查])。现在,为什么Rust不再普及?历史和复杂性。Rust仍然相对较新,并且许多用C编写的最大系统都在Rust发明之前就已经存在-即使安全得多,也不会用新语言重写一百万个LOC。而且每个程序员和他们的狗都知道C,Rust?祝您找到足够的人。
Voo

3
@Voo Dogs在做C吗?...难怪我在那里看到这么多坏代码... j / k关于Rust的要点(我刚刚下载并安装了它,所以您可能要再添加一个转换)BTW到Rust正确地做...我可以为“ D”做同样的扩充:)
Wintermut3,2013年

9

除了上述所有内容之外,还有一个非常常见的用例,它使用C作为其他语言的通用库。

基本上,几乎所有语言都具有C语言的API接口。

简单的例子,尝试为Linux / IOS / Android / Windows创建一个通用应用程序。除了现有的所有工具之外,我们最终要做的是用C语言编写一个核心库,然后为每种环境更改GUI,即:

  • iOS:ObjectiveC可以原生使用C库
  • Android:Java + JNI
  • Linux / Windows / MacOS:通过GTK / .Net,您可以使用本机库。如果使用Python,Perl,Ruby,它们每个都有本机API接口。(再次使用JNI的Java)。

我的两分钱


我喜欢使用PHP的原因之一是因为它的几乎所有库的确都用C编写的-幸运的是,否则PHP的运行速度会令人难以忍受:) PHP非常适合编写草率的代码而无需担心做任何“危险的”事情(这就是为什么我倾向于编写比其他任何事情都要多的PHP代码的原因-我喜欢草率的代码!:D),但是很高兴知道在这些函数调用的下面有很多ol'trusty C库可以提高性能;-)相比之下,用C语言编写草率的代码是一件大事……
Gwyneth Llewelyn

7

C语言的一个基本困难是,该名称用于描述语法相同但语义完全不同的多种方言。一些方言比其他方言安全得多。

在Dennis Ritchie最初设计的C语言中,C语句通常以可预测的方式映射到机器指令。因为C可以在发生有符号算术溢出之类的处理器时以不同的方式运行,所以不知道机器在发生算术溢出时的行为的程序员也不会知道在该机器上运行的C代码的行为,但是如果已知一台机器以某种方式工作(例如,无声的二进制补码环绕),则该机器上的实现通常也会这样做。C之所以以快速而闻名的原因之一是,在程序员知道平台在极端情况下的自然行为能够满足他们的需求的情况下,程序员或编译器无需编写代码来生成此类情况。 。

不幸的是,编译器作者认为,由于该标准对在这种情况下必须执行的操作没有任何要求(放宽是为了允许可能无法预期的硬件实现),因此编译器应该随意生成会否定律的代码时间和因果关系。

考虑类似:

int hey(int x)
{
   printf("%d", x);
   return x*10000;
}
void wow(int x)
{
  if (x < 1000000)
    printf("QUACK!");
  hey(x);    
}

超现代(但很时髦)的编译器理论建议编译器应输出“ QUACK!”。无条件地,因为在任何情况下条件为假,程序最终都将调用未定义的行为执行乘法,其结果将被忽略。由于标准在这种情况下允许编译器执行其喜欢的任何事情,因此它允许编译器输出“ QUACK!”。

虽然C过去比汇编语言更安全,但是当使用超现代编译器时,情况恰恰相反。在汇编语言中,整数溢出可能导致计算产生无意义的结果,但是在大多数平台上,这将是其影响的程度。如果结果最终还是被忽略了,那么溢出将无关紧要。但是,在超现代C语言中,即使通常是未定义行为的“良性”形式(例如计算中的整数溢出最终被忽略)也可能导致任意程序执行。


1
即使在超现代的编译器中,C也不对数组进行边界检查。如果这样做,将与语言的定义不兼容。我有时会用这个事实来制作一个带有指向数组中间的附加指针的数组,以使其具有负索引。
罗伯特·布里斯托

1
我希望看到您的示例产生“ QUACK!”的证据。无条件的。在比较时,x肯定可以大于1000000,以后进行评估会导致溢出不会阻止该情况。更重要的是,如果启用了内联,从而可以消除溢出的乘法,则关于隐式范围限制的论点将不成立。
格雷厄姆

2
@ robertbristow-johnson:实际上,该标准非常明确地指出,例如int arr[5][[5],给定访问尝试arr[0][5]将产生未定义行为。这样的规则使编译器arr[1][0]=3; arr[0][i]=6; arr[1][0]++;可以推断出arr[1][0]等于4的值,而无需考虑的值i
超级猫

2
@ robertbristow-johnson:即使编译器在结构中顺序分配数组而没有间隙,也不能保证索引其中一个数组会影响另一个数组。有关gcc如何处理此类代码的示例,请参见godbolt.org/g/Avt3KW
超级猫

1
@ robertbristow-johnson:我对程序集发表了评论,以解释其功能。编译器发现代码将1存储到s-> arr2 [0]中,然后递增s-> arr2 [0],因此gcc通过使代码简单地存储值2来组合这两个操作,而没有考虑中间写入的可能性到s-> arr1 [i]的值可能会影响s-> arr1 [0]的值(因为根据标准,所以不会)。
超级猫

5

历史原因。我通常不会写全新的代码,主要是我要维护和扩展已经运行了数十年的旧代码。我很高兴它是C而不是Fortran。

当有些学生说“我为什么会在做Y的时候却把这个糟糕的X变成可怕的?”,我会很生气。好吧,X是我的工作,它可以很好地支付账单。我有时候做过Y,这很有趣,但是X是我们大多数人所做的。


5

什么是“危险”?

在语言火焰大战中(经常是与Java相比),C是“危险”的说法是经常谈论的话题。但是,这一说法的证据尚不清楚。

C是一种具有特定功能的语言。其中某些功能可能会允许某些其他类型的语言所不允许的某些类型的错误(通常会突出显示C的内存管理风险)。但是,这与C 总体上比其他语言更危险的说法不同。我不知道有人在这一点上提供令人信服的证据。

同样,“危险”取决于上下文:您要做什么,以及您担心哪种风险?

在许多情况下,我认为C比高级语言更“危险”,因为C需要您对基本功能进行更多的手动实现,从而增加了发生错误的风险。例如,使用C语言进行一些基本的文本处理或开发网站通常很愚蠢,因为其他语言的功能使其变得更加容易。

但是,C和C ++被广泛用于关键任务系统,因为在这种情况下,具有更直接控制硬汉的较小语言被认为“更安全”。从一个很好的堆栈溢出答案

尽管C和C ++并非专门针对此类应用程序而设计,但出于多种原因,它们被广泛用于嵌入式和安全性至关重要的软件。注释的主要属性是对内存管理的控制(例如,它可以避免垃圾回收),简单,调试良好的核心运行时库以及成熟的工具支持。今天使用的许多嵌入式开发工具链最早是在1980年代和1990年代开发的,当时这是一种最新技术,并且来自当时流行的Unix文化,因此这些工具在此类工作中仍然很流行。

尽管必须仔细检查手动内存管理代码以避免错误,但是它可以一定程度地控制应用程序响应时间,而依赖于垃圾回收的语言则无法提供这种控制。C和C ++语言的核心运行时库相对简单,成熟并且易于理解,因此它们是可用的最稳定的平台之一。


2
我想说,超现代的C比汇编语言或C的真正低级方言还要危险,后者始终如一地将C操作转换为机器代码操作,而无需考虑自然机器代码操作会发生的极端情况。已经定义了行为,但是C标准将不施加任何要求。整数溢出会否定时间和因果关系的超现代方法似乎不适合生成安全代码。
超级猫

5

为了增加现有的答案,这是很好的说法,因为它们相对安全,因此您将为项目选择Python或PHP。但是有人必须实现这些语言,而当它们实现时,他们很可能会用C语言来实现(或者,类似的东西。)

这就是为什么人们使用C 来创建要使用的危险程度较小的工具的原因。


2

请允许我重新表述您的问题:

我正在考虑学习[工具]。

但是,如果[危险]可以使用[工具](或[相关工具]),人们为什么要使用它们?

任何有趣的工具都可能会危险地使用,包括编程语言。您可以学到更多,以便更多的事情(使用该工具可以减少危险)。尤其是,您将学习该工具,以便您可以做该工具所擅长的事情(并可能会认识到该工具何时是您所知道的工具中最好的工具)。

例如,如果您需要在一块木头上放置一个直径6毫米,深5厘米的圆柱孔,则钻头比LALR解析器好得多。如果您知道这两个工具是什么,就知道哪个是正确的工具。如果您已经知道如何使用钻头,瞧!

C只是另一个工具。对于某些任务,它比对其他任务要好。这里的其他答案解决了这个问题。如果您学习了一些C语言,您将认识到何时是正确的工具,何时才是正确的工具。


答案之所以如此,是因为为什么问题会以“主要基于意见”的方式抛出。不要说C有它的优势,要说它们是什么!
reinierpost

1

我正在考虑学习C

没有特定的理由不学习C语言,但我建议使用C ++语言。它提供了C所做的很多工作(因为C ++是C的超集),带有大量的“附加”。在C ++之前学习C是不必要的-它们实际上是独立的语言。

换句话说,如果C是一组木工工具,则可能是:

  • 锤子
  • 指甲
  • 手锯
  • 手钻
  • 砂光机
  • 凿子(也许)

您可以使用这些工具来构建任何东西,但是任何好的东西都可能需要大量的时间和技巧。

C ++是您本地硬件商店中的电动工具的集合。

如果您坚持使用基本的语言功能,那么C ++的额外学习曲线就很少。

但是,如果人们可以“危险地”使用C(或C ++),为什么还要使用它呢?

因为有些人不想要宜家的家具。=)

严重的是,尽管许多比C或C ++“更高”的语言可能具有使它们(可能)“更容易”在某些方面使用的功能,但这并不总是一件好事。如果您不喜欢某项工作的完成方式或不提供功能,那么您可能无能为力。另一方面,C和C ++提供了足够的“低级”语言功能(包括指针),您可以直接直接访问许多内容(尤其是硬件或操作系统方面的内容),也可以自己构建它,而这在其他情况下是不可能的。语言已实施。

更具体地说,C具有以下一些功能,这些功能对于许多程序员来说都是理想的:

  • 速度 -由于多年来相对简单和编译器优化,它本来就非常快。而且,许多人在使用该语言时已经找到了许多指向特定目标的捷径,这可能使它甚至更快。
  • 尺寸 -出于同样的原因,作为速度列出的那些,C程序可以做得非常小(在可执行文件的大小和存储器使用方面两者),这是期望对于具有有限存储器(即嵌入或移动的)的环境中。
  • 兼容性 -C已经存在很长时间了,每个人都有相应的工具和库。语言本身也不是挑剔的-它期望处理器执行指令和内存来保存东西,仅此而已。

    此外,还有一种称为应用程序二进制接口(ABI)的东西。简而言之,这是程序在机器代码级别进行通信的一种方法,与应用程序编程接口(API)相比,它可以具有很多优势。尽管其他语言(例如C ++)可以具有ABI,但是通常它们与C的统一性(商定的)不那么统一,因此当您出于某种原因要使用ABI与另一个程序进行通信时,C是一种很好的基础语言。

为什么程序员不仅仅使用Java或Python或其他编译语言(如Visual Basic)?

效率(有时需要在没有相对直接访问内存的情况下才能实现的内存管理方案)。

当您可以将肮脏的爪子直接放在内存小孔中的零零零碎的地方,而不必等待那意味着老师直接分发玩具时,用指针直接访问内存会引入很多巧妙的技巧(通常是快速的)在游戏时,再将它们them起。

简而言之,添加内容可能会导致滞后或以其他方式引入不必要的复杂性。

关于脚本语言和类似的语言,您必须努力工作,以使要求辅助程序运行的语言能够像C(或任何编译语言)本机一样高效地运行。由于要在混合中添加另一个程序,因此添加动态解释器会固有地降低执行速度并增加内存使用率。您的程序效率很大程度上取决于此辅助程序的效率,以及编写原始程序代码的性能(差)。更不用说您的程序通常完全依赖于第二个程序才能执行。由于特定原因,第二个程序在特定系统上不存在?代码不行。

实际上,引入任何 “额外”的内容都可能减慢或使您的代码复杂化。在“没有可怕的指针”的语言中,您总是在等待其他代码清除在您身后,或者以其他方式找出“安全”的处理方式-因为您的程序仍在执行与可能执行的相同的内存访问操作指针。您不是处理它的那个人(所以您不能搞定它,genius = P)。

危险是指指针和其他类似的东西。[...]像堆栈溢出问题一样,为什么gets函数如此危险以至不应该使用它?

根据公认的答案:

“直到1999 ISO C标准,它仍然是该语言的正式组成部分,但在2011年标准中正式将其删除。大多数C实现仍支持该语言,但至少gcc对使用它的任何代码都发出警告。”

因为某些事情可以用某种语言完成,所以必须做到这一点是愚蠢的。语言具有修复的缺陷。出于与旧代码兼容的原因,仍可以使用此构造。但是没有任何(可能)强迫程序员使用gets()的方法,实际上,实际上该命令已被更安全的替代方法替代。

更重要的是,gets()的问题本身并不是指针问题。这是一个命令的问题,它不一定知道如何安全地使用内存。从抽象的意义上讲,这就是所有指针问题-读和写你不希望的东西。指针不是问题。这是指针实现的问题。

需要澄清的是,在您意外访问了您不打算访问的存储位置之前,指针并不危险。即使如此,也不能保证您的计算机会融化或爆炸。在大多数情况下,您的程序将停止运行(正确)。

就是说,由于指针提供对内存位置的访问,并且因为数据和可执行代码一起存在于内存中,所以要正确管理内存确实存在意外损坏的真正危险。

到那时,由于真正的直接内存访问操作通常所提供的利益通常比几年前少,因此即使是非垃圾收集的语言(如C ++)也引入了诸如智能指针之类的东西,以帮助弥合内存效率和安全性之间的差距。

总之,只要安全使用指针,几乎没有理由担心指针。只是从南方公园的史蒂夫“鳄鱼猎人”欧文(Steve Steve )中获得一些启示 - 不要将拇指伸入鳄鱼的洞中


2
我不同意用C ++而不是C的建议。写好C ++比写好C难,读C ++比读C难。因此C ++的学习曲线要​​陡峭得多。“ C ++是C的超集”这或多或少像是说靴子是拖鞋的超集。它们具有不同的优势和用途,并且每个都有其他没有的功能。
martinkunev

“写好的C ++比写好的C难”-绝对。=)“ [R]依赖C ++比阅读C难得多”-任何高级编程都可能与魔术没有区别;-)我的两分钱是,这比程序员依赖于语言要好得多,尽管C ++并不能帮助自己在这个类别中。“因此C ++的学习曲线要​​陡峭得多。” -从长远来看,是的。从短期来看,情况不那么乐观(我认为)。有趣的是,大多数C和C ++基本语言课程可能涵盖大致相同的通用材料类型,但C ++类除外。
Anaksunaman

2
“它们具有不同的优势和用途,并且每个都有其他却没有的功能。” -如前所述,“没有特定的理由不学习C [。]” C是一种很好的语言,我坚持这样做。如果它适合OP或其他任何人,我完全支持对其进行学习。=)
Anaksunaman

学习C可以告诉您机器的工作方式(不是一直向下,而是更接近金属)。这是学习它的很好理由。
Agent_L

1

与往常一样,编程语言只是解决问题的结果。实际上,您不仅应该学习C语言,还应该学习许多不同的语言(以及其他编程计算机的方法,包括GUI工具或命令解释器),以便在解决问题时使用一个不错的工具箱。

有时,您会发现问题很容易导致Java默认库中包含的某些问题,在这种情况下,您可以选择Java以加以利用。在其他情况下,可能需要在Windows上执行.NET运行时中要简单得多的操作,因此您可以使用C#或VB。可能有一个图形工具或命令脚本可以解决您的问题,然后可以使用它们。也许您需要在多个平台上编写GUI应用程序,考虑到JDK中包含的库,可以选择Java,但是,又一次,一个目标平台可能缺少JRE,因此您可能选择了C和SDL(或类似的名称)。

C在此工具集中具有重要的地位,因为它通用,小巧,快速并且可以编译为机器代码。在阳光下的每个平台上也都支持它(但是不是重新编译)。

最重要的是,您应该学习尽可能多的工具,语言和范例。

请远离思维定式:“我是X程序员”(X = C,C ++,Java等)

只需使用“我是程序员”即可。

程序员通过指示机器执行工作负载来解决问题并设计算法。故事结局。这与语言无关。您最重要的技能是解决问题和对结构化问题进行逻辑分解,语言技能/选择始终是次要的和/或问题性质的结果。

如果您对C感兴趣,一条有趣的方法是使用Go扩展技能。Go确实是经过改进的C,具有垃圾回收和接口,以及不错的内置线程模型/通道,它还带来了C的许多优点(例如指针算术和编译为机器代码)。


0

这取决于您打算如何处理。C被设计为汇编语言的替代,并且是最接近于机器语言的高级语言。因此,它在大小和性能上的开销很低,并且适合于系统编程和其他需要占用空间小且与底层硬件接近的任务。


0

当您在位和字节级别上工作时,内存是原始的同类数据集合,通常需要有效地实现最高效的分配器和数据结构,因此没有安全性。安全性主要是与数据类型相关的概念,并且内存分配器不适用于数据类型。它与位和字节一起使用,以汇集那些相同的位和字节,这些位和字节可能在某一时刻代表一种数据类型,而在稍后又代表另一种数据类型。

在这种情况下是否使用C ++都没有关系。你还是会洒static_casts在整个代码从投void*三分球,并仍与比特和字节的工作,只是处理有关在这方面比C具有更简单的类型系统,您可以自由尊重型系统更多的麻烦到memcpy比特和字节左右,而不必担心在推土类型系统。

实际上,在这样的低级位和字节上下文中,使用整体安全的语言C ++编写代码比编写C语言中的代码更危险,通常会更困难,因为您可能会推翻C ++的类型系统并执行类似的操作覆盖vptrs,并且无法在适当的时间调用复制构造函数和析构函数。如果您花适当的时间尊重这些类型,并使用新的Placement并手动调用dtor等,那么您会在RAII不太实用的低级环境中接触到异常处理的世界,并实现异常-在如此低级的环境中,安全性是非常困难的(您必须假装几乎任何函数都可以抛出并捕获所有可能性,并将任何副作用作为不可分割的事务回滚,就像什么也没有发生一样)。C代码通常可以“

而且不可能用不允许您在此处“危险”的语言来实现这样的分配器。您将不得不依靠他们提供的任何分配器(最有可能在C或C ++中实现),并希望它足以满足您的目的。而且,几乎总是有效率更高,但通用性较低的分配器和数据结构适合您的特定用途,但适用性却要窄得多,因为它们是专门为您的目的量身定制的。

大多数人不需要C或C ++之类的东西,因为他们可以调用最初用C或C ++实现的代码,甚至可能已经为它们实现的汇编。许多人可能会从高水平的创新中受益,例如将仅使用已在C中实现的现有图像处理功能库的图像程序串起来,而在通过单个像素循环的最低水平上他们并没有进行太多创新,但是提供了前所未有的非常友好的用户界面和工作流程。在那种情况下,如果软件的目的只是要对低级库进行高层调用(“为我处理整个图像,而不是为每个像素做某事”),那么它可能是过早的优化甚至试图开始用C编写这样的应用程序。

但是,如果您在低级别上做一些新的事情,它可以帮助以低级别的方式访问数据,例如以前从未见过的全新图像过滤器,那么它的速度足以实时处理高清视频,那么您通常必须获得有点危险。

这东西理所当然是容易的。我记得有人在Facebook上发帖,指出有人用Python创建3D视频游戏是可行的,这暗示着低级语言已经过时了,这肯定是一款外观不错的游戏。但是Python正在对用C实现的库进行高层调用,以完成所有繁重的工作。您不能仅通过对现有库进行高层调用来制作虚幻引擎4。虚幻引擎4 图书馆。它完成了其他所有库和引擎中从未存在过的工作,从照明到其节点蓝图系统,以及它如何实时编译和运行代码。如果您想在低引擎/核心/内核级别上进行创新,那么您必须获得低级别的支持。如果所有游戏开发人员都切换到高级安全语言,则不会有虚幻引擎5、6或7。很可能是人们在四十年后仍在使用虚幻引擎,因为您无法在即将到来的水平上进行创新只需对旧版本进行高级调用即可使用下一代引擎。

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.