知道C让你成为更好的高级程序员的具体例子是什么?

时间:2010-01-16 15:47:33

标签: c low-level high-level

我知道this onethis one等问题的存在。让我解释一下。

在阅读Joel的文章Back to Basics并看到许多关于SO的类似问题之后,我开始想知道什么是具体的例子,知道像C这样的东西可以让你成为更好的高级程序员。

我想知道的是,有很多例子。很多时候,这个问题的答案就像“了解C让您更好地了解幕后发生的事情”或“您需要为您的计划奠定坚实的基础 “,这些答案没有多大意义。我想了解您从知道低级概念中获益的不同具体方式,

Joel给出了几个例子:二进制数据库与XML和字符串。但是有两个例子并不能证明学习C和/或汇编是正确的。所以我的问题是:知道C让你成为更好的高级程序员的具体例子是什么?

10 个答案:

答案 0 :(得分:15)

我教学生和与只学习高级语言的人一起工作的经验是,他们倾向于在一定程度的抽象层次上思考,他们认为“一切都是免费的”。他们可以成为非常称职的程序员,但最终他们必须处理一些存在性能问题的代码,然后就会咬它们。

当你使用C工作很多时,你会考虑内存分配。您经常考虑内存布局(以及缓存局部性,如果这是一个问题)。您了解某些图形操作的成本和原因。某些套接字行为的效率或效率如何。缓冲区是如何工作的等等我觉得当你知道如何在封面下实现它时,使用更高级语言的抽象有时会在考虑性能时给你“额外的秘密”。

例如,Java有一个垃圾收集器,你不能直接将内容直接分配给内存。然而,您可以做出某些影响性能的设计选择(例如,使用自定义数据结构),原因与C中的问题相同。

另外,更一般地说,我认为对于一个权力程序员而言,不仅要知道大O符号(大多数学校教授),而且在现实生活中应用这个常数也很重要(哪些学校尝试过)无视)。我的轶事经验是,具有两种语言水平技能的人倾向于更好地理解常数,可能是因为我上面所描述的。

此外,我见过的许多更高级别的系统与较低级别的库和基础架构相互作用。例如,一些通信,数据库或图形库。某些设备的某些驱动程序等。如果您是一名强力程序员,您甚至可能不得不冒险去那里,至少可以了解发生了什么。

答案 1 :(得分:9)

了解低级别的东西可以提供很多帮助。

要成为赛车手,您必须学习并了解轮胎如何抓住道路的基本物理原理。任何人都可以学会驾驶相当快,但你需要很好地理解“低水平”的东西(力和摩擦力,赛车线,精细油门和刹车控制等),以获得最后几个百分点的性能,这将使你能够赢得比赛。

例如,如果您了解CPU架构在计算机中的工作方式,则可以编写更好的代码(例如,如果您知道每个CPU缓存行中都有一定的CPU缓存大小或一定数量的字节) ,您可以安排数据结构和访问它们的方式,以充分利用缓存 - 例如,按顺序处理数组中的许多元素通常比处理随机元素更快,因为CPU缓存)。如果你有一台多核计算机,那么了解线程工作等低级技术可以带来巨大的好处(就像不了解低级别可能会导致线程中的灾难一样)。

如果您了解磁盘I / O和缓存的工作原理,您可以修改文件操作以使其正常工作(例如,如果您从一个文件读取并写入另一个文件,则在RAM中处理大批量数据可以帮助减少我/ O代码的读写阶段之间的争用,并大大提高吞吐量)

如果了解虚函数的工作原理,可以设计使用虚拟函数的高级代码。如果使用不当,可能会严重影响性能。

