• 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
        ",name());
            printf("Result : %d
        ",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
        ",pname());
                    printf("Result : %d
        ",padd(2,3));
                }
                
                //dlclose:关闭动态库文件
                dlclose(pdlib);
            }
            
            else{
                printf("Cannot open lib...
        ");
            }
            
            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...

  • 相关阅读:
    在 LR 中如何解决Socket 接收数据的验证
    UE 的文件比较方法
    使用plSQL连接Oracle报错,SQL*Net not properly installed和TNS:无法解析指定的连接标识符
    plsql developer连接oracle数据库
    将列表中的字符以‘*’连接生成一个新的字符串
    ElasticSearch之CURL操作
    MySQL 5.7.21 免安装版配置教程
    C# IL DASM 使用-破解c#软件方法
    For-each Loop,Index++ Loop , Iterator 那个效率更高
    10种简单的Java性能优化
  • 原文地址:https://www.cnblogs.com/bky-hbq/p/13599092.html
Copyright © 2020-2023  润新知