我正在考虑学习C。

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

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

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

评论

如果厨师可以“危险地”使用刀具,为什么还要使用刀具?

强大的力量伴随着巨大的责任。

乔·布鲁(Joe Blow),有多崇高?

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

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

#1 楼


C早于您想到的许多其他语言。我们现在知道的许多有关如何使编程“更安全”的知识来自对C之类的语言的了解。

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


C是一种更容易实现的语言,因为它相对较小,并且更多。可能甚至在最弱的环境下也能充分发挥性能,因此许多需要开发自己的编译器和其他工具的嵌入式系统更有可能为C提供功能性的编译器。
因为C是如此之小且如此简单,其他编程语言倾向于使用类似C的API进行相互通信。即使我们大多数人仅通过包装程序与C进行交互,这也可能是C不会真正死亡的主要原因。


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

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

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


您永远不会像混淆的C比赛那样使混淆的Java比赛有趣。

评论


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

– underscore_d
16 Jun 9'在11:57



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

– Matthieu M.
16年6月9日12:00

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

– reinierpost
16年6月9日在15:44

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

–罗安
16年6月10日在13:13

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

– Matthieu M.
16年6月10日在13:28

#2 楼

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

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

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


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

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

因此,总结一下:Java和Python不适合系统编程,不是因为它们的主要实现是被解释的,也不是因为本机编译的实现不是自托管的,但是因为它们没有为使用基础平台提供必要的支持。

评论


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

– Vincent Savard
16年6月7日在19:03

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

– Vincent Savard
16年7月7日在19:18

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

– Blfl
16年6月7日在20:04

@Birfl但这并没有说太多。没有汇编编译器就无法编写C语言,而没有实现它的硬件也无法编写汇编语言。

– Gardenhead
16年6月8日,0:15

如果PyPy因为某个地方使用了C编译器而不是“系统编程语言”,那么C也不是因为某个地方使用了汇编程序而也不是系统编程语言。实际上,如今将C语言翻译成其他语言是否流行?例如虚拟机

–user122173
16 Jun 8'在6:30



#3 楼

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


'我正在考虑学习C'


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

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

然后您继续提及安全问题。您不一定需要对安全C有深入的了解即可完成安全C,就像使用高级语言的代码示例可能在没有准备好投入生产时就掌握了要旨一样。

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

评论


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

–罗安
16年6月8日在12:44

确切地说,@ Luaan,学习如何使用指针来复制字符串,这为您提供了一个概念,学习如何安全地进行复制是另一个层次,并且取决于一个人的目标,这也许是不必要的。

–杰瑞德·史密斯(Jared Smith)
16年6月8日在13:09

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

– Tristan
16年6月8日在18:57

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

– glglgl
16年6月9日在7:20

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

–杰瑞德·史密斯(Jared Smith)
16年6月9日14:15



#4 楼

这是一个巨大的问题,有很多答案,但是简短的版本是每种编程语言都专门针对不同的情况。例如,用于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年代初期,所以当我向后推算,我主要是在回响年龄较大,经验丰富的程序员告诉我的内容。显然,它将在像这样的网站上竞争,但值得考虑。

评论


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

–luk32
16年6月8日,0:10

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

–NPSF3000
16年6月8日,下午3:12

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

– Peter Taylor
16年6月8日在11:44

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

–罗安
16年6月8日在12:22

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

–罗安
16年6月8日在12:28

#5 楼

有趣的是,您声称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()的文章反映了历史图书馆的决定,而今天这些决定将有所不同,而不是核心语言。

评论


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

– Sam Dufel
16年6月8日在15:28



另外,请访问stackoverflow.com/questions/57483/…-说Java实际上只有“引用”会更准确。

– Sam Dufel
16年6月8日在15:31

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

– Arlie Stephens
16年6月8日在21:28

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

–大多数情况下
16年6月10日在5:50

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

–彼得-恢复莫妮卡
16-6-10上午8:50



#6 楼

由于“安全”需要速度,因此“安全”语言的运行速度会较慢。

您问为什么使用“危险”语言(例如C或C ++),有人让您编写视频驱动程序等?使用Python或Java等工具,并了解“安全性”的感觉:)

