inline关键字

本文介绍了GCC和C99标准中inline使用上的不同之处。inline属性在使用的时候,要注意以下两点:
  1. inline关键字在GCC参考文档中仅有对其使用在函数定义(Definition)上的描述,而没有提到其是否能用于函数声明(Declare)。

    从inline的作用来看,其放置于函数声明中应当也是毫无作用的:inline只会影响函数在translation unit(可以简单理解为C源码文件)内的编译行为,只要超出了这个范围inline属性就没有任何作用了。所以inline关键字不应该出现在函数声明中,没有任何作用不说,有时还可能造成编译错误(在包含了sys/compiler.h的情况下,声明中出现inline关键字的部分通常无法编译通过);

  2. inline关键字仅仅是建议编译器做内联展开处理,而不是强制。在gcc编译器中,如果编译优化设置为-O0,即使是inline函数也不会被内联展开,除非设置了强制内联(__attribute__((always_inline)))属性。

1. GCC的inline

gcc对C语言的inline做了自己的扩展,其行为与C99标准中的inline有较大的不同。

1.1. static inline

GCC的static inline定义很容易理解:你可以把它认为是一个static的函数,加上了inline的属性。这个函数大部分表现和普通的static函数一样,只不过在调用这种函数的时候,gcc会在其调用处将其汇编码展开编译而不为这个函数生成独立的汇编码。除了以下几种情况外:

  • 函数的地址被使用的时候。如通过函数指针对函数进行了间接调用。这种情况下就不得不为static inline函数生成独立的汇编码,否则它没有自己的地址。

  • 其他一些无法展开的情况,比如函数本身有递归调用自身的行为等。

static inline函数和static函数一样,其定义的范围是local的,即可以在程序内有多个同名的定义(只要不位于同一个文件内即可)。

 注意

gcc的static inline的表现行为和C99标准的static inline是一致的。所以这种定义可以放心使用而没有兼容性问题。

要点:

  • gcc的static inline相对于static函数来说只是在调用时建议编译器进行内联展开;

  • gcc不会特意为static inline函数生成独立的汇编码,除非出现了必须生成不可的情况(如通过函数指针调用和递归调用);

  • gcc的static inline函数仅能作用于文件范围内。

1.2. inline

相对于C99的inline来说,GCC的inline更容易理解:可以认为它是一个普通全局函数加上了inline的属性。即在其定义所在文件内,它的表现和static inline一致:在能展开的时候会被内联展开编译。但是为了能够在文件外调用它,gcc一定会为它生成一份独立的汇编码,以便在外部进行调用。即从文件外部看来,它和一个普通的extern的函数无异。举个例子:

foo.c:/* 这里定义了一个inline的函数foo() */
inline foo() {...;   <- 编译器会像非inline函数一样为foo()生成独立的汇编码
}void func1() {foo(); <- 同文件内foo()可能被编译器内联展开编译而不是直接call上面生成的汇编码
}

而在另一个文件里调用foo()的时候,则直接call的是上面文件内生成的汇编码:

bar.c:extern foo(); <- 声明foo(),注意不能在声明内带inline关键字void func2() {foo();    <- 这里就是直接call在foo.c内为foo()函数生成的汇编码了
}
 重要

虽然gcc的inline函数的行为很好理解,但是它和C99的inline是有很大差别的。请注意看后面对C99 inline的描述(第 2.2 节 “inline”),以及如何以兼顾GCC和C99的方式使用inline函数。

要点:

  • gcc的inline函数相对于普通extern函数来说只是在同一个文件内调用时建议编译器进行内联展开;

  • gcc一定会为inline函数生成一份独立的汇编码,以便其在本文件之外被调用。在别的文件内看来,这个inline函数和普通的extern函数无异;

  • gcc的inline函数是全局性的:在文件内可以作为一个内联函数被内联展开,而在文件外可以调用它。

1.3. extern inline

GCC的static inline和inline都很好理解:看起来都像是对普通函数添加了可内联的属性。但是这个extern inline就千万不能想当然地理解成就是一个extern的函数+inline属性了。实际上gcc的extern inline十分古怪:一个extern inline的函数只会被内联进去,而绝对不会生成独立的汇编码!即使是通过指针应用或者是递归调用也不会让编译器为它生成汇编码,在这种时候对此函数的调用会被处理成一个外部引用。另外,extern inline的函数允许和外部函数重名,即在存在一个外部定义的全局库函数的情况下,再定义一个同名的extern inline函数也是合法的。以下用例子具体说明一下extern inline的特点:

foo.c:extern inline
int foo(int a)
{return (-a);
}void func1()
{...;a = foo(a);   ①p_foo = foo;  ②b = p_foo(b); ③
}

