13 编译和链接

1 编译器组成

  • 预处理器
  • 编译器
  • 汇编器
  • 链接器

2 预编译

  • 预处理指令示例:gcc -E file.c -o file.i

  • 处理所有的注释,以空格代替

  • 将所有的 #define 删除,并且展开所有的宏定义

  • 处理条件编译指令 #if#ifdef#elif#else#endif

  • 处理 #include ,展开被包含的文件

  • 保留编译器需要使用的 #pragma 指令

3 编译

  • 编译指令示例:gcc -S file.i -o file.s

  • 编译

    • 对于预处理文件进行词法分析,语法分析和语义分析
    • 词法分析:分析关键字,标识符,立即数等是否合法
    • 语法分析:分析表达式是否遵循语法规则
    • 语义分析:在语法分析的基础上进一步分析表达式是否合法
  • 分析结束后进行代码优化生成相应的汇编代码文件

4 汇编

  • 汇编指令示例:gcc -c file.s -o file.o
  • 汇编器将汇编代码转变为机器可以执行的指令
  • 每条汇编语句几乎都对应一条机器指令

5 链接

  • 问题:工程中的每个C语言源文件被编译后生成目标文件,这些目标文件如何生成最终的可执行程序?
    • 利用链接器

5.1 链接器的意义

  • 链接器的主要作用是把各个模块之间相互引用的部分处理好,使得各个模块之间能够正确的衔接

5.2 模块链接——静态链接

  • 由链接器在链接时将库的内容直接加入到可执行程序中

  • Linux 下静态库的创建和使用

    • 编译静态库源码:gcc -c lib.c -o lib.o
    • 生成静态库文件:ar -q lib.a lib.o
    • 使用静态库编译:gcc main.c lib.a -o main.out
  • 示例

    • Demo

      //test.c
      #include <stdio.h>
      
      extern char* name();
      extern int add(int a,int b);
      
      int main()
      {
          printf("Name : %s\n",name());
          printf("Result : %d\n",add(2,3));
          
          returnr 0;
      }
      
      
      //slib.c
      char* name()
      {
          return "Statci Lib";
      }
      
      
      int add(int a,int b)
      {
          return a + b;
      }
      
    • slib.c 文件制作成静态库

      1.编译静态库源码:
      gcc -c slib.c -o slib.o
      
      2.生成静态库文件:
      ar -q slib.a slib.o
      
    • 使用静态库编译:gcc test.c slib.a -o test.out

      Name : Static Lib
      Result : 5
      
    • 如果删除静态库文件,不会影响之前生成的可执行文件 main 的运行

5.3 模块链接——动态链接

  • 可执行程序在运行时才动态加载库进行链接

  • 库的内容不会进入可执行程序当中

  • Linux 下动态库的创建和使用

    • 编译动态库源码:gcc -shared dlib.c -o dlib.so
    • 使用动态库编译:gcc main.c -ldl -o main.out
    • 关键系统调用
      • dlopen :打开动态库文件
      • dlsym :查找动态库中的函数并返回调用地址
      • dlclose :关闭动态库文件
  • 示例

    • Demo

      //test.c
      #include <stdio.h>
      #include <dlfcn.h>
      
      int main()
      {
          //dlopen:打开动态库文件,这里以RTLD_LAZY的方式打开
          void* pdlib = dlopen("./dlib.so",RTLD_LAZY);
          
          //定义两个函数指针
          char* (*pname)(); 
          int (*padd)(int,int);
          
          if(pdlib != NULL){
              //dlsym:查找动态库中的函数并返回调用地址
              pname = dlsym(pdlib,"name");
              padd = dlsym(pdlib,"add");
              
              if((pname != NULL) && (padd != NULL)){
                  printf("Name : %s\n",pname());
                  printf("Result : %d\n",padd(2,3));
              }
              
              //dlclose:关闭动态库文件
              dlclose(pdlib);
          }
          
          else{
              printf("Cannot open lib...\n");
          }
          
          return 0;
      }
      
      //dlib.c
      char* name()
      {
          return "Dynamic Lib";
      }
      
      int add(int a,int b)
      {
          return a + b;
      }
      
    • 编译动态库 dlib.c 源码:gcc -shared dlib.c -o dlib.so

    • 使用动态库编译:gcc test.c -ldl -o test.out

    • 如果删除动态库文件,会影响之前生成的可执行文件 main 的运行,即运行结果为:Cannot open lib...

posted @ 2020-09-01 22:20  nxgy  阅读(105)  评论(0编辑  收藏  举报