严重的是,您必须尽可能靠近机器的核心内存才能操纵像素,寄存器等等... Java或Python不能以任何类型的性能值得的速度执行此操作... C和C ++都允许您通过指针等来执行此操作...

评论


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

–leftaround关于
16 Jun 8'在8:34



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

–罗安
16年6月8日在12:35

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

– Voo
16年6月8日在16:25

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

– Voo
16年6月8日在16:52



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

– Wintermut3
16年6月8日在17:21

#7 楼

除了上述所有内容外,还有一个非常普通的用例,它使用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
16年6月12日在22:04



#8 楼

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!”无条件的,因为在任何情况下,条件是
false程序最终将调用未定义的行为执行
乘法,无论如何其结果都将被忽略。由于
Standard允许编译器在这种情况下做任何喜欢的事情,因此
它允许编译器输出“ QUACK!”。

C以前更安全与汇编语言相比,当使用超现代
编译器时,情况恰恰相反。在汇编语言中,整数溢出
可能会使计算产生无意义的结果,但是在大多数平台上,
这将是其影响的程度。如果结果最终仍然被忽略,则溢出将无关紧要。但是,在超现代C语言中,
通常甚至是不确定行为的“良性”形式(例如,在计算中出现整数溢出,最终被忽略)。程序执行。

评论


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

–罗伯特·布里斯托-约翰逊
16年6月9日在3:07

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

–格雷厄姆
16年6月9日在11:35

@ 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的值。

–超级猫
16年6月9日在17:12

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

–超级猫
16年6月9日在22:06

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

–超级猫
16年10月10日在5:15

#9 楼

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

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

#10 楼

什么是“危险的”?

在语言火焰大战中(最经常与Java相比),人们经常说C是“危险的”。但是,有关此主张的证据尚不清楚。

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

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

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

然而,C和C ++被广泛用于任务-关键系统,因为在这种情况下,较小的语言可以更直接地控制硬汉。来自一个非常好的Stack Overflow答案:


尽管C和C ++不是专门为这种类型的
应用程序设计的,但它们已广泛用于嵌入式和对安全性要求严格的
/>软件有几个原因。注释的主要属性是对内存管理的控制(例如,可以避免不必要地
收集),简单,调试良好的核心运行时库
和成熟的工具支持。今天使用的许多嵌入式开发工具链
最初是在1980年代和1990年代开发的,当时这是当前技术,并且来自盛行的Unix文化
那时,这些工具在这类工作中仍然很流行。

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


评论


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

–超级猫
16年6月10日在18:09

#11 楼

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

所以这就是人们使用C进行创建的原因您想要使用的危险性较小的工具。

#12 楼

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


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

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


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

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

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

评论


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

– reinierpost
16年6月9日在15:50

#13 楼


我正在考虑学习C

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

hammer
钉子
手锯
手钻
砂光机
凿子(也许)

您可以使用这些工具来制作任何东西-但任何精美的东西都可能需要很多时间和技能。
C ++是本地硬件商店中的电动工具集合。
如果您坚持使用基本的语言功能,C ++的学习曲线就相对较少。

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

因为有些人不想要宜家的家具。 =)
虽然,严重的是,尽管许多比C或C ++“更高”的语言在某些方面可能使它们(可能)“更容易”使用,但这并不总是一件好事。如果您不喜欢完成某项工作的方式或不提供功能,那么您可能无能为力。另一方面,C和C ++提供了足够的“低级”语言功能(包括指针),您可以直接直接访问许多内容(尤其是硬件或OS),也可以自己构建,而这在其他情况下是不可能的。
更具体地讲,C具有以下一组使许多程序员都希望使用的功能:


速度-由于它相对简单,并且在编译器上进行了优化。年,它本来就非常快。另外,很多人在使用该语言时已经找到了许多指向特定目标的捷径,这可能会使它甚至更快。


大小-出于与列出的速度类似的原因,C程序可以做得非常小(在可执行文件大小和内存使用方面),这对于内存有限的环境(即嵌入式或移动)是理想的。


