2011-09-04 65 views
31

我总是阅读关于C程序设计语言中某些函数如何通过汇编写入来优化的内容。如果这句话听起来有点误导,让我道歉。C代码如何调用汇编代码(例如,优化的strlen)?

所以,我会说清楚:当你在UNIX/C系统上调用像strlen这样的函数时,你调用的实际函数是用汇编语言编写的?您能否以某种方式将程序集编写成C程序,还是外部呼叫情况?它能够做到这一点是C标准的一部分,还是它是一个特定于操作系统的东西?

+0

您需要的最重要的信息之一就是描述您的C编译器如何将参数传递给子例程并返回地址。这被称为该机器或处理器的“调用约定”。例如,x86上通常使用堆栈传递参数并返回地址。这只适用于变量参数函数,如果参数按从右到左的顺序被压入堆栈,则返回地址被压入。如果你编写汇编语言函数以期望这种堆栈布局(“激活记录”),那么唯一重要的... –

+0

...另外的实现是你的汇编函数,一旦汇编和链接,被分配一个地址你的程序代码段。因此,您的C代码可能会将处理器的执行转移到您的asm函数的这个地址。此时,只要你的函数用寄存器做了正确的事情(一些必须保存给调用者,比如EBP),就知道如何找到堆栈中的参数和返回地址,并将结果返回到正确的地方(在x86上的EAX中有32位返回值),那么没有任何关于它的资格不合格。 –

回答

34

C标准规定了每个库函数必须执行的操作,而不是如何实现。

几乎所有已知的C实现都被编译成机器语言。 C编译器/库的实现者应该如何选择实现像strlen这样的函数。他们可以选择在C中实现它并将其编译为一个对象,或者他们可以选择将它编写在汇编中并将其组装到一个对象中。或者他们可以用其他方式来实现它。只要您拨打strlen即可获得正确的效果和结果,这并不重要。

现在,很多C工具集都允许您编写内联汇编,但这绝对不是标准的一部分。任何这样的设备都必须作为C标准的扩展。

+0

非常感谢:)为我清除了很多。 –

+2

注意:如果像'strlen'这样的函数是用程序集而不是C编写的,通常是出于性能原因。 – 2011-09-11 15:42:13

17

在编译程序和汇编程序的结尾都是机器语言,所以可以互相调用。这样做的方法是让汇编代码使用与C编写的程序相同的调用约定(准备调用的方法,准备参数等)。可以找到用于x86处理器的流行调用约定的概述here

+4

对于x86 [Agner Fogs优化指南](http://www.agner.org/optimize/optimizing_assembly.pdf)也是一个有用的参考。 – user786653

+0

@user优秀的链接,谢谢! – fvu

0

您可以在C代码中编写内联汇编。这个语法的编译器是高度特定的,但通常使用asm关键字。查看内联汇编以获取更多信息。

4

当C代码由gcc编译时,它首先编译为汇编指令,然后再汇编成二进制,机器可执行文件。您可以通过指定-S来查看生成的汇编程序指令,如gcc file.c -S中所述。

汇编代码只是传递C到汇编器编译的第一阶段,并且然后从C.

+4

在汇编中实现函数的一种方法是编写一个空的C函数,用-S编译它,然后直接编辑汇编文件。 – Giorgio

1

一种方式编译为做到这一点是使用内联汇编代码区分。这意味着您可以将汇编代码直接写入您的C代码中。具体的语法是编译器特定的。例如,请参阅GCC syntaxMS Visual C++ syntax

8

很多(大多数?)C编译器确实碰巧支持inline assembly,虽然它不是标准的一部分。也就是说,没有严格的需要编译器支持任何这样的事情。首先,要认识到程序集大多只是人类(半)可读的机器代码,而C最终仍然是机器代码。

“调用”一个C函数只是根据某些已建立的调用约定生成一组准备寄存器,堆栈和/或某种其他机器相关机制的指令,然后跳转到被调用函数的开始位置。

一个汇编代码块可以符合适当的调用约定,从而生成一个机器代码块,其中最初用C语言编写的另一块机器代码能够调用。当然,反过来也是可能的。

调用约定,装配过程和链接过程(将装配生成的目标文件与C生成的目标文件链接起来)的细节可能在平台,编译器和链接器之间变化很大。您选择的平台的良好汇编教程可能会涵盖这些细节。

我碰巧喜欢以x86为中心的PC Assembly Tutorial,它特别针对接口汇编和C代码。

+0

有趣的教程,感谢您的帮助:) –