关闭

(GeekBand或极客班) inline 函数的思考。(第二季)

83人阅读 评论(0) 收藏 举报
分类:

在第一季中,我简单的写了一个实验。来证明了inline函数的执行效率。


随后 Richard_HuTianyi 在评论中提出了一些自己的见解。

1--inline函数无法inline的情况。如果一个inline函数在头文件中被定义。但是编译器无法将其inline,将其看成普通函数处理,那么如果这个头文件在多个源文件中被包涵,是否会有函数重定义的情况呢?

2--如果不写inline编译器会不会去inline。


先来看下第一个问题:对此,在这里用一个小实验,观察一下结果。

首先在 Demo.h 中写一个复杂的 inline 函数。为了不让编译能够 inline 。代码如下:

Demo.h

#ifndef __DEMO_H__
#define __DEMO_H__

//循环加递归,想必不会inline
extern inline void Add(int level)
{	
	for (int i = 1; i < level; ++i)
	{
		Add(i);
	}
}

#endif

然后再 Demo1.cpp和Demo2.cpp中调用该函数。在这里如果是普通函数此时必定会产生重定义的错误。代码如下:

Demo1.h

#ifndef __DEMO1H__
#define __DEMO1_H__

#include "Demo.h"

extern void f1();

#endif

Demo1.cpp

#include "Demo.h"

void f1()
{
	Add(10);	
}

Demo2.h

#ifndef __DEMO2_H__
#define __DEMO2_H__

#include "Demo.h"

extern void f2();

#endif

Demo.cpp

#include "Demo.h"

void f2()
{
	Add(10);	
}

下面给出测试代码:

main.cpp

#include <iostream>

#include "Demo1.h"
#include "Demo2.h"

int main()
{

	f1();	

	f2();

	return 0;
}

编译运行。确实没有问题。但是这里有两种可能情况,

1. Add 函数被编译器 inline 。这种情况自然不会有问题。

2. Add函数没有被编译器 inline 。这种情况是我们要验证的。


如何确定上面执行的到底是哪种情况呢?这里我编译出了.s汇编文件。

打开Demo1.s和Demo2.s可以看到下面的代码:(代码很多,我只找到关键的地方。)

LVL26:
	.loc 1 10 0
	call	__Z3Addi

LBB73:
	.loc 1 10 0
	movl	%edx, (%esp)
	call	__Z3Addi

Ltext_cold0:
	.globl	__Z3Addi
	.def	__Z3Addi;	.scl	2;	.type	32;	.endef
__Z3Addi:

这里应该不难看出在Demo1.cpp和Demo2.cpp中发生了函数调用。而不是inline。至此。排除了上面的第一种情况,Add函数没有被编译器内联。




结论:如果inline函数不被编编译器inline,那么不会出现函数重定义的情况。


现在再来看看第二种情况:

我现在将 Demo.h 中的 Add 函数inline去掉,同时将函数代码简化,为了使编译器将其 inline 。代码如下:

#ifndef __DEMO_H__
#define __DEMO_H__

extern void Add(int level)
{	
	level += 1;
}

#endif

编译:

$ make
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -E  main.cpp -o main.i
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -S  main.i -o main.s
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -c  main.s -o main.o
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -E  Demo1.cpp -o Demo1.i
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -S  Demo1.i -o Demo1.s
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -c  Demo1.s -o Demo1.o
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -E  Demo2.cpp -o Demo2.i
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -S  Demo2.i -o Demo2.s
g++ -std=c++11 -DDEBUG -g -O2 -D_REENTRANT -c  Demo2.s -o Demo2.o
g++ main.o Demo1.o Demo2.o -o debug
Demo1.o:在函数‘Z3Addi’中:
/cygdrive/c/Users/Lxp/Desktop/03/Demo.h:6: multiple definition of `Add(int)'
main.o:/cygdrive/c/Users/Lxp/Desktop/03/Demo.h:6:第一次在此定义
Demo2.o:在函数‘Z3Addi’中:
/cygdrive/c/Users/Lxp/Desktop/03/Demo.h:6: multiple definition of `Add(int)'
main.o:/cygdrive/c/Users/Lxp/Desktop/03/Demo.h:6:第一次在此定义
collect2: 错误:ld 返回 1
Makefile:32: recipe for target 'debug' failed
make: *** [debug] Error 1

果然:函数重定义了。那么将函数定义写入源文件试试呢。

修改代码如下:

Demo.h

#ifndef __DEMO_H__
#define __DEMO_H__


extern void Add(int level);

#endif
Demo.cpp

#include "Demo.h"

void Add(int level)
{	
	level += 1;
}

这次编译一下。嗯没问题。看看汇编文件.

	movl	$10, (%esp)
	call	__Z3Addi

看来还是函数调用。

结论:不加 inline 编译器不会将其 inline。



总结:
1--对于复杂的inline函数编译器不会将其inline。这种情况,不会出现重定义。
2--对于不加 inline 的函数。函数的实现不能写入头文件中,编译器不会自动将其inline,这种情况会出现函数重定义。写入源文件中的函数即使简单也不会 inline(如果inline 要在编译的时候找到函数的定义,写在源文件显然在其他文件引用这个函数的时候是看不到函数定义的。)。


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1341次
    • 积分:73
    • 等级:
    • 排名:千里之外
    • 原创:6篇
    • 转载:0篇
    • 译文:0篇
    • 评论:3条
    文章分类
    文章存档
    最新评论