兼容性-C已经存在很长时间了,每个人都有相应的工具和库。该语言本身也不挑剔-它期望处理器执行指令和内存来保存内容。
此外,还有一种称为应用程序二进制接口(ABI)的东西。简而言之,这是程序在机器代码级别进行通信的一种方法,与应用程序编程接口(API)相比,它可以具有很多优势。虽然其他语言(例如C ++)可以具有ABI,但通常它们之间的一致性(商定)不如C,因此当您出于某种原因要使用ABI与另一个程序进行通信时,C是一种很好的基础语言。



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

效率(有时是没有相对的内存管理方案就无法实现)直接访问内存)。
通过指针直接访问内存会引入很多巧妙(通常是快速的)技巧,您可以将肮脏的爪子直接放在内存小孔中的零和零上,而不必等待意思是说,老师要在玩耍时分发玩具,然后再将它们铲起。
简而言之,添加东西可能会导致滞后或导致不必要的复杂性。
对于脚本语言和类似的语言,您必须努力工作,以使要求辅助程序运行的语言能够像C(或任何编译语言)本机一样高效地运行。由于要在混合中添加另一个程序,因此添加动态解释器会固有地降低执行速度并增加内存使用率。您的程序效率很大程度上取决于此辅助程序的效率以及编写原始程序代码的性能(差)。更不用说您的程序通常完全依赖于第二个程序才能执行。由于特定原因,第二个程序在特定系统上不存在?代码行不通。
实际上,引入任何“额外”内容都可能会使您的代码变慢或使其复杂化。在“没有可怕的指针”的语言中,您总是在等待其他代码清除在您身后,或者以其他方式找出“安全”的处理方式-因为您的程序仍在执行与可能执行的相同的内存访问操作指针。您只是不是一个正在处理它的人(因此您无法对其进行修正,天才= P)。

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

根据公认的答案:

”直到1999 ISO C标准,它仍然是该语言的正式组成部分,但在2011年标准中正式删除了该语言。大多数C实现仍支持该语言,但至少gcc会为使用该代码的任何代码发出警告。 />
因为可以用某种语言完成某件事,所以必须做到这一点很愚蠢。语言具有修复的缺陷。出于与旧代码兼容的原因,仍可以使用此构造。但是没有(可能)强迫程序员使用gets(),实际上,该命令实际上已被更安全的替代方法替代。
更重要的是,gets()的问题本身并不是指针问题。这是一个命令的问题,它不一定知道如何安全地使用内存。从抽象的意义上讲,这就是所有指针问题-读和写您不希望的内容。指针不是问题。
要澄清一下,在您不小心访问了您不打算访问的内存位置之前,指针并不危险。即使如此,也不能保证您的计算机会融化或爆炸。在大多数情况下,您的程序将停止(正确)运行。
这就是说,由于指针提供对内存位置的访问,并且由于数据和可执行代码一起存在于内存中,因此存在真正的意外损坏危险想要正确管理内存。
到那时,由于真正的直接内存访问操作通常所提供的好处通常比几年前少,因此即使是非垃圾收集的语言(如C ++)也引入了诸如智能指针之类的东西。有助于缩小内存效率和安全性之间的差距。
总而言之,只要安全使用指针,几乎没有理由担心指针。只是从南方公园的史蒂夫“鳄鱼猎人”欧文(Steve Irwin)那里得到一点启示-不要将拇指伸入鳄鱼的洞中。

评论


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

–martinkunev
16年6月10日在17:50

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

–Anaksunaman
16年6月11日在14:33

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

–Anaksunaman
16年6月11日在14:39

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

–Agent_L
16年6月13日在9:01

#14 楼

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

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

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

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

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

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

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

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

#15 楼

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

#16 楼

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

在这种情况下使用C ++并不重要。您仍然会在整个代码中撒满static_casts,以从void*指针进行转换,并且仍在处理位和字节,并且在这种情况下,与C相比,涉及到比尊重类型系统更多的麻烦,而C的类型系统要简单得多。重新释放memcpy的位和字节,而不必担心类型系统上的推土作业。

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

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

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

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

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