在这个文件内,gcc不会生成foo函数的汇编码。在func1中的调用点①,编译器会将上面定义的foo函数在这里内联展开编译,其表现类似于普通inline函数。因为这样的调用是能够进行内联处理的。而在②处,引用了foo函数的地址。但是注意:编译器是绝对不会为extern inline函数生成独立汇编码的!所以在这种非要个函数地址不可的情况下,编译器不得不将其处理为外部引用,在链接的时候链接到外部的foo函数去(填写外部函数的地址)。这时如果外部没有再定义全局的foo函数的话就会在链接时产生foo函数未定义的错误。

假设在另一个文件里面也定义了一个全局函数foo:

foo2.c:int foo(int a)
{return (a);
}

那么在上面那个例子里面,后面一个对foo函数地址的引用就会在链接时被指到这个foo2.c中定义的foo函数去。也就是说:①调用foo函数的结果是a=-a,因为其内联了foo.c内的foo函数;而③调用的结果则是b=b,因为其实际上调用的是foo2.c里面的foo函数!

extern inline的用法很奇怪也很少见,但是还是有其实用价值的。第一:它可以表现得像宏一样,可以在文件内用extern inline版本的定义取代外部定义的库函数(前提是文件内对其的调用不能出现无法内联的情况);第二:它可以让一个库函数在能够被内联的时候尽量被内联使用。举个例子:

在一个库函数的c文件内,定义一个普通版本的库函数libfunc:

lib.c:void libfunc()
{...;
}

然后再在其头文件内,定义(注意不是声明!)一个实现相同的exterin inline的版本:

lib.h:extern inline libfunc()
{...;
}

那么在别的文件要使用这个库函数的时候,只要include了lib.h,在能内联展开的地方,编译器都会使用头文件内extern inline的版本来展开。而在无法展开的时候(函数指针引用等情况),编译器就会引用lib.c中的那个独立编译的普通版本。即看起来似乎是个可以在外部被内联的函数一样,所以这应该是gcc的extern inline意义的由来。

但是注意这样的使用是有代价的:c文件中的全局函数的实现必须和头文件内extern inline版本的实现完全相同。否则就会出现前面所举例子中直接内联和间接调用时函数表现不一致的问题。

 重要

gcc的extern inline函数的用法相当奇怪,使用的范围也非常狭窄:几乎没有什么情况会需要用它。

在C99中,也没有关于extern inline这样的描述,所以不建议大家使用extern inline,除非你明确理解了这种用法的意义并且有充足的理由使用它!

要点:

  • gcc绝对不会为extern inline的函数生成独立汇编码

  • extern inline函数允许和全局函数重名,可以在文件范围内替代外部定义的全局函数

  • extern inline函数的应用范围十分狭窄,而且行为比较奇怪,不建议使用

2. C99的inline

以下主要描述C99的inline与Gcc不同的部分。对于相同的部分请参考GCC inline的说明。

2.1. static inline

同GCC的static inline(第 1.1 节 “static inline”)。

2.2. inline

C99的inline的使用相当令人费解。当一个定义为inline的函数没有被声明为extern的时候,其表现有点类似于gcc中extern inline那样,即如果一个inline函数在文件范围内没有被声明为extern的话,这个函数在文件内的表现就和gcc的extern inline相似:在本文件内调用时允许编译器使用本文件内定义的这个内联版本,但同时也允许外部存在同名的全局函数。只是比较奇怪的是C99居然没有指定编译器是否必须在本文件内使用这个inline的版本而是让编译器厂家自己来决定,相当模糊的定义。

如果在文件内把这个inline函数声明为extern,则这个inline函数的表现就和gcc的inline一致了:这个函数即成为一个“external definition”(可以简单理解为全局函数):可以在外部被调用,并且在程序内仅能存在一个这样名字的定义。

下面举例说明C99中inline的特性:

inline double fahr(double t)
{return (9.0 * t) / 5.0 + 32.0;
}inline double cels(double t)
{return (5.0 * (t - 32.0)) / 9.0;
}extern double fahr(double);   ①double convert(int is_fahr, double temp)
{return is_fahr ? cels(temp) : fahr(temp);   ②
}

在上面这个例子里,函数fahr是个全局函数:因为在①处将fahr声明为extern,因此在②处调用fahr的时候使用的一定是这个文件内所定义的版本(只不过编译器可以将这里的调用进行内联展开)。在文件外部也可以调用这个函数(说明像gcc的inline一样,编译器在这种情况下会为fahr生成独立的汇编码)。

