C代码如何调用汇编代码(例如优化后的strlen函数)?

31

我经常读到有关C编程语言中某些函数通过用汇编语言编写来进行优化的内容。如果这句话听起来有点误导人,那么我表示歉意。

因此,我将明确表达:当您在UNIX / C系统上调用一些函数(如strlen)时,实际调用的函数是用汇编语言编写的吗?您是否可以将汇编语言直接写入C程序中,还是需要进行外部调用?这是否是C标准的一部分,还是特定于操作系统?


你需要了解的最重要的信息之一是关于C编译器如何将参数和返回地址传递给子程序的描述。这被称为该机器或处理器的“调用约定”。例如,在x86上,通过使用堆栈来传递参数和返回地址是很典型的。如果参数按从右到左的顺序推入堆栈,然后推入返回地址,那么只有在变量参数函数中才能起作用。如果您编写汇编语言函数以期望此堆栈布局(“激活记录”),则唯一重要的... - Heath Hunnicutt
另一个需要注意的问题是,一旦您的汇编函数被汇编和链接,它就会被分配到程序代码段中的一个地址。因此,您的C代码可以将处理器的执行转移到该汇编函数的地址。在这一点上,只要您的函数正确处理寄存器(有些必须为调用者保留,例如EBP),知道如何在堆栈上找到参数和返回地址,并将其结果放在正确的位置(32位返回值在x86上放在EAX中),那么没有任何不合格的地方。 - Heath Hunnicutt
6个回答

34

C标准规定每个库函数必须做什么而不是如何实现。

几乎所有已知的C实现都被编译成机器语言。对于C编译器/库的实现者来说,他们可以选择如何实现strlen等函数。他们可以选择用C实现并将其编译为对象,或者他们可以选择用汇编语言编写并将其汇编为对象。或者他们可以以其他方式实现它。只要在调用strlen时得到正确的效果和结果就可以了。

现在,许多C工具集确实允许你编写内联汇编,但这绝对不是标准的一部分。任何此类设施都必须作为C标准的扩展包括进去。


2
请注意:如果像 strlen 这样的函数是用汇编语言而不是 C 语言编写的,通常是出于性能考虑。 - user142019

17

在最终编译的程序和汇编程序中,它们都是机器语言,因此它们可以互相调用。实现这一点的方式是通过汇编代码使用与C语言编写的程序相同的调用约定(准备调用、准备参数等方式)。有关x86处理器流行的调用约定的概述可以在这里找到。


4
对于x86架构,Agner Fog的优化指南(http://www.agner.org/optimize/optimizing_assembly.pdf)也是一个有用的参考资料。 - user786653

8
许多(大多数?)C编译器确实支持inline assembly,尽管它不是标准的一部分。也就是说,编译器没有必要支持任何这样的东西。
首先,要认识到汇编语言基本上只是人类(半)可读的机器码,而C最终也会转换为机器码。
“调用”C函数只是生成一组指令,根据某个已建立的调用约定准备寄存器、堆栈和/或其他一些机器相关的机制,然后跳转到被调用函数的开始处。
一段汇编代码块可以符合适当的调用约定,从而生成一个机器码块,另一个最初用C编写的机器码块可以调用它。反过来也是可能的。
调用约定、汇编过程和链接过程(将汇编生成的目标文件与C生成的目标文件链接在一起)的详细信息可能在平台、编译器和链接器之间都有很大的差异。针对您选择的平台的良好汇编教程可能会涵盖这些细节。
我喜欢以x86为中心的PC Assembly Tutorial,它专门讲解汇编和C代码的接口。

4

当C代码通过gcc编译时,首先被编译成汇编指令,然后再编译成二进制的可执行文件。您可以通过指定-S来查看生成的汇编指令,如:gcc file.c -S

汇编代码只是经过C到汇编编译的第一阶段,然后与从C编译的代码无法区分。


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

1

有一种方法是使用内联汇编。这意味着您可以直接将汇编代码编写到C代码中。具体语法因编译器而异。例如,请参阅 GCC 语法MS Visual C++ 语法


0

你可以在C代码中编写内联汇编。这种语法高度依赖于编译器,但通常使用asm关键字。请查阅有关内联汇编的更多信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接