关于 extern inline
2014-04-16 13:50
323 查看
摘自:http://wangcong.org/blog/archives/2021
大家一定对C语言 inline 关键字不陌生,甚至经常用到 static inline 的函数。可能感到陌生的是 extern inline。C11 标准在6.7.4 p7 中对此进行了描述,不过比较生涩难读。简单地讲,static line 和 extern inline 的区别,从名字上也能看得出来,就是编译单元之外可见性的问题。把一个普通函数给内联掉之后,一个显著的区别就是外部可见性没了,怎么能同时既能内联又能保留外部的可见性呢?为了解决这个问题就有了extern inline。
static inline 是说这个函数就这么一个,而且没有外部可见性,在所有的编译单元中大家必须共用这么一个定义,这也是为什么 static inline 通常要放到头文件中的原因;而 extern inline 就不一样了,它是说这个函数虽然只有一个定义,但是既有内联的版本,也有非内联的版本,如果你能看得到它的定义。即在同一个编译单元中,那么你就可以用它的内联版本;看不到的话,你就可以用非内联版本,即和其它普通函数一模一样。
而如果既不带 static 也不带 extern 的话,含义又不同了:前面的 extern inline 只需要定义一次,只是不同的地方看到的版本不同,有的地方看到的是内联的版本,而有的地方看到的只是个 external reference。而仅用 inline 的话,就变成了要定义两次,带 inline 关键字的这个定义就是内联版本,在这个编译单元中都用这个版本,而在外部,还是使用普通的非内联定义。换句话说,带 inline 的定义遮盖住了外部的定义。既然有两个不同的版本,那么也就需要保持这两个版本的定义相同,否则就会有问题。
以上是标准C的定义,而 GNU89 的定义就不同了,基本上是把"extern inline"和"inline"的含义给交换了,“static line” 的含义都是相同的。看下面的标准C的例子:
PLAIN TEXT
C:
//a.c
//C99
extern inline int max(int a, int b)
{
return a> b ? a : b;
}
int a = 10;
int b = 20;
int foo(void)
{
return max(a, b);
}
extern int bar(void);
int main(void)
{
return foo() + bar();
}
//b.c
//C99
extern int max(int a, int b);
int bar(void)
{
int a = 10;
int b = 20;
return max(a, b);
}
我们这么编译:
% gcc -std=c99 -O2 -Wall -W a.c b.c -o c
而我们如果把它编译成 GNU89 的话,就会报错:
% gcc -std=gnu89 -O2 -Wall -W a.c b.c -o c
/tmp/ccAJTzwY.o: In function `bar':
b.c:(.text+0xb): undefined reference to `max'
collect2: ld returned 1 exit status
很明显,因为 GNU89 中的 extern inline 需要两个定义,明显我们少了一个。修改后的代码如下:
PLAIN TEXT
C:
//a.c
//GNU89
extern inline int max(int a, int b)
{
return a> b ? a : b;
}
int a = 10;
int b = 20;
int foo(void)
{
return max(a, b);
}
extern int bar(void);
int main(void)
{
return foo() + bar();
}
// b.c
//GNU89
int max(int a, int b)
{
return a> b ? a : b;
}
int bar(void)
{
int a = 10;
int b = 20;
return max(a, b);
}
glibc 中就用到了 GNU89 的extern inline 特性,在 ctype.h 中,tolower()/toupper() 的定义是:
PLAIN TEXT
C:
# ifdef __USE_EXTERN_INLINES
__extern_inline int
__NTH (tolower (int __c))
{
return __c>= -128 && __c
<256 ? (*__ctype_tolower_loc ())[__c] : __c;
}
__extern_inline int
__NTH (toupper (int __c))
{
return __c>= -128 && __c
<256 ? (*__ctype_toupper_loc ())[__c] : __c;
}
# endif
而在 ctype/ctype.c 中却是:
PLAIN TEXT
C:
#define __ctype_tolower \
((int32_t *) _NL_CURRENT (LC_CTYPE,
_NL_CTYPE_TOLOWER) + 128)
#define __ctype_toupper \
((int32_t *) _NL_CURRENT (LC_CTYPE,
_NL_CTYPE_TOUPPER) + 128)
int
tolower (int c)
{
return c>= -128 && c
<256 ? __ctype_tolower[c] : c;
}
int
toupper (int c)
{
return c>= -128 && c
<256 ? __ctype_toupper[c] : c;
}
顺便说一句,gcc 提供了-fgnu89-inline 和 -fno-gnu89-inline 选项可在编译时控制上述 inline 的行为。
Stackoverflow 上有人做了一个很好的总结,我翻译了一下:
GNU89:
"inline": 函数可能会被内联掉,非内联的版本总是会生成,而且外部可见,因此内联的定义在本编译单元中只能有一次,其它地方看到的是非内联版本。
"static inline":不会生成外部可见的非内联版本,可能会生成一个 static 的非内联版本。它当然可以被定义多次,因为外部看不到它。
"extern inline":不会生成一个非内联的版本,但是可以调用一个非内联版本(因此你必须在其它编译单元中定义它)。只能有一个定义的规则当然也适用,非内联版本和内联版本的代码必须是一样的。
C99 (or GNU99):
"inline":和 GNU89 的 "extern inline" 一样,没有外部可见的函数生成,但是外部可见的函数必须存在,因为有可能会用到它。
"extern inline":和 GNU89 的 "inline" 一样, 会生成外部可见的代码,最多一个编译单元可以使用它。
"static inline":和 GNU89 的 "static inline" 一样,这是唯一一个在 GNU89 和 C99之间可移植的。
最后,我们可以看大神 Linus Torvalds 如何描述 extern inline:
"static inline" means "we have to have this function, if you use it, but don't inline it, then make a static version of it in this compilation unit". "extern inline" means "I actually _have_ an extern for this function, but if you want to inline it, here's
the inline-version".
参考资料:
http://www.greenend.org.uk/rjk/tech/inline.html http://gcc.gnu.org/ml/gcc/2006-11/msg00006.html http://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99 http://www.cnblogs.com/cnmaizi/archive/2011/01/19/1939686.html
大家一定对C语言 inline 关键字不陌生,甚至经常用到 static inline 的函数。可能感到陌生的是 extern inline。C11 标准在6.7.4 p7 中对此进行了描述,不过比较生涩难读。简单地讲,static line 和 extern inline 的区别,从名字上也能看得出来,就是编译单元之外可见性的问题。把一个普通函数给内联掉之后,一个显著的区别就是外部可见性没了,怎么能同时既能内联又能保留外部的可见性呢?为了解决这个问题就有了extern inline。
static inline 是说这个函数就这么一个,而且没有外部可见性,在所有的编译单元中大家必须共用这么一个定义,这也是为什么 static inline 通常要放到头文件中的原因;而 extern inline 就不一样了,它是说这个函数虽然只有一个定义,但是既有内联的版本,也有非内联的版本,如果你能看得到它的定义。即在同一个编译单元中,那么你就可以用它的内联版本;看不到的话,你就可以用非内联版本,即和其它普通函数一模一样。
而如果既不带 static 也不带 extern 的话,含义又不同了:前面的 extern inline 只需要定义一次,只是不同的地方看到的版本不同,有的地方看到的是内联的版本,而有的地方看到的只是个 external reference。而仅用 inline 的话,就变成了要定义两次,带 inline 关键字的这个定义就是内联版本,在这个编译单元中都用这个版本,而在外部,还是使用普通的非内联定义。换句话说,带 inline 的定义遮盖住了外部的定义。既然有两个不同的版本,那么也就需要保持这两个版本的定义相同,否则就会有问题。
以上是标准C的定义,而 GNU89 的定义就不同了,基本上是把"extern inline"和"inline"的含义给交换了,“static line” 的含义都是相同的。看下面的标准C的例子:
PLAIN TEXT
C:
//a.c
//C99
extern inline int max(int a, int b)
{
return a> b ? a : b;
}
int a = 10;
int b = 20;
int foo(void)
{
return max(a, b);
}
extern int bar(void);
int main(void)
{
return foo() + bar();
}
//b.c
//C99
extern int max(int a, int b);
int bar(void)
{
int a = 10;
int b = 20;
return max(a, b);
}
我们这么编译:
% gcc -std=c99 -O2 -Wall -W a.c b.c -o c
而我们如果把它编译成 GNU89 的话,就会报错:
% gcc -std=gnu89 -O2 -Wall -W a.c b.c -o c
/tmp/ccAJTzwY.o: In function `bar':
b.c:(.text+0xb): undefined reference to `max'
collect2: ld returned 1 exit status
很明显,因为 GNU89 中的 extern inline 需要两个定义,明显我们少了一个。修改后的代码如下:
PLAIN TEXT
C:
//a.c
//GNU89
extern inline int max(int a, int b)
{
return a> b ? a : b;
}
int a = 10;
int b = 20;
int foo(void)
{
return max(a, b);
}
extern int bar(void);
int main(void)
{
return foo() + bar();
}
// b.c
//GNU89
int max(int a, int b)
{
return a> b ? a : b;
}
int bar(void)
{
int a = 10;
int b = 20;
return max(a, b);
}
glibc 中就用到了 GNU89 的extern inline 特性,在 ctype.h 中,tolower()/toupper() 的定义是:
PLAIN TEXT
C:
# ifdef __USE_EXTERN_INLINES
__extern_inline int
__NTH (tolower (int __c))
{
return __c>= -128 && __c
<256 ? (*__ctype_tolower_loc ())[__c] : __c;
}
__extern_inline int
__NTH (toupper (int __c))
{
return __c>= -128 && __c
<256 ? (*__ctype_toupper_loc ())[__c] : __c;
}
# endif
而在 ctype/ctype.c 中却是:
PLAIN TEXT
C:
#define __ctype_tolower \
((int32_t *) _NL_CURRENT (LC_CTYPE,
_NL_CTYPE_TOLOWER) + 128)
#define __ctype_toupper \
((int32_t *) _NL_CURRENT (LC_CTYPE,
_NL_CTYPE_TOUPPER) + 128)
int
tolower (int c)
{
return c>= -128 && c
<256 ? __ctype_tolower[c] : c;
}
int
toupper (int c)
{
return c>= -128 && c
<256 ? __ctype_toupper[c] : c;
}
顺便说一句,gcc 提供了-fgnu89-inline 和 -fno-gnu89-inline 选项可在编译时控制上述 inline 的行为。
Stackoverflow 上有人做了一个很好的总结,我翻译了一下:
GNU89:
"inline": 函数可能会被内联掉,非内联的版本总是会生成,而且外部可见,因此内联的定义在本编译单元中只能有一次,其它地方看到的是非内联版本。
"static inline":不会生成外部可见的非内联版本,可能会生成一个 static 的非内联版本。它当然可以被定义多次,因为外部看不到它。
"extern inline":不会生成一个非内联的版本,但是可以调用一个非内联版本(因此你必须在其它编译单元中定义它)。只能有一个定义的规则当然也适用,非内联版本和内联版本的代码必须是一样的。
C99 (or GNU99):
"inline":和 GNU89 的 "extern inline" 一样,没有外部可见的函数生成,但是外部可见的函数必须存在,因为有可能会用到它。
"extern inline":和 GNU89 的 "inline" 一样, 会生成外部可见的代码,最多一个编译单元可以使用它。
"static inline":和 GNU89 的 "static inline" 一样,这是唯一一个在 GNU89 和 C99之间可移植的。
最后,我们可以看大神 Linus Torvalds 如何描述 extern inline:
"static inline" means "we have to have this function, if you use it, but don't inline it, then make a static version of it in this compilation unit". "extern inline" means "I actually _have_ an extern for this function, but if you want to inline it, here's
the inline-version".
参考资料:
http://www.greenend.org.uk/rjk/tech/inline.html http://gcc.gnu.org/ml/gcc/2006-11/msg00006.html http://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99 http://www.cnblogs.com/cnmaizi/archive/2011/01/19/1939686.html
相关文章推荐
- 关于 extern inline
- 关于inline/static inline/extern inline
- 关于 extern inline
- 关于: Error in mounted hook: "Error: Component series.line not exists. Load it first
- Multiple definition of... extern inline
- 关于display: inline-block 默认空白间距的问题
- 关于QT中奇数个汉字出现newline in constant的错误
- 关于python无法显示中文的问题:SyntaxError: Non-ASCII character '\xe4' in file test.py on line 3, but no encoding
- 关于display:inline-block 的详解
- 关于python无法显示中文的问题:SyntaxError: Non-ASCII character '\xe4' in file test.py on line 3, but no encoding
- 关于ie7下display:inline-block;不支持的解决方案
- static inline 和 extern inline 的含义
- 关于ie7下display:inline-block;不支持的解决方案
- 关于 display: inline 、block 、inline-block (一)
- 关于display:inline-block
- 关于display:inline-block,你有可能不知道的东西
- eclipse关于Unable to install breakpoint in due to missing line number
- 关于div的设置:display:inline-block出现div对不齐的情况处理方法·
- 关于display:inline的理解
- 关于 display: inline 、block 、inline-block (二)