内联函数
1
什么是内联函数:
简单的来说,内联函数就是在你定义一个函数的时候,在最前面加一个关键字inline(一般在定义一个函数前加inline关键字有用,但在声明函数前面加inline关键字不起作用)。
inline void Foo(int x, int y); // inline 仅与函数声明放在一起
void Foo(int x, int y){
}
void Foo(int x, int y);
inline void Foo(int x, int y) // inline 与函数定义体放在一起{
}
2
为什么要使用内联函数
:
对于这个内联函数的使用,我们要明白为啥要用它,其实这个原因在之前的文章c语言宏定义里面有讲过关于带参宏和函数的区别和优缺点——C语言之宏定义用法;大家可以去看一下这个文章,我们写函数的话,无非就是要实现功能,然后我们在主函数里面去调用它,当主函数执行到这个函数时,就会跳去这个函数里面去执行函数里面的每一条语句,执行完的话,再跳回来执行主程序里面的程序,这样的话效率有点低;而且我们的带参宏的话,它可以直接展开语句在调用它的地方直接用,不用跳到定义的那个地方去执行完,而是直接原地展开来执行,然后再来执行主程序(不过带参宏的缺点就是,它的功能语句不能写太多条相比于函数来说的话)。而我们使用内联函数,就能把它们两者的有点都结合一身(调用内联函数可以原地展开,不用跑回定义函数的地方去执行函数里面的程序,执行的语句条数也更多了),而且当函数多次被调用时,这个时候其实函数内部的一些数据会占用栈空间的,我们使用内联函数就能节约栈空间(就是函数内数据的内存空间,在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足所造成的程式出错的问题,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭)。(这里我的理解就是,因为内联函数具有函数和带参宏的特点,所以当多次调用内联函数的话,函数里面的语句就直接放到当执行到内联函数的那个地方,就不用跑到定义的地方去执行了,又开辟栈空间了)。
#include
inline char* dbtest(int a) {
return (a % 2 > 0) ? "奇" : "偶";
}
int main(){
int i = 0;
for (i=1; i 10; i++)
{
printf("i:%d 奇偶性:%s \n", i, dbtest(i));
}
}
演示结果:
i:1 奇偶性:奇
i:2 奇偶性:偶
i:3 奇偶性:奇
i:4 奇偶性:偶
i:5 奇偶性:奇
i:6 奇偶性:偶
i:7 奇偶性:奇
i:8 奇偶性:偶
i:9 奇偶性:奇
说明:
上面的例子就是标准的内联函数的用法,使用inline修饰带来的好处我们表面看不出来,其实在内部的工作就是在每个for循环的内部任何调用dbtest(i)的地方都换成了(a%2>0)?"奇":"偶"这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。 其实这种有点类似咱们前面学习的动态库和静态库的问题,使 dbtest 函数中的代码直接被放到main 函数中,执行for 循环时,会不断调用这段代码,而不是不断地开辟一个函数栈。(和上面说的是一个意思)。
3
内联函数的使用限制
:
内联能提高函数的执行效率,为什么不把所有的函数都定义成内联函数?如果所有的函数都是内联函数,还用得着“内联”这个关键字吗?
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联函数:
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
(2)如果函数体内出现循环(如while,以及switch),那么执行函数体内代码的时间要比函数调用的开销大。
(3)不能做递归函数使用。
动态链接库的制作和使用
1
动态链接库的制作:
在我们gcc编译环境下默认使用的就是动态链接库的,今天我们来自己制作动态链接库。这里制作的一些步骤和昨天的有点类似,但是也有不同的地方,我挑重点来讲。
这里我先在当前目录创建两个文件一个是hell.c ,另外一个是hell.h,然后在hell.c里面写上:
#include
void fun1(void){
printf("hello\n");
}
然后把这个函数的原型写到hell.h文件当中去:
void fun1(void);
然后同样也是在当前目录下写一个Makefile文件来记录生成静态链接库的
过程,这
个文件里面写(注意这个和静态链接库里面不一样):
all:
gcc aston.c -o aston.o -c -fPIC
gcc -o libaston.so aston.o -shared
说明:
这里面的-fPIC(fPIC的全称是 Position Independent Code, 用于
生成位置无
关
代码。
什么是位置无关代码,个人理解是代码无绝对跳转,跳转都为相对跳
转,)
-
shared是按照共享库的方式来链接;具体可以看这篇博客的介绍或者百度一下网上有比较多的介绍,这里我就不具体介绍了
下面是详细过程:
root@ubuntu-virtual-machine:/home/ubuntu/tu# touch hell.c
root@ubuntu-virtual-machine:/home/ubuntu/tu# touch hell.h
root@ubuntu-virtual-machine:/home/ubuntu/tu# ls
hell.c hell.h
root@ubuntu-virtual-machine:/home/ubuntu/tu# touch Makefile
root@ubuntu-virtual-machine:/home/ubuntu/tu# vim hell.c
root@ubuntu-virtual-machine:/home/ubuntu/tu# vim hell.h
root@ubuntu-virtual-machine:/home/ubuntu/tu# vim Makefile
root@ubuntu-virtual-machine:/home/ubuntu/tu# make
gcc hell.c -o hell.o -c -fPIC
gcc -o libhell.so hell.o -shared
注意:做库的人给用库的人发布库时,发布libxxx.so和xxx.h即可。
2
动态链接库的使用
:
上面创建好了静态链接库,现在我们就来使用这个静态链接库,然后我在当前目录下再创建一个目录叫做testlib,然后把hell.h和libhell.so移到这个目录下面,同时在这个目录下面创建一个test.c文件:
root@ubuntu-virtual-machine:/home/ubuntu/tu# mkdir testlib
root@ubuntu-virtual-machine:/home/ubuntu/tu# ls
hell.c hell.h hell.o libhell.so Makefile testlib
root@ubuntu-virtual-machine:/home/ubuntu/tu#cp hell.h testlib
root@ubuntu-virtual-machine:/home/ubuntu/tu# cp libhell.so testlib
root@ubuntu-virtual-machine:/home/ubuntu/tu# cd testlib/
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# ls
hell.h libhell.so
然后的话,我在这个test.c文件里面就使用刚才那个创建的函数fun1():
#include
#include "hell.h"
int main(void){
fun1();
return 0;
}
现在的话,我们来看效果,这个时候直接编译的话,会显示找不到这个函数(和静态链接库一样):
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# gcc test.c -o test
/tmp/cc7QhpGO.o:在函数‘main’中:
test.c:(.text+0x5):对‘fun1’未定义的引用
collect2: error: ld returned 1 exit status
说明我们还没有用到我们的动态链接库(会显示找不到库):
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# gcc test.c -o test -lhell
/usr/bin/ld: 找不到 -lhell
collect2: error: ld returned 1 exit status
我们还是要使用"-L."来指定库的地址(但是虽然能够编译通过,但是执行会报错):
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# gcc test.c -o test -lhell -L.
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# ./test
./test: error while loading shared libraries: libhell.so:
cannot open shared object file: No such file or directory
我们的解决方案有两种:
第一种:将libhell.so放到固定目录下就可以了,这个固定目录一般是/usr/lib目录:
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# cp libhell.so /usr/lib
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# ./test
hello
第二种方法:使用环境变量LD_LIBRARY_PATH。操作系统在加载固定目录/usr/lib之前,会先去LD_LIBRARY_PATH这个环境变量所指定的目录下去寻找,如果找到就不用去/usr/lib下面找了,如果没找到再去/usr/lib下面找。所以解决方案就是将libaston.so所在的目录导出到环境变量LD_LIBRARY_PATH中即可。
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ubuntu/tu/testlib
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# ./test
hello
上面的动态链接库的制作和使用就成功了,这里再介绍一下ldd命令:作用是可以在一个使用了共享库的程序执行之前解析出这个程序使用了哪些共享库,并且查看这些共享库是否能被找到,能被解析(决定这个程序是否能正确执行)。
root@ubuntu-virtual-machine:/home/ubuntu/tu/testlib# ldd test
linux-vdso.so.1 (0x00007ffcf67e3000)
libhell.so (0x00007f5805c61000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5805870000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5806065000)