inline关键字(c语言32个关键字及其意义是什么)
本文目录
c语言32个关键字及其意义是什么
类型关键字(共12个):
int 整形,float 单精度浮点,double 双精度浮点,long 长型,short 短型,signed 有符号类型,unsigned 无符号类型,void 空型,enum 枚举类型,struct 结构体类型,union联合(共用体)类型,char 字符类型。
存储类型修饰符(共3个):
auto 自动类型,register CPU寄存器类型,static 静态类型。
函数调用约定(共3个):
__stdcall __fastcall __cdecl
预处理指令(共7个):
#include #pragma #define #if #ifdef #ifndef #endif
流程控制关键字(共12个):
if 条件判断,else 条件,for循环,switch 开关,do 循环,while 循环,retrun 函数返回,break 结束循环,continue 直接进入下一轮循环,default 开关的默认选项,case 开关的选项,goto 跳转。
其它关键字(共9个):
typedef 类型重定义,sizeof 查询类型占用字节数,const 常量,try异常,catch 异常捕获,inline 内联,public 共有成员,private 私有成员,protected 保护成员。(new delete认为是C++关键字)
不止这些,随着C语言不断发展,新的标准不断定制,越来越多的关键字被加入。
由于现在纯的C语言编译器已经很少,笔者只知道gcc这一款,大部分都是用C++的编译器去编译C语言代码,所以仅了解C语言关键字还是远远不够的。
之前的__barecall笔者笔误,实为naked call,但是这个像是Microsoft专用,如有误请在评论区注明,大家相互学习。我整理的可能也有疏漏,不保证完全。
C语言编程中宏定义的使用有什么技巧吗函数式宏定义为啥不直接定义成函数
谢邀。
谈到较大的C语言项目,就不得不提“宏定义”了,较大的项目都会用大量的宏定义来组织代码,随便找一个开源项目,打开它的源代码头文件,看看能发现多少宏定义。
题主可能用过 #define N 20 这种宏定义,看起来宏定义只不过是做个替换而已,其实里面有比较复杂的规则,有些规则可以成为实际的C语言程序开发中不错的技巧。
函数式宏定义
C语言程序中像 #define N 20 这种宏定义称为“变量式”宏定义,N 可以像变量一样使用,但是 N 属于常量表达式。实际上,还有一种可以像函数一样使用的宏定义,可称之为“函数式宏定义”,请看如下代码:
将 x = MIN(3&0x0f, 5&0x0f) 表达式展开,得:
可以看出,C语言程序中的函数式宏定义 MIN 可以像函数一样使用,两个实参被替换到宏定义形参 a 和 b 的位置了。应当注意,函数式宏定义和真正的函数是有区别的:
- 函数式宏定义的参数没有类型,预处理时不做参数类型检查,所以使用时要确保类型正确。
- 函数式宏定义本身不会被编译为函数,调用时就是直接把宏定义替换过来,而不是简单的几条传参和 call 指令,所以函数式宏定义编译生成的目标会比真正的函数大。
- 定义函数式宏定义要非常小心,如果 MIN 定义成 #define MIN(a, b) ( a《b? a:b ),则 x = MIN(3&0x0f, 5&0x0f) 展开就成了 x = ( 3&0x0f《5&0x0f?3&0x0f:5&0x0f ),运算符的优先级就错了,不会得出正确结果。读者思考一下,外层括号能否省略?
- 因为调用函数式宏定义就是简单替换,所以如果 MIN(i++, j++) 时,展开就是 ( (i++)《(j++)?(i++):(j++) ),i和j自加的次数是不确定的。如果是 MIN 真正的函数,则 i 和 j 确定是只自加一次。
宏定义的小技巧和注意事项
在 Linux 内核中,函数式宏定义通常使用 do{…}while(0) 包裹,请看下面的C语言代码示例:
为什么呢?请看下面这段C语言代码,就明白了:
如果没有使用 do{…}while(0) 包裹,把 do_something 展开后,变为:
printf(“i = %d\n”, i); 这句没有被包含在 if 判断语句里,而且 else 语句并没有与 if 配对,所以编译会报错。那能否在宏定义时,使用 {} 包裹呢?还是上面的例子,使用 {} 包裹展开后:
虽然 printf(“i = %d\n”, i); 这句被包含在 if 判断语句里了,但是 do_something(i); 最后的 “;”会被展开到 {} 后面,这样表示 if 的判断结束了,else 依然没有与 if 配对,还是会编译报错。
那 do_something(i); 后面的这个“;”不写不就行了吗?的确,不写就没有错误了,但是不写 “;”,看起来就不像函数调用了,对不?整个语句显得怪怪的,哪天顺手一加,就又错了。
有时候,C语言函数式宏定义可以做到函数难以实现的事
现在的C语言及其编译器支持了很多有趣的关键字,例如:
请看如下C语言代码:
编译器在编译时,会自动的把 “__FUNTION__” 和“__LINE__”替换为函数名和行号,这样就不用程序员逐个手动输入了,而且C语言代码的可移植性也更强。
为了更方便的输出当前位置,我们可以定义函数式宏定义:
打印出C语言语句位置是有用的,它能帮助我们在大型项目的复杂代码中快速的找到出错的函数,出错的行号。(类似于 __LINE__ 的关键字还有一些,留给题主自行查阅了)
location 是一个函数式宏定义,所以调用它,就相当于把C语言代码展开到调用位置,所以它可以打印出 test 中的位置,也可以打印出 main 中的位置。如果 location 是一个真正的函数,输出结果就不同了,请看:
原因相信题主自己能够分析出来,其实这就是C语言程序中,函数式宏定义的特殊之处。另外,因为调用函数式宏定义相当于把C语言代码展开,少了函数调用的开销,整个C语言程序的效率也会有所提升。
欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。
C语言中inline关键字有什么用书上说inline函数可以代替宏是什么意思
谢邀。
打开 Linux 内核源代码,会发现内核在定义C语言函数时,有很多都带有 “inline”关键字,请看下图,那么这个关键字有什么作用呢?
inline 关键字的作用
在C语言程序开发中,inline 一般用于定义函数,inline 函数也被称作“内联函数”,C99 和 GNU C 均支持内联函数。那么在C语言中,内联函数和普通函数有什么不同呢?其实,从 inline 这个名字就应该能看出一点它的性质了——内联函数会在它被调用的位置上展开,这一点表现的和 define 宏定义是非常相似的。
将被调用的函数代码展开,操作系统就无需再在为被调用函数做申请栈帧和回收栈帧的工作,而且,由于编译器会把被调用的函数代码和函数本身放在一起优化,所以也有进一步优化C语言代码,提升效率的可能。
每发生一次函数调用,操作系统就要在程序的栈空间申请一块内存区域(栈帧),供被调用函数使用,被调用函数执行完毕后,操作系统还要回收这些内存。
不过,天下没有免费的午餐,C语言程序要实现内联函数的上述特性是要付出一定的代价的。普通函数只需要编译出一份,就可以被所有其他函数调用,而内联函数没有严格意义上的“调用”,它只是将自身的代码展开到被调用处的,这么做无疑会使整个C语言代码变长,也就意味着占用更多的内存空间,以及更多的指令缓存。
显然,如果滥用内联函数,cpu 的指令缓存肯定是不够用的,这会导致 cpu 缓存命中率降低,反而可能会降低整个C语言程序的效率。因此,建议把那些对时间要求比较高,且C语言代码长度比较短的函数定义为内联函数。如果在C语言程序开发中的某个函数比较大,又会被反复调用,并且没有特别的时间限制,是不适合把它做成内联函数的。
在 Linux 内核中,内联函数常常使用 static 修饰,例如:
需要注意的是,内联函数必须在使用之前就定义好,否则编译器没法把这个函数展开。Linux 内核中经常像下面这样,将内联函数放在调用它的函数前面,请看C语言代码:
所以,Linux 内核常常把内联函数定义在头文件里,这样在其他C语言代码文件开头包含头文件时,能确保内联函数在文件的最开始,无需再写额外的声明语句。
这也解释了为什么 Linux 内核为何常常使用 static 修饰内联函数,因为可以避免函数的重复定义。
前文提到内联函数的表现有些像 define 宏定义,但是为了类型安全和易读性,应优先使用内联函数而不是复杂的宏。下面通过实例进一步分析 inline 内联函数的特性。
inline内联函数的“展开代码”是什么意思?
使用过 define 写 C语言代码的朋友应该都知道,编译器在编译 C语言代码时,会将 define 定义的宏展开,而不是像普通函数那样使用 call 指令调用,例如下面这段C语言代码:
使用 gcc -E 编译这段C语言代码,能够得到预处理后的代码如下,显然 define 定义的宏被展开了,请看:
使用 gcc -g 命令编译C语言代码,得到可执行文件,然后调用 objdump 命令查看汇编代码,得到如下结果:
从 f_add() 函数的汇编代码也可以看出,程序首先将 2 个参数赋值给寄存器,然后使用 call 指令调用 f_add() 函数。而宏定义 d_add() 就简单了,只有一行汇编代码,这种情况下,使用 define 宏定义显然效率更高。不过,宏定义没有参数的类型检查,使用起来不太安全,好在C语言还有 inline 函数,下面再定义一个 inline 函数,请看C语言代码如下:
在 main() 函数中使用 gcc -E 命令查看添加 inline 函数后的C语言代码预处理结果,如下:
可以看出,在预处理阶段,inline 函数并没有像 define 宏那样展开。现在使用 gcc -g 命令编译得到可执行文件,然后使用 objdump 查看汇编代码,如下:
从汇编代码可以看出,inline 函数似乎并没有起到作用,i_add() 函数和 f_add() 函数的表现并没有什么不同,继续往上查看,发现编译器也将 i_add() 函数的汇编代码生成了,这无疑是将 i_add() 函数当作普通函数使用了:
怎么回事?不是说 inline 函数的表现和 define 宏相似,会将函数代码展开吗?其实,inline 只是建议编译器这么做,编译器究竟会不会这么做就不一定了。这与编译器的优化级别相关,请看下图:
gcc 的 -O 选项可以指定优化级别,我们上面编译程序时没有使用 -O 选项,因此编译器执行的是默认的 -O0,也即无优化编译。那能否在 -O0 优化级别也使用 inline 函数的特性呢?当然是可以的,只需要在定义 inline 函数时,添加 __attribute__((always_inline)) 即可,例如:
现在再来编译C语言程序并查看汇编代码,得到如下结果:
这种情况下,编译器并没有为 i_add() 函数生成响应的汇编代码。虽然 inline 函数在预处理阶段没有像 define 宏定义那样展开,但是在生成汇编代码阶段展开了,而且参与了调用它的代码部分的优化,这显然会让整个C语言程序的效率提高。
inline 函数虽然表现上很像 define 宏定义,但是却并不能完全取代 define 宏定义,这一点在我之后的文章里会讨论,敬请关注。
小结
在 C语言程序开发中,建议把那些对时间要求比较高,且C语言代码长度比较短的函数定义为 inline 函数,这么做常常可以提升程序的效率。在默认的 -O0 编译优化项不能确保 inline 一定起作用,但是可以添加添加 __attribute__((always_inline))强制编译器对 inline 函数做相应的处理。因为 inline 函数会将自己展开,所以编译器通常不会再为 inline 生成汇编代码,不过,如果是通过函数指针的形式调用 inline 函数,编译器为了获得 inline 函数的地址,仍然会为其生成汇编代码的。
欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。
本文相关文章:

哪一个编程语言比较好?什么是pypl,以及tiobe-index的不同
2025年4月5日 03:30

java接口代码(不同编程语言的程序可不可以通过接口相互调用)
2025年4月1日 13:00

stakeholder theory(英语语言文学硕士毕业论文答辩一般会问什么问题)
2025年3月29日 17:00

在C语言中 z=x>y x: y; 这个表达式是什么意思啊,求解?if(i=a<ba:b;i>0;i--)啥意思
2025年3月29日 11:50

谭浩强c语言电子版(哪本书最适合初学电工电子比如初学C语言就用谭浩强C程序设计,单片机就郭天祥单片机)
2025年3月23日 06:30

易语言辅助论坛(请问想学写游戏脚本,是学易语言还是学按键精灵)
2025年3月18日 14:40

c语言和c加加先学哪个(c++是在c语言的基础上演变来的,那为何c++没有代替c语言呢)
2025年3月9日 22:20

编程学哪个语言好(现在学习编程,学那个语言简单点好学,好就业)
2025年3月9日 16:20

c语言程序设计第三版(软件工程大一新生,自学C语言用c程序设计第三版还行吗)
2025年3月7日 21:20

最容易学的编程语言(JAVA是不是目前最容易学最容易上手的编程语言)
2025年3月7日 05:30