如果您了解如何处理绘图,则可以使用巧妙的技巧来提高绘图速度。例如您可以通过交替绘制64个白色和黑色方块来绘制棋盘。但是绘制32个白色平方和32个黑色通常会更快(因为你只需要改变绘图颜色两次而不是64次)。但实际上你可以把整个黑板画成黑色,然后在整个黑板上画出XOR 4条纹,在白板上画下4条条纹,这样可以更加快速(2种颜色变化,只有9种矩形可以画出而不是64种)。这个棋盘技巧教你一个非常重要的编程技巧:横向思维。通过很好地设计算法,您通常可以对程序的运行情况产生重大影响。

答案 2 :(得分:5)

了解C,或者就此而言,任何低级编程语言,都让您有机会理解内存使用等内容(即为什么创建数百万个重型对象是件坏事),指针/对象引用如何工作,等

问题在于,随着我们创建不断增加的抽象级别,我们发现自己正在进行大量的“lego block”编程,而不了解legos的实际功能。通过拥有几乎无限的资源,我们开始像水一样处理记忆和资源,并倾向于通过在这种情况下投入更多铁来解决问题。

虽然不仅限于C,但是对于像Arduino或老式8位处理器这样的小得多的内存受限系统而言,在低级别工作会带来巨大的好处。它让您在更加平易近人的包装中体验接近金属编码的经验,并且在花费时间将应用程序压缩到512K之后,您会发现自己在日常编程中更大程度地应用这些技能。

所以语言本身并不重要,但是对所有比特如何结合起来有更深入的了解,以及如何在更接近硬件的水平上有效地工作是一套有益于任何软件开发人员的技能。

答案 3 :(得分:2)

首先,了解C可以帮助您了解内存在操作系统和其他高级语言中的工作原理。当您的C#或Java程序在内存使用方面充满活力时,理解引用(基本上只是指针)也会占用内存,并了解实现了多少数据结构(通过在C中创建自己的数据结构)可以帮助您理解你的字典保留了大量未实际使用的内存。

另一方面,了解C可以帮助您了解如何使用较低级别的操作系统功能。您不需要经常这样做,但有时您可能需要内存映射文件,或者在C#中使用编组,而C将极大地帮助您理解在发生这种情况时您正在做什么。

我认为C也有助于我理解网络协议,但我不能指出具体的例子。前几天我正在阅读另一个SO问题,有人在抱怨C的比特字段“基本上没用”,而我正在思考C比特字段代表低级网络协议的优雅程度。处理位结构的高级语言总是一团糟!

答案 4 :(得分:2)

一般来说,你知道的越多,你就会成为更优秀的程序员。

然而,有时候知道另一种语言,比如C,可能会让你做错事,因为可能有一种假设在更高级别的语言中是不正确的(例如Python或PHP)。例如,可以假设找到列表的长度可以是O(N),其中N是列表的长度。但是,在许多高级语言实例中可能并非如此。在Python中,对于大多数类似列表的东西,成本是O(1)。

了解有关语言细节的更多信息会有所帮助,但一般了解更多信息可能导致人们做出错误的假设。

答案 5 :(得分:1)

只是“知道”C不会让你变得更好。

但是,如果您了解整个事情,本机二进制文件如何工作,CPU如何使用它,什么是架构限制,您可以编写一个更容易为CPU编写的代码。

例如,L1 / L2缓存如何影响您的工作,以及如何编写代码以在L1 / L2缓存中获得更多匹配。使用C / C ++并进行大量优化时,您将不得不接受这类事情。

答案 6 :(得分:1)

知道C并不是因为C比其他许多语言更接近裸机。您需要更加了解如何分配/释放内存,因为您必须自己完成。自己动手可以帮助您了解您做出的许多决定的含义。

对我来说,只要您了解编译器/解释器(基本上)如何将代码映射到计算机上,任何语言都是可以接受的。在直接暴露这种语言的语言中做起来要容易一些,但是你应该能够通过一些阅读来弄清楚内存的分配和组织方式,哪种索引模式比其他模式更优化,结构是什么对特定应用程序等更有效。