而cels函数因为没有在文件范围内被声明为extern,因此它就是前面所说的“inline definition”,这时候它实际上仅能作用于本文件范围(就像一个static的函数一样),外部也可能存在一个名字也为cels的同名全局函数。在②处调用cels的时候编译器可能选择用本文件内的inline版本,也有可能跑去调用外部定义的cels函数(C99没有规定此时的行为,不过编译器肯定都会尽量使用文件内定义的inline版本,要不然inline函数就没有存在的意义了)。从这里的表现上看C99中未被声明为extern的inline函数已经和gcc的extern inline十分相似了:本文件内的inline函数可以作为外部库函数的替代。

 重要

C99标准中的inline函数行为定义的比较模糊,并且inline函数有没有在文件范围内被声明为extern的其表现有本质不同。如果和gcc的inline函数比较的话,一个被声明为extern的inline函数基本等价于GCC的普通inline函数;而一个没有被声明为extern的inline函数基本等价于GCC的extern inline函数。

因为C99的inline函数如此古怪,所以在使用的时候,建议为所有的inline函数都在头文件中创建extern的声明:

foo.h:extern foo();

而在定义inline函数的c文件内include这个头文件:

foo.c:#include "foo.h"inline void foo()
{...;
}

这样无论是用gcc的inline规则还是C99的,都能得到完全相同的结果:foo函数会在foo.c文件内被内联使用,而在外部又可以像普通全局函数一样直接调用。

 

2.3. extern inline

C99没有见到extern inline的用法。

 

转载于:https://www.cnblogs.com/chengxuyuancc/articles/2999397.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/259872.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springmvc 组合注解

组合注解的意思就是一个注解中包含多个注解。在springmvc 的RestController中&#xff0c;你就可发现. Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Controller ResponseBody public interface RestController {/*** The value may indicate a su…

人才管理是什么意思_上海托管仓库外包仓库管理什么意思

上海托管仓库外包仓库管理什么意思上海仓库托管外包。好的上海仓库托管是预估好自己的货物总计有多少个方。车子的体积有多少&#xff0c;然后估算出总计需要多少车需要多少钱&#xff0c;需要怎么装车、卸货码放方式是什么样的&#xff0c;算出总的费用然后包干给搬家公司。这…

window server 安装与卸载

安装window server 程序:C:\Windows\Microsoft.NET\Framework\v2.0.50727\installutil DataUpdateService.exe net start LuceneServer 卸载window server 程序:net stop LuceneServer C:\Windows\Microsoft.NET\Framework\v2.0.50727\installutil /U DataUpdateService.exe …

Makefile学习(二)[第二版]

复杂实例#示例1:在上一个示例的基础上再增加一个可执行文件03test[修改之处已标红].PHONY: clean all CC gcc CFLAGS -Wall -gBIN 01test 02test 03testSOURCES $(BIN:.c)OBJECTS $(BIN:.o)all: $(BIN)01test: 01test.o02test: 02test.o03test: 03test.o.c.o:$(CC) $(CFLA…

计算机网络asp视频教程,轻轻松松学编程!ASP互动视频教程

从2006年5月18日开始&#xff0c;PConline将与FIF联合推出国内网上第一部互动视频教程&#xff1a;《ASP互动视频教程》。它预示着一个全新的自助学习时代的到来。尽管相较于传统的图文教程&#xff0c;以前的多媒体视频课件优点非常明显&#xff0c;但它仍然存在交互性差的缺点…

Oracle查询和解锁表

一些ORACLE中的进程被杀掉后&#xff0c;状态被置为"killed"&#xff0c;但是锁定的资源很长时间不释放&#xff0c;有时实在没办法&#xff0c;只好重启数据库。现在提供一种方法解决这种问题&#xff0c;那就是在ORACLE中杀不掉的&#xff0c;在OS一级再杀。1.下面…

三维家可以导入别人的方案吗_广州深圳天津形位公差检测三维缺陷检测服务

形位公差检测三维缺陷检测服务标签&#xff1a;形位公差检测 三维缺陷检测服务 三维缺陷检测铸造工艺是一种经济实惠的毛坯成形方式&#xff0c;对于一些形状复杂的零件更能显示出它的经济性。比如汽车发动机的缸体和缸盖&#xff0c;船舶螺旋桨以及精致的艺术品等。本期案例的…

计算机缺失esul.dll,SceneUI.ES.dll

我该如何安装从金山毒霸下载的DLL文件&#xff1f;一&#xff1a;1、从金山毒霸下载压缩文件。2、将DLL文件解压到电脑上的某个地方。3、把该文件跟要求使用它的程序放在同一路径上。注意32位程序需要使用32位的DLL文件&#xff0c;64位程序需要使用64位的DLL文件。否则会出现0…

android+ import R错误

