C/C++ inline函数--undefined reference to `xxxxx`错误解析

发现问题

  • 最近在做gcc plugin和llvm pass相关的东西,偶然之间发现一个奇怪的问题,问题代码如下
#include <stdio.h>
inline void nothing() {
    printf("nothing \n");
}
int main() {
    nothing();
    return 0;
}
  • 编译时报错如下:
    /usr/bin/ld: /tmp/ccGjvgiZ.o: in function main': csdn.c:(.text+0xa): undefined reference to nothing’
    collect2: 错误:ld 返回 1·
  • 其实这和我们平常的代码没什么不同,只是加了个inline而已。然后我就尝试编译出汇编代码来看看有什么问题,汇编代码如下:
	.file	"csdn.c"
	.text
	.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
	movl	$0, %eax
	call	nothing
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.ident	"GCC: (GNU) 7.3.0"
	.section	.note.GNU-stack,"",@progbits

  • 我们可以发现在汇编代码中根本没有nothing()这个函数的定义,但是却在main函数中直接call了nothing这个函数,所以肯定后面会报undefined reference的错误。为了印证这个观点,我编译生成了.o文件并通过nm查看符号信息:
    在这里插入图片描述
    可以看到,nothing是一个外部符号,而我们又没有外部的定义,所以肯定会报undefined reference错误。

解决问题

加static关键字

  • static关键字的作用有:将局部变量的生命周期延长、将成员函数(变量)的作用域限制在本文件等所以我们通过static关键字,显示的把函数的作用域限制在本文件,也就会限制它生成的符号为内部符号。
  • C语言代码如下
#include <stdio.h>
inline static void nothing() {
    printf("nothing \n");
}
int main() {
    nothing();
    return 0;
}
  • 符号如下:可以看到nothing已经变成了本地实现的标识t
    在这里插入图片描述
  • 我们也的确能够正确的编译出可执行文件如下:
    在这里插入图片描述

存在的问题

  • 我们从加static关键字方法编译得到的.s文件中可以看到,其实nothing是并没有被内联展开的,汇编代码如下:
	.file	"csdn.c"
	.text
	.section	.rodata
.LC0:
	.string	"nothing "
	.text
	.type	nothing, @function
nothing:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$.LC0, %edi
	call	puts
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	nothing, .-nothing
	.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
	movl	$0, %eax
	call	nothing
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.ident	"GCC: (GNU) 7.3.0"
	.section	.note.GNU-stack,"",@progbits

  • 但是我们加入inline关键字的目的,肯定是想要内联展开,减少频繁的函数调用的开销,所以有了下面的方法。

attribute((always_inline))编译属性

  • 往inline函数中加入__attribute__((always_inline))编译属性,这会告诉编译器必须将该函数内联展开。
  • 源码如下:
#include <stdio.h>
#define FORCE_FUNCTION  __attribute__((always_inline))
FORCE_FUNCTION inline void nothing()
{
    printf("nothing \n");
}
int main() {
    nothing();
    return 0;
}
  • 得到的符号如下:可以看到已经没有了nothing符号
    在这里插入图片描述
  • 而且生成的.s文件也可以看到inline函数被正确的内联展开了,汇编代码如下
.file	"csdn.c"
	.text
	.section	.rodata
.LC0:
	.string	"nothing "
	.text
	.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
	movl	$.LC0, %edi
	call	puts
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.ident	"GCC: (GNU) 7.3.0"
	.section	.note.GNU-stack,"",@progbits

参考大佬分享

  • 25
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值