我认为更重要的是对操作系统,内存架构和算法有很好的理解。如果您了解算法的工作原理,为什么选择一种算法或数据结构优于另一种算法或数据结构(例如,HashSet与List),以及您的代码如何映射到机器上,那么使用何种语言并不重要

答案 7 :(得分:1)

这是我对自己学习和自学编程的经验,特别是对C的理解,这可以追溯到1990年代早期,所以可能有点古老,但激情和动力很重要:

  • 学习理解计算机的低级原理,例如EGA / VGA编程,这里是C程序员PC指南中Simtel档案的link
  • 了解TSR的工作方式
  • 下载Bob Stout's snippets的整个档案,这是一个只收集一件事的C代码的大集合 - 研究它们并理解它,而不仅仅是,这些片段的集合力求可移植。
  • 在线浏览国际混淆C代码竞赛(IOCCC),了解C代码如何被滥用并理解该语言的内部性。最糟糕的代码滥用是赢家!下载档案并进行研究。
  • 和我一样,我喜欢臭名昭着的Ponzo's C Tutorial,这对我很有帮助,不幸的是,档案很难找到。如果有人知道从哪里获取它们,请发表评论,我将修改此答案以包含该链接。还有一个我能记得的 - 科罗纳多的[通用?] C教程,再一次,我对这一个的记忆是模糊的......
  • 看看Dobb博士的期刊和C用户期刊here - 我不知道你是否仍然可以将它们打印出来,但它们是经典的,可以记住我手里拿着印刷品的感觉撕掉家里输入代码看看会发生什么!
  • 抓住Turbo C v2的古代副本,我相信你可以从borland.com获得并只使用16位C编程来获得感觉和混乱的指针...确定它古老而古老但是玩指点就可以了。
  • 了解并学习指针,将here与遗产Simtel.net联系起来 - 这是获得更好词汇来实现C大师关键的重要环节,同时您还会发现大量有关C编程语言 - 我记得实际上订购了Simtel CD Archive并寻找C的东西......

答案 8 :(得分:0)

你需要在C中直接处理的其他一些语言,包括显式内存管理(malloc)和直接处理指针。

我的女朋友是从麻省理工学院(主要使用Java,Scheme和Python)毕业并获得计算机科学学位的一个学期,她目前在一家代码库为C ++的公司工作。在最初的几天里,她很难理解所有的指针/参考/等等。

另一方面,我发现从C ++迁移到Java非常容易,因为我从未对按值传递引用与引用传递感到困惑。

类似地,在C / C ++中,更简单的是原语只是编译器以不同的方式处理相同的位集,而不是像Python或Ruby这样的语言,其中所有东西都是具有自己独特属性的对象。

答案 9 :(得分:-2)

一个简单(不完全现实)的例子来说明上面的一些建议。考虑看似无害的

while(true)
   for(Iterator iter = foo.iterator(); iter.hasNext();)
       bar.doSomething( iter.next() )

或更高级别

while(true)
    for(Baz b: foo)
        bar.doSomething(b)

这里可能存在的问题是,每次围绕 while 循环时,都会创建一个新对象(迭代器)。如果你关心的只是程序员的方便,那么后者肯定更好。但是如果循环必须高效或机器受资源限制,那么你几乎受到高级语言设计者的支配。

例如,执行高性能Java的典型抱怨是执行停止,同时回收垃圾(例如所有分配的Iterator对象)。如果您的软件负责跟踪来袭导弹,自动驾驶客机,或者只是不让用户想知道GUI停止响应的原因,那就不太好了。

一种可能的解决方案(仍然使用更高级语言)将削弱迭代器的便利性,如

Iterator iter = new Iterator();
while(true)
    for(foo.initAlreadyAllocatedIterator(iter); iter.hasNext();)
       bar.doSomething(iter.next())

但是,如果您对内存分配有所了解,这只会有意义...否则它看起来就像一个令人讨厌的API。方便总是花费在某个地方,而了解较低级别的东西可以帮助您识别并降低这些成本。