4.函数库
4.1、什么是函数库?
(1)函数库就是一些事先写好的函数的集合,给别人复用。
(2)函数是模块化的,因此可以被复用。我们写好了一个函数,可以被反复使用。也可以A写好了一个函数然后共享出来,当B有相同的需求时就不需自己写直接用A写好的这个函数即可。
4.2、函数库的由来
(1)最开始是没有函数库,每个人写程序都要从零开始自己写。时间长了慢慢的早期的程序员就积累下来了一些有用的函数。
(2)早期的程序员经常参加行业聚会,在聚会上大家互相交换各自的函数库。
(3)后来程序员中的一些大神就提出把大家各自的函数库收拢在一起,然后经过校准和整理,最后形成了一份标准化的函数库,就是现在的标准的函数库,譬如说glibc。
4.3、函数库的提供形式:动态链接库与静态链接库
(1)早期的函数共享都是以源代码的形式进行的。这种方式共享是最彻底的(后来这种源码共享的方向就形成了我们现在的开源社区)。但是这种方式有它的缺点,缺点就是无法以商业化形式来发布函数库。
(2)商业公司需要将自己的有用的函数库共享给被人(当然是付费的),但是又不能给客户源代码。这时候的解决方案就是以库(主要有2种:静态库和动态库)的形式来提供。
(3)比较早出现的是静态链接库。静态库其实就是商业公司将自己的函数库源代码经过只编译不连接形成.o的目标文件,然后用ar工具将.o文件归档成.a的归档文件(.a的归档文件又叫静态链接库文件)。商业公司通过发布.a库文件和.h头文件来提供静态库给客户使用;客户拿到.a和.h文件后,通过.h头文件得知库中的库函数的原型,然后在自己的.c文件中直接调用这些库文件,在连接的时候链接器会去.a文件中拿出被调用的那个函数的编译后的.o二进制代码段链接进去形成最终的可执行程序。
(4)动态链接库比静态链接库出现的晚一些,效率更高一些,是改进型的。现在我们一般都是使用动态库。静态库在用户链接自己的可执行程序时就已经把调用的库中的函数的代码段链接进最终可执行程序中了,这样好处是可以执行,坏处是太占地方了。尤其是有多个应用程序都使用了这个库函数时,实际上在多个应用程序最后生成的可执行程序中都各自有一份这个库函数的代码段。当这些应用程序同时在内存中运行时,实际上在内存中有多个这个库函数的代码段,这完全重复了。而动态链接库本身不将库函数的代码段链接入可执行程序,只是做个标记。然后当应用程序在内存中执行时,运行时环境发现它调用了一个动态库中的库函数时,会去加载这个动态库到内存中,然后以后不管有多少个应用程序去调用这个库中的函数都会跳转到第一次加载的地方去执行(不会重复加载)。
4.4、函数库中库函数的使用
(1)gcc中编译链接程序默认是使用动态库的,要想静态链接需要显式用-static
来强制静态链接。
(2)库函数的使用需要注意3点:第一,包含相应的头文件;第二,调用库函数时注意函数原型;第三,有些库函数链接时需要额外用-lxxx来指定链接;第四,如果是动态库,要注意-L指定动态库的地址。
5.字符串函数
5.1、什么是字符串
(1)字符串就是由多个字符在内存中连续分布组成的字符结构。字符串的特点是指定了开头(字符串的指针)和结尾(结尾固定为字符’\0’),而没有指定长度(长度由开头地址和结尾地址相减得到)
5.2、为什么要讲字符串处理函数
(1)函数库为什么要包含字符串处理函数?因为字符串处理的需求是客观的,所以从很早开始人们就在写很多关于字符串处理的函数,然后逐渐形成了现在的字符串处理函数库。
(2)面试笔试时,常用字符串处理函数也是经常考到的点。
5.3、常用字符串处理函数
(1)C库中字符串处理函数包含在string.h中,这个文件在ubuntu系统中在/usr/include中
(2)常见字符串处理函数及作用:
memcpy 确定src和dst不会overlap,则使用memcpy效率高
memmove 确定会overlap或者不确定但是有可能overlap,则使用memove比较保险
memset
memcmp
memchr
strcpy
strncpy
strcat
strncat
strcmp
strncmp
strdup
strndup
strchr
strstr
strtok
·······
6.数学库函数
6.1、math.h
(1)真正的数学运算的函数定义在:/usr/include/i386-linux-gnu/bits/mathcalls.h
(2)使用数学库函数的时候,只需要包含math.h
即可。
6.2、计算开平方
(1)库函数: double sqrt(double x);
#include <stdio.h>
#include <math.h>
int main(void)
{
double a = 16.0;
double b = sqrt(a);
printf("b = %lf.\n", b); //b=4.
return 0;
}
注意区分编译时警告/错误,和链接时的错误:
编译时警告/错误:
4.6.10.math.c:9:13: warning: incompatible implicit declaration of built-in function ‘sqrt’ [enabled by default]
double b = sqrt(a);
链接时错误:
4.6.10.math.c:(.text+0x1b): undefined reference to `sqrt’
collect2: error: ld returned 1 exit status
分析:这个链接错误的意思是:sqrt函数有声明(声明就在math.h中)有引用(在math.c)但是没有定义,链接器找不到函数体。sqrt本来是库函数,在编译器库中是有.a和.so链接库的(函数体在链接库中的)。
C链接器的工作特点:因为库函数有很多,链接器去库函数目录搜索的时间比较久。为了提升速度想了一个折中的方案:链接器只是默认的寻找几个最常用的库,如果是一些不常用的库中的函数被调用,需要程序员在链接时明确给出要扩展查找的库的名字。链接时可以用-lxxx来指示链接器去到libxxx.so中去查找这个函数。
6.3、链接时加-lm
(1)-lm就是告诉链接器到libm中去查找用到的函数。
(2)实战中发现在高版本的gcc中,经常会出现每加-lm也可以编译链接的。