欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言:探究Linux环境下编译C程序的整个过程

程序员文章站 2022-06-04 13:18:25
...

 编译一个C程序可以分为四个过程:预处理,编译,汇编,链接。编写一个程序逐步了解整个过程:

// file: test.c
#include <stdio.h>

#define DEBUG printf

int add(int a, int b)
{
        return (a+b);
}


int main()
{
        int val = add(1, 2);
        DEBUG("val = %d\n", val);

        return 0;
}

.

预处理

 将带有#的头文件等预处理命令替换成真正的内容,这会导致得到的文件体积增大。通常使用命令gcc -E test.c -o test.i或者cpp test.c -o test.i输出.i文件。打开文件可以看到头文件被替换成一系列的代码,再看我们自己定义的宏DEBUG已经被替换成printf了:

...	// 省略以上头文件的内容
int main()
{
 int val = add(1, 2);
 printf("val = %d\n", val);

 return 0;
}

.

编译

 将预处理得到的文件进行翻译成汇编代码。C语言是一门高级语言,需要转换成汇编代码,但是汇编语言也还不是机器能识别的语言,机器能能识别的是0101这种二进制可执行程序,不急,现在才第2个步骤。编译阶段只是将它先翻译成汇编语言,可以使用命令gcc -S test.i -o test.sgcc -S test.c -o test.s编译后查看.s汇编文件:

...// 省略
main:
.LFB1:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    $2, %esi
        movl    $1, %edi
        call    add
        movl    %eax, -4(%rbp)
        movl    $.LC0, %eax
        movl    -4(%rbp), %edx
        movl    %edx, %esi
        movq    %rax, %rdi
        movl    $0, %eax
        call    printf
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
...// 省略

.

汇编

 前面说了汇编语言还不是机器能识别的代码,需要进一步处理成机器能识别的二进制代码即机器码。通常使用命令gcc -c test.c -o test.ogcc -c test.s -o test.oas test.s -o test.o生成.o二进制文件。注意,在汇编这一步之前的文件都是文本文件而不是二进制文件,所以如果现在打开生成的文件看到的都是一堆乱码。
.

链接

 前面说汇编之后生成了机器能识别的二进制代码了,但是这里还不能直接运行。简单点讲就是,程序目前还只是一个类似于框架的东西,那些printf函数你的文件里面没有实现,包含头文件只是这些函数的声明而已,知道这个东西,但实际上还没拥有。所以,需要把那些别人写好的函数原型“串”(链接)起来才可以实现功能。嗯,链接就是实现这个功能。
 链接还分为两种链接方式:静态链接与动态链接。(可以参考一些库的制作,一起搅拌一下可能会更香:https://blog.csdn.net/weixin_44498318/article/details/105597170
 链接通常使用命令gcc test.o -o test或ld的相关命令即可将标准库的文件链接进来,另外还可以使用-l、-I和-L来静态或动态链接一些库。将得到的可执行文件执行命令./test即可
.

总结一下

 事实上,我们平时编译一个程序的时候并不需要分开这么多个步骤,直接执行命令gcc test.c -o test即可得到可执行文件。将编译过程分为4个步骤有一个好处就是:如果修改其中一个文件的一个地方,就不需要每个文件都进行这四个步骤,只编译刚才修改的那个文件,到时直接进行链接即可,有利于提高编译效率,这点在编写大项目中的Makefile中可以发挥优势。

相关标签: C语言