有哪些具体例子可以说明了解 C 语言可以让您成为更好的高级程序员?
阅读 Joel 的文章回到基础并看到许多类似的问题,我已经开始想知道了解 C 之类的东西可以让你成为一个更好的高级程序员,有哪些具体的例子。
我想知道是否有很多这样的例子。很多时候,这个问题的答案类似于“了解 C 可以让您更好地了解幕后发生的事情”或“您的程序需要坚实的基础 ”,而这些答案并没有多大意义。我想了解您将从了解低级概念中受益的不同具体方式,
Joel 举了几个例子:二进制数据库与 XML 和字符串。但两个例子并不能真正证明学习 C 和/或汇编的合理性。所以我的问题是:有哪些具体例子可以说明了解 C 语言可以让你成为更好的高级程序员?
I know about the existance of question such as this one and this one. Let me explain.
Afet reading Joel's article Back to Basics and seeing many similar questions on SO, I've begun to wonder what are specific examples of situations where knowing stuff like C can make you a better high level programmer.
What I want to know is if there are many examples of this. Many times, the answer to this question is something like "Knowing C gives you a better feel of what's happening under the covers" or "You need a solid foundation for your program", and these answers don't have much meaning. I want to understand the different specific ways in which you will benefit from knowing low level concepts,
Joel gave a couple of examples: Binary databases vs XML, and strings. But two examples don't really justify learning C and/or Assembly. So my question is this: What specific examples are there of knowing C making you a better high level programmer?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
我教学生以及与只学习高级语言的人一起工作的经验是,他们倾向于在一定的高级抽象水平上思考,并且他们认为“一切都是免费的”。他们可以成为非常有能力的程序员,但最终他们必须处理一些存在性能问题的代码,然后这些问题就会困扰他们。
当您大量使用 C 语言时,您确实会考虑内存分配。您经常会考虑内存布局(如果存在问题,还需要考虑缓存位置)。您了解某些图形操作如何以及为何成本高昂。某些套接字行为的效率如何或低效。缓冲区如何工作等等。我觉得,当您确实知道它是如何在幕后实现的时,使用高级语言中的抽象有时会给您在考虑性能时带来“额外的秘密”。
例如,Java有一个垃圾收集器,你不能直接将东西直接分配给内存。然而,您可以做出某些影响性能的设计选择(例如,使用自定义数据结构),因为与 C 中的问题相同的原因。
此外,更一般地说,我认为对于高级程序员来说很重要不仅知道大 O 表示法(大多数学校都会教授),而且在现实生活中的应用中常数也很重要(学校试图忽略这一点)。我的轶事经验是,具有两种语言水平技能的人往往对常数有更好的理解,也许是因为我上面描述的。
此外,我见过的许多更高级别的系统与较低级别的库和基础设施交互。例如,一些通信、数据库或图形库。某些设备的一些驱动程序等。如果您是一名高级程序员,您最终可能不得不冒险去那里,这至少有助于了解正在发生的事情。
My experience with teaching students and working with people who only studied high-level languages is that they tend to think at a certain high level of abstraction, and they assume that "everything comes for free". They can become very competent programmers, but eventually they have to deal with some code that has performance issues and then it comes to bite them.
When you work a lot with C, you do think about memory allocation. You often think about memory layout (and cache locality if that's an issue). You understand how and why certain graphics operations just cost a lot. How efficient or inefficient certain socket behaviors are. How buffers work, etc. I feel that using the abstractions in a higher level language when you do know how it is implemented below the covers sometimes gives you "that extra secret sauce" when thinking about performance.
For example, Java has a garbage collector and you can't directly assign things to memory directly. And yet, you can make certain design choices (e.g., with custom data structures) that affect performance because of the same reasons this would be an issue in C.
Also, and more generally, I feel that it is important for a power programmer to not only know big-O notation (which most schools teach), but that in real-life applications the constant is also important (which schools try to ignore). My anecdotal experience is that people with skills in both language levels tend to have a better understanding of the constant, perhaps because of what I described above.
In addition, many higher level systems that I have seen interface with lower level libraries and infrastructures. For instance, some communications, databases or graphics libraries. Some drivers for certain devices, etc. If you are a power programmer, you may eventially have to venture out there and it helps to at least have an idea of what is going on.
了解底层的东西会有很大帮助。
要成为一名赛车手,您必须学习并了解轮胎如何抓地的基本物理原理。任何人都可以学会开得很快,但你需要很好地理解“低水平”的东西(力和摩擦力、赛车线、精细油门和制动控制等),以获得最后百分之几的性能,这将使你能够赢得比赛。
例如,如果您了解 CPU 架构如何在计算机中工作,您可以编写更适合它的代码(例如,如果您知道每个 CPU 缓存行中有一定的 CPU 缓存大小或一定数量的字节,您可以安排数据结构和访问它们的方式以充分利用缓存 - 例如,由于 CPU 缓存的原因,按顺序处理数组的许多元素通常比处理随机元素更快)。如果您有一台多核计算机,那么了解线程等低级技术如何工作可以带来巨大的好处(就像不了解低级技术可能会导致线程灾难一样)。
如果您了解磁盘 I/O 和缓存的工作原理,则可以修改文件操作以使其更好地工作(例如,如果您从一个文件读取并写入另一个文件,则处理 RAM 中的大批量数据可以帮助减少 I/O 争用) 如果您了解虚拟函数的工作原理
,则可以很好地设计使用虚拟函数的高级代码。如果使用不当,它们会严重影响性能。
如果您了解绘图的处理方式,则可以使用巧妙的技巧来提高绘图速度。例如,您可以通过交替绘制 64 个白色和黑色方块来绘制棋盘。但通常先绘制 32 个白色方块,然后绘制 32 个黑色方块会更快(因为您只需更改绘制颜色两次,而不是 64 次)。但实际上您可以将整个板绘制为黑色,然后对板上的 4 个条纹和板上的 4 个白色条纹进行异或,这仍然会快得多(2 种颜色变化,并且只绘制 9 个矩形,而不是 64 个)。这个棋盘技巧教你一项非常重要的编程技能:横向思维。通过良好地设计算法,通常可以对程序的运行效果产生很大的影响。
Knowing low level stuff can help a lot.
To become a racing driver, you have to learn and understand the basic physics of how tyres grip the road. Anyone can learn to drive pretty fast, but you need a good understanding of the "low level" stuff (forces and friction, racing lines, fine throttle and brake control, etc) to get those last few percent of performance that will allow you to win the race.
For example, if you understand how the CPU architecture works in your computer, you can write code that works better with it (e.g. if you know you have a certain CPU cache size or a certain number of bytes in each CPU cache line, you can arrange your data structures and the way that you access them to make the best use of the cache - for example, processing many elements of an array in order is often faster than processing random elements, due to the CPU cache). If you have a multi-core computer, then understanding how low level techniques like threading work can gave huge benefits (just as not understanding the low level can lead to disaster in threading).
If you understand how Disk I/O and caching works, you can modify file operations to work well with it (e.g. if you read from one file and write to another, working on large batches of data in RAM can help reduce I/O contention between the reading and writing phases of your code, and vastly improve throughput)
If you understand how virtual functions work, you can design high-level code that uses virtual functions well. If used incorrectly they can severely hamper performance.
If you understand how drawing is handled, you can use clever tricks to improve drawing speed. e.g. You can draw a chessboard by alternately drawing 64 white and black squares. But it is often faster to draw 32 white sqares and then 32 black ones (because you only have to change the drawing colour twice instead of 64 times). But you can actually draw the whole board black, then XOR 4 stripes across the board and 4 stripes down the board in white, and this can be much faster still (2 colour changes, and only 9 rectangles to draw instead of 64). This chessboard trick teaches you a very important programming skill: Lateral thinking. By designing your algorithm well, you can often make a big difference to how well your program operates.
了解 C,或者就此而言,任何低级编程语言,让您有机会了解诸如内存使用情况(即为什么创建数百万个重对象是一件坏事)、指针/对象引用如何工作等
。问题是,随着我们创建的抽象级别不断增加,我们发现自己进行了大量的“乐高积木”编程,而不了解乐高积木的实际功能。由于拥有几乎无限的资源,我们开始像对待水一样对待内存和资源,并倾向于通过向情况扔更多铁来解决问题。
虽然不限于 C,但使用 Arduino 或老式 8 位处理器等更小的、内存受限的系统在低级别上工作有巨大的好处。它让您在更平易近人的包中体验接近金属编码的体验,在花时间将应用程序压缩到 512K 后,您会发现自己在日常编程中在更大的层面上应用这些技能。
因此,语言本身并不重要,但更深入地了解所有这些部分如何组合在一起,以及如何在更接近硬件的级别上有效工作,是对任何软件开发人员都有益的一组技能。
Understanding C, or for that matter, any low level programming language, gives you an opportunity to understand things like memory usage (i.e. why is it a bad thing to create several million heavy objects), how pointers/object references work, etc.
The problem is that as we've created ever increasing levels of abstraction, we find ourselves doing a lot of 'lego block' programming, without understanding how the legos actually function. And by having almost infinite resources, we start treating memory and resources like water, and tend to solve problems by throwing more iron at the situation.
While not limited to C, there's a tremendous benefit to working at a low level with much smaller, memory constrained systems like the Arduino or old-school 8-bit processors. It lets you experience close to the metal coding in a much more approachable package, and after spending time squeezing apps into 512K, you will find yourself applying these skills at a larger level within your day to day programming.
So the language itself is not important, but having a deeper appreciation for how all of the bits come together, and how to work effectively at a level closer to the hardware is a set of skills beneficial to any software developer.
其一,了解 C 语言可以帮助您了解内存在操作系统和其他高级语言中的工作原理。当您的 C# 或 Java 程序内存使用量激增时,了解引用(基本上只是指针)也会占用内存,并了解实现了多少数据结构(通过在 C 中创建自己的数据结构获得)可以帮助您理解这一点您的字典保留了大量未实际使用的内存。
另一方面,了解 C 可以帮助您了解如何使用较低级别的操作系统功能。您并不经常需要这样做,但有时您可能需要内存映射文件,或者在 C# 中使用编组,当发生这种情况时,C 将极大地帮助您理解您正在做什么。
我认为 C 也有助于我理解网络协议,但我无法指出具体的例子。前几天我读到另一个 SO 问题,其中有人抱怨 C 的位字段“基本上无用”,而我在想 C 位字段如何优雅地表示低级网络协议。处理位结构的高级语言总是会弄得一团糟!
For one, knowing C helps you understand how memory works in the OS and in other high level languages. When your C# or Java program balloons on memory usage, understanding that references (which are basically just pointers) take memory too, and understand how many of the data structures are implemented (which you get from making your own in C) helps you understand that your dictionary is reserving huge amounts of memory that aren't actually used.
For another, knowing C can help you understand how to make use of lower level operating system features. You don't need this often, but sometimes you may need memory mapped files, or to use marshalling in C#, and C will greatly help understand what you're doing when that happens.
I think C has also helped my understanding of network protocols, but I can't put my finger on specific examples. I was reading another SO question the other day where someone was complaining about how C's bit-fields are 'basically useless' and I was thinking how elegantly C bit fields represent low-level network protocols. High level languages dealing with structures of bits always end up a mess!
一般来说,你知道的越多,你就会成为越好的程序员。
然而,有时了解另一种语言(例如 C)可能会让您做错误的事情,因为在高级语言(例如 Python 或 PHP)中可能存在不正确的假设。例如,人们可能假设查找列表的长度可能是 O(N),其中 N 是列表的长度。然而,在许多高级语言实例中情况可能并非如此。在 Python 中,对于大多数类似列表的事物,成本是 O(1)。
更多地了解一种语言的细节会有所帮助,但了解更多的一般性可能会导致人们做出错误的假设。
In general, the more you know, the better programmer you will be.
However, sometimes knowing another language, such as C, can make you do the wrong thing, because there might be an assumption that is not true in a higher-level language (such as Python, or PHP). For example, one might assume that finding the length of a list might be O(N) where N is the length of the list. However, this is probably not the case in many high-level language instances. In Python, for most list-like things the cost is O(1).
Knowing more about the specifics of a language will help, but knowing more in general might lead one to make incorrect assumptions.
仅仅“了解”C 并不会让你变得更好。
但是,如果您了解整个事情,本机二进制文件如何工作,CPU 如何与其一起工作,架构限制是什么,您可能会编写对 CPU 更容易的代码。
例如,L1/L2 缓存如何影响您的工作,以及您应该如何编写代码才能在 L1/L2 缓存中获得更多命中。当使用 C/C++ 并进行大量优化时,您必须进行此类操作。
Just "knowing" C would not make you better.
But, if you understand the whole thing, how native binaries work, how does CPU work with it, what are architecture limitations, you may write a code which is easier for CPU.
For example, how L1/L2 caches affect your work, and how should you write your code to have more hits in L1/L2 caches. When working with C/C++ and doing heavy optimizations, you will have to go down to that kind of things.
与其说是了解 C,不如说 C 比许多其他语言更接近裸机。您需要更加了解如何分配/释放内存,因为您必须自己执行此操作。自己做可以帮助您了解您所做的许多决定的含义。
对我来说,只要您了解编译器/解释器(基本上)如何将代码映射到机器上,任何语言都是可以接受的。使用直接公开这一点的语言会更容易一些,但是通过一些阅读,您应该能够弄清楚内存是如何分配和组织的,哪种索引模式比其他模式更优化,什么构造是对特定应用程序更有效等。
我认为更重要的是对操作系统、内存架构和算法的良好理解。如果您了解算法的工作原理,为什么选择一种算法或数据结构优于另一种算法或数据结构(例如,HashSet 与 List),以及您的代码如何映射到机器上,那么您使用什么语言并不重要。
It isn't so much knowing C as it is that C is closer to the bare metal than many other languages. You need to be more aware of how to allocate/deallocate memory because you have to do it yourself. Doing it yourself helps you understand the implications of many decisions that you make.
To me any language is acceptable as long as you understand how the compiler/interpreter (basically) maps your code onto the machine. It's a bit easier to do in a language that exposes this directly, but you should be able to, with a bit of reading, figure out how memory is allocated and organized, what sort of indexing patterns are more optimal than others, what constructs are more efficient for particular applications, etc.
More important, I think, is a good understanding of operating systems, memory architectures, and algorithms. If you understand how your algorithm works, why it would be better to choose one algorithm or data structure over another (e.g., HashSet vs. List), and how your code maps onto the machine, it shouldn't matter what language you are using.
这是我学习和自学编程的经验,特别是理解 C,这可以追溯到 1990 年代初,所以可能有点过时,但热情和动力很重要:
This is my experience of how I learnt and taught myself programming, specifically, understanding C, this is going back to early 1990's so may be a bit antique, but the passion and the drive is important:
您必须在 C 中直接处理其他语言从您那里抽象出来的一些事情,包括显式内存管理 (
malloc
) 和直接处理指针。我女朋友刚从 MIT(他们主要使用 Java、Scheme 和 Python)毕业一个学期,获得了计算机科学学位,她目前在一家代码库是 C++ 的公司工作。在最初的几天里,她很难理解所有的指示/参考资料/等。
另一方面,我发现从 C++ 迁移到 Java 非常容易,因为我从来没有混淆过按值传递引用和按引用传递。
类似地,在 C/C++ 中,更明显的是,原语只是编译器以不同的方式处理相同的位集,而不像 Python 或 Ruby 这样的语言,其中一切都是具有自己独特属性的对象。
A couple of things that you have to deal directly with in C that other languages abstract away from you include explicit memory management (
malloc
) and dealing directly with pointers.My girlfriend is one semester from graduating MIT (where they mainly use Java, Scheme, and Python) with a Computer Science degree, and she is currently working at a company whose codebase is in C++. For the first few days she had a difficult time understanding all the pointers/references/etc.
On the other hand, I found moving from C++ to Java very easy, because I was never confused about pass-references-by-value vs pass-by-reference.
Similarly, in C/C++ it is much more apparent that primitives are just the compiler treating the same sets of bits in different ways, as opposed to a language like Python or Ruby where everything is an object with its own distinct properties.
一个简单的(不完全现实的)例子来说明上面的一些建议。考虑看似无害
或什至更高的级别
这里可能的问题是,每次 while 循环都会创建一个新对象(迭代器)。如果你关心的只是程序员的便利性,那么后者肯定更好。但是,如果循环必须高效或者机器资源有限,那么您几乎就要受到高级语言设计者的摆布。
例如,执行高性能 Java 的一个典型抱怨是在垃圾(例如所有分配的 Iterator 对象)被回收时停止执行。如果你的软件负责跟踪来袭导弹、自动驾驶客机,或者只是不让用户想知道为什么 GUI 停止响应,那就不太好了。
一种可能的解决方案(仍然是高级语言)是将迭代器的便利性削弱为类似的东西,
但是只有当您对内存分配有所了解时,这才有意义……否则它看起来就像是一个令人讨厌的 API。便利总是需要付出代价的,了解较低级别的东西可以帮助您识别并减轻这些成本。
A simple (not entirely realistic) example to illustrate some of the advice above. Consider the seemingly harmless
or the even higher level
A possible problem here is that each time round the while loop a new object (the iterator) is created. If all you care about is programmer convenience, then the latter is definitely better. But if the loop has to be efficient or the machine is resource constrained then you are pretty much at the mercy of the designers of your high level language.
For example, a typical complaint for doing high-performance Java is having execution stop while garbage (such as all those allocated Iterator objects) is reclaimed. Not very good if your software is charged with tracking incoming missiles, auto-piloting a passenger jet, or just not leaving the user wondering why the GUI has stopped responding.
One possible solution (still in the higher-level language) would be to weaken the convenience of the iterator to something like
But this would only make sense if you had some idea about memory allocation...otherwise it just looks like a nasty API. Convenience always costs somewhere, and knowing lower-level stuff can help you identify and mitigate those costs.