美文网首页
程序编译链接(一)

程序编译链接(一)

作者: wayyyy | 来源:发表于2018-10-02 18:19 被阅读0次

一个C程序最后生成可执行目标文件,会分阶段经过预处理编译汇编链接的过程,而往往整个过程由IDE提供的编译驱动程序, 全权代表用户调用预处理器,编译器,汇编器和链接器。

编译链接.png

预处理

#include <stdio.h>
int main()
{
    printf("hello world\n");
}

可以使用gcc -E hello.c -o hello.i或者cpp hello.c > hello.i命令将 hello.c 文件被预编译成一个hello.i 文件。
一般说,预处理器将会做以下事情:

  • 去掉注释
  • 展开所有宏定义
  • 处理#include预编译指令,复制头文件,这个过程是递归进行的。
  • 处理所有的条件预编译指令:#if #ifdef #elif #else #endif
  • 保留所有#pragma编译器指令

编译

可以使用gcc -S hello.i -o hello.s将预处理后的文件变为汇编文件。
编译过程就是把预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件。编译器过程非常复杂,这里不作详细阐述。

  • 词法分析

    array[index] = (index + 4) * (2 + 6)
    

    源代码程序被输入到扫描器,扫描器的任务很简单,它只是简单的进行词法分析,运用一种有限状态机的算法将源代码的字符序列分割成一系列的记号。

    词法分析的记号一般分为:关键字,标识符,字面量和特殊符号。

  • 语法分析
    语法分析器将对由扫描器产生的记号进行语法分析,从而产生语法树。由语法分析器产生的语法树是以表达式为节点的树。

  • 语义分析
    语法分析仅仅是完成了对表达式的语法层面的分析,但是它并不能了解这个语句是否真正的意义。比如:C语言中两个指针做减法是没有意义的,但是这个语句在语法上是合法的。
    编译器所能分析的是静态语义,所谓静态语义是指在编译器可以确定的语义,与之对应的是动态语义,指只有在运行期才能确定的语义。

汇编

可以使用gcc -c hello.s -o hello.o 或者as hello.s -o hello.o,将汇编语言文件生成目标文件。as是汇编器,它的作用是直接将汇编代码转变成机器指令,每一个汇编语句几乎都对应一条机器指令。


链接

  • 为什么需要链接?
    在一个程序被分割成多个模块之后,这些模块之间如何组合形成一个单一的程序是必须解决的问题,模块之间如何组合的问题可以归结为模块之间如何通信的问题。最常见的属于静态语言的C/C++模块之间通信的两种方式:

    • 模块间的函数调用
    • 另外一种是模块间的变量访问。

    函数访问必须知道目标函数的地址,变量访问必须知道目标变量的地址。所以两种方式都可以归结为一种方式:那就是模块间符号的引用。完成模块间符号引用的过程就是链接。链接的主要过程包括地址和空间分配符号决议重定位

  • 对于链接时出现的"usr/bin/ld: cannot find -lxxx 的错误"
    比如这种,"usr/bin/ld: cannot find -labc",首先要知道,这里labc表示的是libabc.so这个动态库。

    出现这种情况:

    • 第一种是你确实没有安装这个动态库,这个需要你自己安装。
    • 第二种情况是:动态库放置位置不对。
      系统识别动态库路径配置在etc/ld.so.conf文件中。
      cat /etc/ld.so.conf
        include /etc/ld.so.conf.d/*.conf
      cat /etc/ld.so.conf.d/*.conf
        # libc default configuration
        /usr/local/lib
        /usr/lib
        # Multiarch support
        /lib/x86_64-linux-gnu
        /usr/lib/x86_64-linux-gnu
      
      所以,当gcc编译链接时没有显示指定目录,那么链接器将会默认搜索:/usr/local/lib/usr/lib/lib/x86_64-linux-gnu/usr/lib/x86_64-linux-gnu
      而你的位置放置的不对,这时可以手动拷贝至上面的目录中,
      或者建立符号链接:
      ln -s 这个库的路径/libabc.so /usr/lib/libabc.so
      

    手动拷贝至上面目录或者建立符号链接之后,需要用ldconfig 刷新缓存,可以用ldconfig -p查看动态库列表,是否已经更新。

附:gcc 编译选项

文档:https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/

功能 命令
预处理 gcc -E hello.c -o hello.i
编译(生成汇编) gcc -S hello.i -o hello.s
汇编(机器指令) gcc -c hello.s -o hello.o
指定源码目录 -I(大写的i),多个源码目录,每一个都需要在前面指定-I
链接动态库 -l (小写的L)放在/usr/lib等目录里的库直接用参数就能链接了
-L参数跟着的是库文件所在的目录名(比如/home/libabc.so,需要用:-L/home -labc (一般的在-L的后面可以一次用-l指定多个库文件)

参开资料
1.《程序员的自我修养》

  1. https://www.cnblogs.com/yongy1030/p/10367070.html

相关文章

  • 程序编译链接(一)

    一个C程序最后生成可执行目标文件,会分阶段经过预处理, 编译,汇编 和 链接的过程,而往往整个过程由IDE提供的编...

  • 程序编译链接

    问:程序链接过程是把不同的程序段链接起来,是不是因为有的函数是用单独的头文件写着,所以需要include,链接的过...

  • Linux主程序子程序编译,链接; makefile编写和tar

    主程序子程序的编译和链接 主程序 hello.c 子程序 thanks.c 编译 -c 是免去链接,否则主程序调用...

  • 程序的运行过程

    GCC只是完成编译工作的驱动程序,它会根据编译流程分别调用预处理程序、编译程序、汇编程序、链接程序来完成具体工作。...

  • 存储器管理

    程序运行分为三个阶段,编译,链接,装入。编译:将程序分为若干个目标块链接:将库函数以及各个目标块链接成一个整体装入...

  • Linux 编译和链接程序

    Linux 编译和链接程序 使用编译器gcc g++Linux可执行程序编译步骤1、编译c/cpp文件 ->.o2...

  • 程序编译链接(四)-- 静态链接

    对于链接器来说,整个链接的过程,就是将几个输入目标文件加工后合并成一个输出文件。那么对于多个输入的文件,链接器是如...

  • 程序编译链接(五)-- 动态链接

    为什么需要动态链接? 静态链接的方法很简单,在早期,绝大部分系统采用这种方案。随着计算机软件的发展,这种方案的缺点...

  • 第四章 1程序编译连接

    1、程序的装入和链接 程序进内存的一般过程:a.编译compiler:编译程序:将用户源代码编译成若干个目标模块。...

  • 基础知识

    程序的生成过程:预处理:#define,include编译:生成汇编语言程序汇编:生成机器代码链接:动态链接(程序...

网友评论

      本文标题:程序编译链接(一)

      本文链接:https://www.haomeiwen.com/subject/foqriftx.html