import android.R; 在开发过程中有时候eclipse自动导入的包&#xff0c;该包有时候会导致一些奇怪的错误&#xff0c;再次出现该问题的时候&#xff0c;把import android.R;删掉。setContentView(R.layout.secondactivity); //不删掉 import android R&#xff1b;就会报错转载…

一、后台首页index.php【dedecms后台源码分析】

后台目录dede目录的所有问价的源码分析 使用的dedecms的版本5.7&#xff08;2012-04-01更新&#xff09; 后台登陆之后的首页分析dede/index.php <?php /*** 管理后台首页** version $Id: index.php 1 11:06 2010年7月13日Z tianya $* package DedeCMS.Admin…

transferto 文件不存在_文件上传时,MultipartFile.transferTo() 方法报 FileNotFoundException...

Spring Upload File 报错FileNotFoundException环境&#xff1a;Springboot2.0.4JDK1.8内嵌 Apache Tomcat/8.5.321、前端代码前端上传网页表单&#xff0c;enctype 和 input 的typefile 即可&#xff0c;使用单文件上传举例&#xff1a;图片2、后端代码RestControllerRequestM…

对“粘连”footer布局的思考和总结

经典的"粘连"footer布局 参考文章链接在文章末尾&#xff0c;简单的语言总结如下&#xff1a; 经典的“粘连”footer布局就是。我们有一块内容<main>。当<main>的高度足够长的时候&#xff0c;紧跟在<main>后面的元素<footer>会跟在<main…

计算机函数公式一等奖怎么算,信息技术应用 用计算机画函数图象教案设计(一等奖)...

卫鹏展地区&#xff1a; 湖北省 - 黄冈市 - 英山县学校&#xff1a;英山县金铺镇金铺中学 共1课时信息技术应用 用计算机画函数图象">信息技术应用 用计算机画… 初中数学 人教2011课标版 1教学目标1&#xff0e;结合具体情境理解一次函数的意义&#xff0c;能结…

这样去写你的 HTML

昨天在 twitter 上说&#xff0c;怎么忍心把页面写得这么难用&#xff1f;是的&#xff0c;这个世界还有一群人等着我们创建出来的东西&#xff0c;可以让他们的生活能过得更容易呢。比如那些需要读屏软件的用户。作为一个前端&#xff0c;我们又怎么会忍心呢。之前就一直想写这…

iframe懒加载_前端常见问题

原地址&#xff1a;https://blog.csdn.net/Mr_JavaScript/article/details/843110681. flex布局&#xff1a;又叫做弹性布局任何一个容器都可以指定flex布局&#xff0c;如display:flex 或 display:inline-flex注意&#xff1a;设置了flex布局以后&#xff0c;子元素的float&am…

手机运行服务器无响应,《最强蜗牛》服务器无响应怎么办 服务器无响应解决方法...

导读最强蜗牛服务器无响应怎么办&#xff1f;本作在今日迎来了正式的公测&#xff0c;这会导致大批量的玩家同时涌入进来&#xff0c;而服务器也因此而遭受到了非常大的符合&#xff0c;所以会导致后续加入进来的玩家出现服务器无响应进不去的现象。下面就为大家带...最强蜗牛服…

Android 开源框架Universal-Image-Loader学习

Android 开源框架Universal-Image-Loader完全解析&#xff08;一&#xff09;--- 基本介绍及使用 Android 开源框架Universal-Image-Loader完全解析&#xff08;二&#xff09;--- 图片缓存策略详解 Android 开源框架Universal-Image-Loader完全解析&#xff08;三&#xff09;…

自己动手写操作系统--个人实践

近期開始看于渊的《自己动手写操作系统》这本书&#xff0c;刚開始看就发现做系统的引导盘居然是软盘&#xff01;心里那个汗啊&#xff01; 如今都是U盘了&#xff0c;谁还用软盘。于是考虑用U盘。 于是開始下面步骤&#xff1a; 1、既然书上说给先要把软盘做引导盘&#xff0…

蔻驰和mk哪个更大牌_mk和coach哪个好?mk和coach包包是一个档次吗?

说到包包&#xff0c;mk和coach可谓是轻奢界的两大巨头了。因此&#xff0c;两个品牌的包包不可避免的会被经常拿来作比较。那么&#xff0c;从各个角度来讲mk和coach那款包包更好呢&#xff1f;这两款包包又有哪些本质上的区别呢&#xff1f;mk和coach哪个好价格对比大致上&am…

Linux 配置 swap 区

Linux 配置 swap 区 很多时候我们需要配置 swap 主要的原因是物理内存太贵了&#xff0c; 服务器也是一样&#xff0c; 当内存不够用时&#xff0c; 系统会卡死&#xff0c; 因此我们宁愿牺牲一点性能也要让系统正常运行。 当然&#xff0c; 在系统物理内存足够的条件下&#x…