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

原创 2016年05月30日 14:47:03

在第一季中,我简单的写了一个实验。来证明了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 要在编译的时候找到函数的定义,写在源文件显然在其他文件引用这个函数的时候是看不到函数定义的。)。


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

GeekBand-极客班-C++算法原理与实践-董飞-课程笔记(一)

GeekBand-极客班-C++算法原理与实践-董飞-课程笔记(一)算法入门和编程风格

Geekban极客班C++ 第四周

Geekban

C++面向对象高级开发_极客班_第二节课笔记

Big threeclass 有两种 ,一种是带指针的另一种是不带指针的。不带指针的类最典型的就是complex类,而带指针的最典型的就是string类。带指针的类有很多特性,其中最重要的就是big ...

Geekban极客班 第三周

继承 复合

极客班STL与泛型编程 第一周学习笔记

泛型编程需注意的问题

Geekban极客班 第二周

构造函数 inline String::String(const char* cstr) { if (cstr) { //判断指针所指是否为空 m_data = ne...

极客班C++(上)第一周课程笔记

防御式声明,const,private。

Geekban极客班C++STL与泛型编程 第二周

SetSet和multiset会根据特定的排序准则,自动将元素排序。 两者不同之处在于multiset允许元素重复,但是set不允许。Template<class_Kty,class_Pr=less...

极客班C++设计模式第一周课堂笔记——设计原则

软件设计的目标是抵御变化,方法是提高复用性。8个重要的设计原则

Geekban极客班 C++第五周

vptr和vtbl 只要类里带有虚函数,对象里就会多一个指针。 继承函数是继承函数的调用权,父类有虚函数则子类一定虚函数。 虚指针vptr指向虚表vtbl,虚表中存放虚函数指针。 c++编译器...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)