c语言编译器安卓版(暑假打算写个基于安卓的C语言编译器,请教大佬该学习哪方面的知识)
2025年3月4日 04:30
更多文章:

幻灯片制作时应当做到(如何制作让人满意的PPT,制作一份合格的PPT应该掌握哪些要领)
2025年3月3日 00:00

trailblazer(雪佛兰Trailblazer回归,它为何被称为美式越野小霸王)
2025年2月27日 02:00

php声明数组(PHP中要使用数组的话必须先定义一个变量为array()吗)
2025年4月4日 15:10

switch case用法(switch case的用法,最好详细介绍一下)
2025年2月10日 21:10

tcpip详解电子版(TCP/IP协议详解!就是什么机械出版社的那本书```)
2025年2月23日 01:00

suspecting(suspecting与suspicious的区别)
2025年2月18日 13:50

reasonable翻译(理性的人,感性的人,用英语怎么说)
2025年3月27日 11:50

python中float的用法(python怎么float转成int)
2025年3月19日 17:00

jitter是什么意思中文(Jetter是什么意思,就这个我不会)
2025年3月27日 14:50

finaldata数据恢复(目前市场上最好的数据恢复软件是什么)
2025年3月30日 14:30

求推荐几本好看的穿越或重生到拳皇世界八神庵的小说,主角要保留拳皇记忆要完结的谢谢?有关八神庵的小说
2025年2月15日 16:50