今天再看一段代码的时候看到了extern inline定义,当时没感觉,回过神来后觉得好像哪里不对。自己写代码经常是static inline,突然看到这个在自己看来的新东西而对大牛来说只是司空见惯的小菜时,我该哭还是笑嘞!!(还是太水的原因。)
我们还是先来说说static inline,我们都知道对于static声明,当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性,加了static,就会对其它源文件隐藏,并保持变量内容的持久。当然相信大家对inline也都不陌生,我们都知道函数调用需要时间和空间开销,调用函数实际上将程序执行流程转移到被调函数中,被调函数的代码执行完后,再返回到调用的地方。这种调用操作要求调用前保护好现场并记忆执行的地址,返回后恢复现场,并按原来保存的地址继续执行。对于较长的函数这种开销可以忽略不计,但对于一些函数体代码很短,又被频繁调用的函数,就不能忽视这种开销。inline正是为了解决这个问题,提高程序的运行效率。
GCC的static inline定义很容易理解:你可以把它认为是一个static的函数,加上了inline的属性。这个函数大部分表现和普通的static函数一样,只不过在调用这种函数的时候,gcc会在其调用处将其汇编码展开编译而不为这个函数生成独立的汇编码。
除了以下几种情况外:
1.函数的地址被使用的时候。如通过函数指针对函数进行了间接调用。这种情况下就不得不为static inline函数生成独立的汇编码,否则它没有自己的地址。
2.其他一些无法展开的情况,比如函数本身有递归调用自身的行为等。
static inline函数和static函数一样,其定义的范围是local的,即可以在程序内有多个同名的定义(只要不位于同一个文件内即可)。 当然gcc的static inline的表现行为和C99标准的static inline是一致的。所以这种定义可以放心使用而没有兼容性问题。
然而对于extern inline 来说,绝逼不能简单理解为一个extern属性的函数+inline属性。首先看看c99定义的吧:" If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition"
我擦,写的太复杂,绕口了,(当然借助google翻一下)简单来讲static inline与extern inline的区别就在于编译单元之外可见性的问题。简单的函数如果inline那就丧失了外部可见性。C为了保证inline并对外部的可见性就产生了extern inline这个东西。
具体地说static inline 函数就那一个,无外部可见性,所有的编译单元中用的是一个东西,而extern inline则是说虽然此函数只有一个声明,但既有inline版本也有非inline版本,在同一编译单元中就可以用inline版本,不在的话就可以用非inline版本,就是和其它函数一样。并且一个extern inline的函数只会被inline进去,而绝对不会生成独立的汇编码!即使是通过指针应用或者是递归调用也不会让编译器为它生成汇编码,在这种时候对此函数的调用会被处理成一个外部引用。除此之外,对于extern inline的函数还允许和外部函数重名。
比如:
File1.c:
extern inline int function_test(int val){
return (val+1);
}
void function_test_one() {
a = function_test(a);
p_ function_test = function_test;
b = p_ function_test(b)
}
在这个文件中,gcc不会生成functio_test的汇编代码。而在funcntion_test_one中的调用点a=fcuntion_test(a) ,编译器会将上面定义的 function_test函数在这里内联展开编译,其行为类似于普通inline函数。因为这样的调用是能够进行内联处理的。而 执行p_ function_test = function_test; 的时候引用了fcuntion_test函数的地址。但是注意:编译器是绝对不会为extern inline函数生成独立汇编码,所以在这种非要个函数地址不可的情况下,编译器不得不将其处理为外部引用,在链接的时候链接到外部的function_test函数去(填写外部函数的地址)。这时如果外部没有再定义全局的function_test函数的话就会在链接时产function_test函数未定义的错误。
如果在另一个文件中也存在一个全局函数function_test():
File2.c:
int function_test(int val){
return val-1;
}
那么在file1.c里面,后面一个对function_test函数地址的引用就会在链接时被指到file2.c中定义的function_test函数去。也就是说代码执行a = function_test(a);调用function_test函数的结果是a=a+1,因为其内联第一个文件内的function_test函数;而 b = p_ function_test(b)调用的结果则是b=b-1,因为其实际上调用的是file2.c里面的function_test函数.
extern inline这种用法其实很奇怪,关于作用当然不是我这菜菜能想到的,google如下:
1.它可以表现得像宏一样,可以在文件内用extern inline版本的定义取代外部定义的库函数(前提是文件内对其的调用不能出现无法内联的情况)
2.可以让一个库函数在能够被内联的时候尽量被内联使用。
需要注意的是,extern inline 只需要定义一次,不同的地方看到的版本不同,有的地方是inline的版本,而有的地方看到的只是个外部引用。而仅用 inline 的话,就变成了要定义两次,带 inline 关键字的这个定义就是inline版本,在这个编译单元中都用这个版本,而在外部,还是使用普通的非inline定义。即带 inline的定义遮盖住了外部的定义。
GNU89中说:"extern inline" will not generate an out-of-line version,but might call one (which you therefore must define in some other compilation unit.The one-definition rule applies, though; the out-of-line version must have the same code as the inline offered here, in case the compiler calls that instead.
翻译过来就是:"extern inline":不会生成一个非inline的版本,但是可以调用一个非inline版本(必须在其它编译单元中定义它)。只能有一个定义的规则当然也适用,非inline版本和inline版本的代码必须是一样的。
擦,又十二点多了。