在听StackOverflow播客的时候,经常有人说“真正的程序员”是用C语言编写的,而C语言的速度要快得多,因为它“接近机器”。把前面的断言留到另一篇文章,C有什么特别之处,使它比其他语言更快?或者换句话说:什么能阻止其他语言编译成二进制代码,使其运行速度与C语言一样快?
当前回答
这些答案中的许多都给出了为什么C更快或更快的有效理由(无论是在一般情况下还是在特定的场景中)。不可否认的是:
Many other languages provide automatic features that we take for granted. Bounds checking, run-time type checking, and automatic memory management, for example, don't come for free. There is at least some cost associated with these features, which we may not think about—or even realize—while writing code that uses these features. The step from source to machine is often not as direct in other languages as it is in C. OTOH, to say that compiled C code executes faster than other code written in other languages is a generalization that isn't always true. Counter-examples are easy to find (or contrive).
尽管如此,我还是注意到另一件事,我认为它比其他任何因素都更能影响C与许多其他语言的比较性能。即:
其他语言通常更容易编写执行较慢的代码。通常,它甚至受到该语言的设计哲学的鼓励。推论:C程序员更有可能编写不执行不必要操作的代码。
例如,考虑一个简单的Windows程序,其中创建了一个主窗口。C版本将填充一个WNDCLASS[EX]结构,该结构将传递给RegisterClass[EX],然后调用CreateWindow[EX]并进入消息循环。以下是高度简化和缩写的代码:
WNDCLASS wc;
MSG msg;
wc.style = 0;
wc.lpfnWndProc = &WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "MainWndCls";
RegisterClass(&wc);
CreateWindow("MainWndCls", "", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
c#中类似的程序可能只有一行代码:
Application.Run(new Form());
这一行代码提供了近20行C代码所提供的所有功能,并添加了一些我们遗漏的功能,例如错误检查。这个更丰富、更完整的库(与典型C项目中使用的库相比)为我们做了很多工作,解放了我们的时间来编写更多的代码片段,这些代码对我们来说很短,但涉及到幕后的许多步骤。
但是,一个丰富的库使简单快速的代码膨胀并不是我真正想要的。当您开始检查我们的小一行程序实际执行时实际发生了什么时,我的观点就更加明显了。为了好玩,可以在Visual Studio 2008或更高版本中启用. net源代码访问,然后进入上面简单的一行代码。您将遇到的一个有趣的小珍宝是Control的getter中的这个注释。CreateParams:
// In a typical control this is accessed ten times to create and show a control.
// It is a net memory savings, then, to maintain a copy on control.
//
if (createParams == null) {
createParams = new CreateParams();
}
十次。信息大致相当于存储在WNDCLASSEX结构中的内容和传递给CreateWindowEx的内容的总和,在它存储在WNDCLASSEX结构中并传递给RegisterClassEx和CreateWindowEx之前,从Control类中检索10次。
总而言之,在c#中执行这个非常基本的任务的指令数量比在C中多2-3个数量级,部分原因是使用了功能丰富的库,这是必然的,而我们简单的C代码只做了我们需要的事情,仅此而已。但部分原因是. net框架的模块化、面向对象的特性导致了大量的重复执行,而过程方法通常可以避免这些重复执行。
我并不是要挑c#或。net框架。我也不是说模块化、泛化、库/语言特性、OOP等等都是不好的东西。我曾经用C语言进行大部分开发,后来用c++,最近用c#。类似地,在使用C之前,我主要使用汇编。我的语言每“提高”一步,我就能在更短的时间内写出更好、更可维护、更健壮的程序。然而,它们的执行速度往往要慢一些。
其他回答
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.
这是自动和手动的区别,高级语言是抽象的,因此是自动化的。C/ c++是人工控制和处理的,甚至错误检查代码有时也是人工劳动。
C和c++也是编译语言,这意味着没有任何一种语言可以在任何地方运行,这些语言必须针对您使用的硬件进行微调,从而增加了额外的隐患。尽管现在C/ c++编译器在所有平台上变得越来越普遍,这有点令人不安。您可以在平台之间进行交叉编译。这仍然不是一个到处运行的情况,你基本上是在指示编译器a针对编译器B编译相同的代码,不同的架构。
归根结底,C语言并不意味着容易理解或推理,这也是为什么它们被称为系统语言。他们出现在所有高层次抽象的废话之前。这也是为什么它们不用于前端web编程。他们只是不适合这项任务,他们的意思是解决传统语言工具无法解决的复杂问题。
这就是为什么你会得到一些疯狂的东西(微架构、驱动程序、量子物理、AAA游戏、操作系统),这些东西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快,其他语言中的算法或设计模式的一些实现可能比C快。
当人们说C语言很快,然后转向谈论其他语言时,他们通常是在用C语言的性能作为基准。
推荐文章
- 如何找到Java堆大小和内存使用(Linux)?
- 如何在HTML5中改变视频的播放速度?
- 我如何提高ASP。NET MVC应用程序性能?
- C多行宏:do/while(0) vs作用域块
- time_t最终的类型定义是什么?
- 我需要显式处理负数或零时,总和平方数字?
- 列表推导式和函数式函数比for循环更快吗?
- 函数名周围的括号是什么意思?
- 用C语言创建自己的头文件
- 格式化IO函数(*printf / *scanf)中的转换说明符%i和%d之间的区别是什么?
- main()中的Return语句vs exit()
- 如果不是内存地址,C指针到底是什么?
- 我如何在Visual Studio中预处理后看到C/ c++源文件?
- Pandas loc vs iloc vs at vs iat?
- 当WebSockets可用时,为什么要使用AJAX ?