浅谈C语言inline关键字

对于C++开发者来说,inline是个再熟悉不过的关键字,因为默认的成员函数都是inline,也是常规高校教材中宣扬C++的“优势”之一。

但是C语言其实也是支持inline关键字的,而且是很早期的gcc就支持了该关键字。在Linux0.12版本内核代码中也用到了该关键字。

今天码哥浅谈一下这个关键字的作用和使用。

inline的作用浅显一点说,就是将声明了该关键字的函数不以call指令调用的方式来调用,而是直接将其展开在调用函数中。似乎感觉有点像宏展开的样子?

实际不然,我们以一个C语言示例来进行说明:

inline int foo(int a)
{
   
   a *= 3;
   return a;
}
int main(void)
{
   
    int a = foo(2);
    a += foo(1);
    return a;
}

例子很简单,foo函数被声明了inline,作用是将输入参数扩大三倍返回。

下面我们来看看这个例子的汇编是如何的。在此之前,需要重点提示:

inline关键字只有在开启了编译优化后才会启用,且如果函数定义时不声明为inline,那么inline只发生在有inline声明之后的调用点。

无编译优化汇编

$ gcc -S a.c

我们并未开启任何编译优化,其汇编如下:

	.file	"c.c"
	.text
	.globl	foo
	.type	foo, @function
foo:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	%edi, -4(%rbp)
	movl	-4(%rbp), %edx
	movl	%edx, %eax
	addl	%eax, %eax
	addl	%edx, %eax
	movl	%eax, -4(%rbp)
	movl	-4(%rbp), %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	foo, .-foo
	.globl	main
	.type	main, @function
main:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	$2, %edi
	call	foo
	movl	%eax, -4(%rbp)
	movl	$1, %edi
	call	foo
	addl	%eax, -4(%rbp)
	movl	-4(%rbp), %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.ident	"GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-39)"
	.section	.note.GNU-stack,"",@progbits

可以看到在main中存在两个call指令调用foo函数,我们的inline关键字作用并未生效。

编译优化汇编

$ gcc -S a.c -O

我们仅启用O1优化,那么看下汇编成了什么样子呢?

	.file	"c.c"
	.text
	.globl	foo
	.type	foo, @function
foo:
.LFB0:
	.cfi_startproc
	leal	(%rdi,%rdi,2), %eax
	ret
	.cfi_endproc
.LFE0:
	.size	foo, .-foo
	.globl	main
	.type	main, @function
main:
.LFB1:
	.cfi_startproc
	movl	$9, %eax
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.ident	"GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-39)"
	.section	.note.GNU-stack,"",@progbits

这里,码哥没有做任何删减。

读者可能会发现,main中返回值直接给了一个立即数9,而不是jmp到foo或者使用其相关指令计算的。这说明了什么呢?

这说明了,inline并不简简单单的类似宏扩展,而是编译器在编译时将foo代码展开进main中,并且在优化剪枝等优化步骤中对展开后的整体内容做优化,进而发现foo的输入与输出以及foo的两次调用结果是一个可推算的常数值。

因此,inline的作用不仅仅是避免了call指令的使用以及其关联的压栈弹栈等操作,更是可以让编译器对整体性能做出非常多改进优化,大幅提升性能。

但是,inline也不可滥用,这是因为原本只需要一份的函数被展开到整个工程中各个使用点上,虽然效率会有些许提升,但是指令数量可能会大幅增长,导致可执行程序体积过大。

相关推荐

  1. C语言inline关键字

    2024-01-01 01:08:02       38 阅读
  2. c/c++的关键字 inline 介绍

    2024-01-01 01:08:02       10 阅读
  3. C++ inline 关键字有什么做用?

    2024-01-01 01:08:02       32 阅读
  4. Effective C++(七):inline关键字, 降低文件间依存关系

    2024-01-01 01:08:02       32 阅读
  5. C4模型

    2024-01-01 01:08:02       32 阅读
  6. 【计算机语言 — 计算机语言分类

    2024-01-01 01:08:02       45 阅读
  7. 深入浅出C/C++中static关键字

    2024-01-01 01:08:02       9 阅读
  8. C++容器——unordered_set

    2024-01-01 01:08:02       24 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-01 01:08:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-01 01:08:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-01 01:08:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-01 01:08:02       20 阅读

热门阅读

  1. C++每日一练(7):爬山

    2024-01-01 01:08:02       38 阅读
  2. STL--映射:map

    2024-01-01 01:08:02       39 阅读
  3. 第二篇 创建型设计模式 - 灵活、解耦的创建机制

    2024-01-01 01:08:02       35 阅读
  4. 第二百三十八回

    2024-01-01 01:08:02       36 阅读
  5. C# 时间修改器,修改系统本地时间

    2024-01-01 01:08:02       34 阅读
  6. GBASE南大通用-CodeFirst 模式

    2024-01-01 01:08:02       39 阅读
  7. 关于解决el-select组件自动清除数据空格的问题

    2024-01-01 01:08:02       35 阅读