在听StackOverflow播客的时候,经常有人说“真正的程序员”是用C语言编写的,而C语言的速度要快得多,因为它“接近机器”。把前面的断言留到另一篇文章,C有什么特别之处,使它比其他语言更快?或者换句话说:什么能阻止其他语言编译成二进制代码,使其运行速度与C语言一样快?
当前回答
主要的因素是它是一种静态类型的语言,可以编译为机器代码。此外,由于它是一种低级语言,它通常不会做任何您不让它做的事情。
这些是我想到的其他一些因素。
Variables are not automatically initialized No bounds checking on arrays Unchecked pointer manipulation No integer overflow checking Statically-typed variables Function calls are static (unless you use function pointers) Compiler writers have had lots of time to improve the optimizing code. Also, people program in C for the purpose of getting the best performance, so there's pressure to optimize the code. Parts of the language specification are implementation-defined, so compilers are free to do things in the most optimal way
大多数静态类型语言的编译速度可以和C语言一样快,甚至比C语言更快,特别是如果它们可以假设C语言因为指针别名等原因而不能这样做的话。
其他回答
甚至C和c++之间的差异有时也会很大。
当你为一个对象分配内存,调用构造函数,在字边界上对齐内存等等,程序最终会经历很多开销,这些开销都是从程序员那里抽象出来的。
C迫使您查看程序所做的每一件事,通常是非常精细的细节。这使得编写执行大量与当前目标无关的任务的代码变得更加困难(尽管并非完全不可能)。
因此,例如在BASIC程序中,你可以使用INPUT关键字从STDIN读取字符串并自动为其变量分配内存,在C中,程序员通常已经分配了内存,并可以控制诸如程序是否阻塞I/O,以及它是否在获得所需信息后停止读取输入或继续读取字符到行尾等事情。
C also performs a lot less error-checking than other languages, presuming the programmer knows what they're doing. So whereas in PHP if you declare a string $myStr = getInput(); and go on to reference $myStr[20], but the input was only 10 characters long, PHP will catch this and safely return to you a blank string. C assumes that you've either allocated enough memory to hold data past the end of the string or that you know what information comes after the string and are trying to reference that instead. These small factors have a huge impact on overhead in aggregate.
这实际上是一个长期存在的谎言。虽然C程序确实经常更快,但情况并非总是如此,特别是当C程序员不太擅长它的时候。
人们往往会忘记的一个明显的漏洞是,当程序必须为某种IO阻塞时,比如任何GUI程序中的用户输入。在这些情况下,使用什么语言并不重要,因为您受到数据传入速度的限制,而不是处理数据的速度。在这种情况下,不管你使用的是C、Java、c#甚至Perl;你不能比数据进入的速度更快。
The other major thing is that using garbage collection and not using proper pointers allows the virtual machine to make a number of optimizations not available in other languages. For instance, the JVM is capable of moving objects around on the heap to defragment it. This makes future allocations much faster since the next index can simply be used rather than looking it up in a table. Modern JVMs also don't have to actually deallocate memory; instead, they just move the live objects around when they GC and the spent memory from the dead objects is recovered essentially for free.
This also brings up an interesting point about C and even more so in C++. There is something of a design philosophy of "If you don't need it, you don't pay for it." The problem is that if you do want it, you end up paying through the nose for it. For instance, the vtable implementation in Java tends to be a lot better than C++ implementations, so virtual function calls are a lot faster. On the other hand, you have no choice but to use virtual functions in Java and they still cost something, but in programs that use a lot of virtual functions, the reduced cost adds up.
C的设计者们已经做出了取舍。也就是说,他们决定把速度放在安全之上。C不会
检查数组下标边界 检查未初始化的变量值 检查内存泄漏 检查空指针解引用
当你索引到一个数组时,在Java中,它接受虚拟机中的一些方法调用,绑定检查和其他健全检查。这是有效的,绝对没问题,因为它在应有的地方增加了安全性。但是在C语言中,即使是非常微不足道的东西也不会被放在安全的地方。例如,C不要求memcpy检查要复制的区域是否重叠。它并不是一种用于编写大型商业应用程序的语言。
但是这些设计决策并不是C语言中的bug。它们是被设计出来的,因为它允许编译器和库编写者从计算机中获得每一点性能。下面是C语言的精神——C语言的基本原理文档是这样解释的:
C code can be non-portable. Although it strove to give programmers the opportunity to write truly portable programs, the Committee did not want to force programmers into writing portably, to preclude the use of C as a ``high-level assembler'': the ability to write machine-specific code is one of the strengths of C. Keep the spirit of C. The Committee kept as a major goal to preserve the traditional spirit of C. There are many facets of the spirit of C, but the essence is a community sentiment of the underlying principles upon which the C language is based. Some of the facets of the spirit of C can be summarized in phrases like Trust the programmer. Don't prevent the programmer from doing what needs to be done. Keep the language small and simple. Provide only one way to do an operation. Make it fast, even if it is not guaranteed to be portable. The last proverb needs a little explanation. The potential for efficient code generation is one of the most important strengths of C. To help ensure that no code explosion occurs for what appears to be a very simple operation, many operations are defined to be how the target machine's hardware does it rather than by a general abstract rule. An example of this willingness to live with what the machine does can be seen in the rules that govern the widening of char objects for use in expressions: whether the values of char objects widen to signed or unsigned quantities typically depends on which byte operation is more efficient on the target machine.
只需在您的IDE中逐步检查机器代码,您就会看到为什么它更快(如果它更快的话)。它省去了很多手把手的环节。很有可能你的Cxx也会被告知不要使用,在这种情况下,它应该是相同的。
编译器优化被高估了,就像几乎所有关于语言速度的看法一样。
优化生成的代码只会对热点代码产生影响,也就是说,没有函数调用(显式或隐式)的紧凑算法。在其他地方,收效甚微。
For the most part, every C instruction corresponds to a very few assembler instructions. You are essentially writing higher level machine code, so you have control over almost everything the processor does. Many other compiled languages, such as C++, have a lot of simple looking instructions that can turn into much more code than you think it does (virtual functions, copy constructors, etc..) And interpreted languages like Java or Ruby have another layer of instructions that you never see - the Virtual Machine or Interpreter.