从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);
    return;
  }

  /* two simple cases: 4 and 6 */
  if ((4 == n) || (6 == n)) {
    printf("%i = %i + %i
", 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, 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("
is_prime
");
  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
", i);
    }
  }
  printf("%i primes in range of 1 to a thousand.
", count);

  /* prime_factors */
  printf("
prime_factors
");
  printf("prime factors of 12: ");
  prime_factors(12);
  printf("
");
  
  printf("prime factors of 13: ");
  prime_factors(13);
  printf("
");
  
  printf("prime factors of 876,512,779: ");
  prime_factors(876512779);
  printf("
");

  /* are_coprimes */
  printf("
are_coprime
");
  printf("Are %i and %i coprime? %s
",
     21, 22, are_coprimes(21, 22) ? "yes" : "no");
  printf("Are %i and %i coprime? %s
",
     21, 24, are_coprimes(21, 24) ? "yes" : "no");

  /* goldbach */
  printf("
goldbach
");
  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

人就像是被蒙着眼推磨的驴子,生活就像一条鞭子;当鞭子抽到你背上时,你就只能一直往前走,虽然连你也不知道要走到什么时候为止,便一直这么坚持着。
原文地址:https://www.cnblogs.com/guochaoxxl/p/14859789.html