从C 语言用户角度理解 Linux 的库
从软件诞生的那天开始,如何有效地重复利用代码就成为了软件的开发的需要,否则,每个程序都要从头开始,编程就谈不上乐趣,就无法大规模推广了。C语言中库无疑是解决代码重复利用的重要途径之一,是非常简单合理的复用代码的一种方式。其实,软件库技术:长期存在;简单合理; 代码复用。下面以 Linux 为例,创建、发布和使用这些库,这些步骤也可以应用于其它类 Unix 系统。这些示例库使用 C 语言编写,适合该任务。Linux 内核大部分由 C 语言和少量汇编语言编写(Windows 和 Linux 的表亲如 macOS 也是如此)。用于输入/输出、网络、字符串处理、数学、安全、数据编码等的标准系统库等主要由 C 语言编写。所以使用 C 语言编写库就是使用 Linux 的原生语言来编写。除此之外,C 语言的性能也非常突出的。主要包括库和测试client程序用 C 语言客户程序来访问 C 语言编写的库,实际也可以用于其他编程语言。
一、静态库和动态库
linux中主要有静态库和动态库两种。其中
静态库:链接时直接将库文件的内容编码进代码;每个用到库文件的client程序都拥有库文件的拷贝;如果库文件更新了须要再次更新链接;
静态库:链接阶段只是做了标识;多个client用到的同一个动态库文件在内存中只有一份拷贝;库文件的更新须要系统加载器将共享库和client程序链接即可;动态库性能不如静态库高效;动态库的复杂性高;动态库的应用异常灵活;
1、库的源码先被便衣成一个或多个目标模块(二进制文件,但不包含可执行信息),目标模块可以被包含到库中;也可以被链接到可执行的二进制文件中
2、目标模块被打包成一个文件(静态库以.a动态库以.o)特殊后缀名,一般加上lib前缀
3、库文件的目录:/usr/lib /usr/local/lib ./ 当前目录
二、C语言的函数
1、函数的存储类决定着函数的应用范围,其中extern(默认)表明函数是整个库有效的;而static表明函数的范围被限制到函数所在的文件中;
通过函数的存储类型的修饰符,完成函数应用范围的区分与特定函数的隐藏与隔离
2、函数的定义和声明,C语言中只允许命名函数
函数定义:1)、函数名称必须唯一
2)、参数必须指明类型,参数列表可以为空,也就是无需传入参数
3)、返回必须有具体类型,若明确无返回,需指定为void,只能返回一个值
4)、函数主体包含若干程序语句,须要用{}包括,
3、函数的声明,与函数定义唯一的不同就是函数主体部分用 ; 替换{}部分即可;函数可以被多次声明,但只能被定义一次
4、函数的应用,
1)、C语言中规定变量需先定义后使用,
2)、被调用函数需对调用函数是可见的,
3)、对一些暂时没有实现的被调用的函数,可以通过先声明,后定义的方式避免编程过程被打断、
4)、也可以通过先定义被调用函数,再调用;
5、库
1)、C语言中功能相近的函数形成库,
2)、库中的函数通过其头文件实现对调用的函数的隐藏和封装,
3)、头文件是客户程序与库函数的接口,
4)、动态库的优势会更加明显
三、静态库的三步走制作:
以源代码primes.c为例,目标文件和库文件均以常规方式命名。
1、生成目标文件
gcc -c primes.c //只编译,生成目标文件primes.o
2、ar -cvq libprimes.a primesc.o //ar是静态库压缩的命令;选项cvq是创建、详细和快速添加等;库名须以lib为前缀,静态库以.a为后缀名,库名必须是唯一的;
3、sudo cp libprimes.a /usr/local/lib //静态库的发布,就是将其拷贝至相应的目录;
四、动态库的五步骤制作:
以源代码primes.c为例,目标文件和库文件均以常规方式命名。
1、生成位置无关文件
gcc primes.c -c -fpic //编译选项fpic,生成位置无关代码
2、gcc -shared -W1, -soname, libshprimes.so -o libshprimes.so.1 primes.o //gcc是动态库制作的命令;
//选项shared表明库是共享(动态)的,而非静态的;
//选项soname指定了库的逻辑名称;客户程序一般通过库的逻辑名称访问库文件
//选项o指定了库的物理文件名称;
3、sudo cp libshprimes.so.1 /usr/local/lib //动态库的发布,就是将其拷贝至相应的目录;
4、sudo ln --symbolic libshprimes.so.1 libshprimes.so //链接库的物理名称和逻辑名称
5、sudo ldconfig //使用ldconfig配置系统的动态加载器,此过程是为了确保系统的中的库加载器能够正确找到新发布的库
五、实战
1、primes.h
/** header file primes.h: function declarations **/ extern unsigned is_prime(unsigned); extern void prime_factors(unsigned); extern unsigned are_coprimes(unsigned, unsigned); extern void goldbach(unsigned);
2、primes.c
#include <stdio.h> #include <math.h> extern unsigned is_prime(unsigned n) { if (n <= 3) return n > 1; /* 2 and 3 are prime */ if (0 == (n % 2) || 0 == (n % 3)) return 0; /* multiples of 2 or 3 aren't */ /* check that n is not a multiple of other values < n */ unsigned i; for (i = 5; (i * i) <= n; i += 6) if (0 == (n % i) || 0 == (n % (i + 2))) return 0; /* not prime */ return 1; /* a prime other than 2 or 3 */ } extern void prime_factors(unsigned n) { /* list 2s in n's prime factorization */ while (0 == (n % 2)) { printf("%i ", 2); n /= 2; } /* 2s are done, the divisor is now odd */ unsigned i; for (i = 3; i <= sqrt(n); i += 2) { while (0 == (n % i)) { printf("%i ", i); n /= i; } } /* one more prime factor? */ if (n > 2) printf("%i", n); } /* utility function: greatest common divisor */ static unsigned gcd(unsigned n1, unsigned n2) { while (n1 != 0) { unsigned n3 = n1; n1 = n2 % n1; n2 = n3; } return n2; } extern unsigned are_coprimes(unsigned n1, unsigned n2) { return 1 == gcd(n1, n2); } extern void goldbach(unsigned n) { /* input errors */ if ((n <= 2) || ((n & 0x01) > 0)) { printf("Number must be > 2 and even: %i is not.\n", n); return; } /* two simple cases: 4 and 6 */ if ((4 == n) || (6 == n)) { printf("%i = %i + %i\n", n, n / 2, n / 2); return; } /* for n >= 8: multiple possibilities for many */ unsigned i; for (i = 3; i < (n / 2); i++) { if (is_prime(i) && is_prime(n - i)) { printf("%i = %i + %i\n", n, i, n - i); /* if one pair is enough, replace this with a break */ } } }
3、测试文件,testPrimes.c
#include <stdio.h> #include <primes.h> int main() { /* is_prime */ printf("\nis_prime\n"); unsigned i, count = 0, n = 1000; for (i = 1; i <= n; i++) { if (is_prime(i)) { count++; if (1 == (i % 100)) printf("Sample prime ending in 1: %i\n", i); } } printf("%i primes in range of 1 to a thousand.\n", count); /* prime_factors */ printf("\nprime_factors\n"); printf("prime factors of 12: "); prime_factors(12); printf("\n"); printf("prime factors of 13: "); prime_factors(13); printf("\n"); printf("prime factors of 876,512,779: "); prime_factors(876512779); printf("\n"); /* are_coprimes */ printf("\nare_coprime\n"); printf("Are %i and %i coprime? %s\n", 21, 22, are_coprimes(21, 22) ? "yes" : "no"); printf("Are %i and %i coprime? %s\n", 21, 24, are_coprimes(21, 24) ? "yes" : "no"); /* goldbach */ printf("\ngoldbach\n"); goldbach(11); /* error */ goldbach(4); /* small one */ goldbach(6); /* another */ for (i = 100; i <= 150; i += 2) goldbach(i); return 0; }
或者:
from ctypes import cdll cdll.LoadLibrary("libshprimes.so") ## logical name primes.is_prime(13) primes.is_prime(12) primes.are_coprimes(8, 24) primes.are_coprimes(8, 25) primes.prime_factors.restype = None primes.goldbach.restype = None primes.prime_factors(72) primes.goldbach(32)
后续修改
http://www.linuxeden.com/a/85624
http://www.linuxeden.com/a/85505
http://www.linuxeden.com/a/84903
https://dp2px.com/2020/02/09/soft-hard-work/
https://www.bilibili.com/video/av88392907/
https://www.bilibili.com/video/BV1CX4y137rg/?spm_id_from=333.788.recommend_more_video.0