分类: LINUX
2015-10-03 09:38:14
inline函数有两种声明形式,一种是static __inline__,另一种是extern __inline__,inline不都是要展开的么,那么这两种形式有什么不同呢?还是只是形式上的不同?
如果一个函数既是inline又是static,如果所有对函数的调用都能被展开在调用者里面,并且这个函数的地址从来没有被使用过,那么这种 情况下不存在对这个函数本身汇编代码的引用。这时,GNU CC实际上并不输出这个函数的汇编代码,除非加选项“-fkeep-inline-functions”。存在一些由于各种原因不能被展开的调用(比如, 在函数声明前的调用不能被展开,定义中的递归调用同样也不行)。如果存在未展开的函数调用,那么这个函数象通常一样被编译生成汇编代码。如果这个函数被通 过地址引用,那么这个函数也必须象一般函数那样被编译生成,因为那样的引用不能够被展开(inlined)。
当一个inline函数不是 static时,那么编译器必须假设其他源程序中可能存在调用,因为一个全局符号只能被定义一次,所以这个函数在其他源程序中不能被定义,于是那里的调用 不能够被展开。因此,一个非static的inline函数总是同普通函数一样被编译生成。 如果函数声明中同时使用了inline和extern,那么这个定义只被用来inline展开。这个函数体的汇编代码从来都不会被编译生成,即使你显示引用了它的地址,显示引用的地址变成一个外部引用,就像你仅仅声明了这个函数,而没有定义它一样。
inline 和extern的结合几乎达到一个宏定义的效果。使用它的方法是在头文件中使用这些关键字写出函数定义,同时在库文件中放入另一份函数体定义的拷贝(没有 inline和extern)。在头文件中的定义将会使大部分对函数的调用被inline展开。如果存在对这个函数的使用,它们将会引用库中的单一拷贝。
嵌入式汇编:
在汇编语言中调用c语言:程序首先按照逆向顺序把函数的参数压入栈中,最右边的参数最先压栈,让后执行指令CALL函数名调用函数,然后返回后将先前压栈的参数清除掉:
例子:
.c
int add(int a,int b)
{
return a+b;
}
.s
SYSWRITE = 4
.global main
.data
format: .asciz "%d\n"
.text
main:
pushl %ebp
movl %esp, %ebp
movl $6,%eax
pushl %eax
movl $5,%eax
pushl %eax
call add
pushl %eax
pushl $format
call printf
movl %ebp,%esp;
popl %ebp
ret
在c语言中调用汇编:
callee.s
SYSWRITE = 4
.global mywrite,myadd .text mywrite: pushl %ebp movl %esp, %ebp pushl %ebx movl 8(%ebp),%ebx movl 12(%ebp),%ecx movl 16(%ebp),%edx movl $SYSWRITE,%eax int $0x80 popl %ebx movl %ebp,%esp; popl %ebp ret myadd: pushl %ebp movl %esp,%ebp movl 8(%ebp),eax movl 12(%ebp),edx xorl %ecx,%ecx addl %eax,%edx jo 1f movl 16(%ebp),%eax movl %edx,(%eax)
incl %ecx
1: movl %ecx,%eax
movl %ebp,%esp
popl %ebp ret
caller.c
#include<stdio.h>
#include<string.h> int main() { char buf[1024]; int a,b,res; char *mystr="Calculating...\n"; char *emsg = "Error in adding \n"; a=5;b=10; mywrite(1,mystr,strlen(mystr)); if(myadd(a,b,&res)) { sprintf(buf,"The result is %d\n",res); mywrite(1,buf,strlen(buf)); } else{ mywrite(1,emsg,strlen(emsg)); } return 0;
};
|