5分钟读懂c语言编译步骤

编写的C/C++代码,通过ide生成一个可执行文件,我们只需要点一下编译就可以实现,但实际上,我们点下编译之后,编译器经历了很多步骤,才最终生成了我们需要的软件。

在早期没有ide或者在Linux上我们自己配置的编译环境下,需要自己手动去执行预编译->编译->汇编->链接的步骤,最终生成我们的可执行文件。这有个好处是我们可以更了解c语言的编译原理。

预编译阶段

预编译阶段是将我们的一些预处理指令(头文件、宏定义等)以及注释进行处理。在Linux下的预编译会生成.i文件

gcc -E testCmp.c -o testCmp.i

此步生成的文件内容截取(因为内容太多了)

extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 868 "/usr/include/stdio.h" 3 4
# 2 "testCmp.c" 2
# 3 "testCmp.c"
int main()
{

}

预处理指令指的就是我们在程序中在头部有#标志的,这是c语言的一个重要的知识,之所以叫他预处理指令,就是因为他在预编译阶段就被处理,此阶段会处理掉一部分内容,这部分内容不会进入到我们之后的步骤当中。

有时候我们在程序中会看到#if、#else,#elif、#endif,这些也是预处理指令,我们将编译他的阶段叫做条件编译,意思就是如果不符合#if或者#else或者#elif条件的程序部分,根本就不会编译进最终的可执行软件当中,这样可以大大减小我们的机器代码量。

当然,程序中少不了我们的头文件,预编译阶段会将头文件展开,将其中的内容,插入到源文件中。

还有一些比如宏定义,这个阶段会将宏定义内容替换到源码中

程序中不免有些注释的内容,我们在初学c语言的时候就听说,我们加多少注释都不会影响程序速度,因为预编译阶段会将注释内容删除,在之后的编译步骤中不会出现注释。

扫描二维码关注公众号,回复: 10745975 查看本文章

总结:

        1、头文件展开,内容插入到源文件

        2、宏定义内容替换源码中相应位置

        3、删除注释

        4、条件编译

编译阶段

编译阶段是将之前经过预编译阶段的代码,编译成汇编语言。实际上这一步在网络上有两种说法,一种说法是编译成机器代码,另一种是编译成汇编语言,实际上这两种说法也都没有错,只是一个细分和不细分的问题,我想的是既然要讲,就要细化的讲,我就将编译阶段分为两步来说。在Linux上此阶段会生成.s文件

gcc -S testCmp.i -o testCmp.s

这个阶段会进行一些语法上的词法分析、语法分析、语义分析、性能优化,经过这个阶段,我们的编译器会认识到我们在语法上的一些错误,从而发出警告或者严重的会直接抛出错误,使得编译终止。我们来看这一步生成的文件内容

  .file  "testCmp.c"
  .text
  .globl  main
  .type  main, @function
main:
.LFB0:
  .cfi_startproc
  pushq  %rbp
  .cfi_def_cfa_offset 16
  .cfi_offset 6, -16
  movq  %rsp, %rbp
  .cfi_def_cfa_register 6
  movl  $0, %eax
  popq  %rbp
  .cfi_def_cfa 7, 8
  ret
  .cfi_endproc
.LFE0:
  .size  main, .-main
  .ident  "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
  .section  .note.GNU-stack,"",@progbits

相信电气专业的朋友一定学过微机原理,里面两种处理器8086/88就是使用汇编来写,当时上大学这个头痛,因为汇编实在是不太符合写代码的思想。实际上汇编已经属于相对底层的语言了,现在我们使用最广泛的接近底层的代码就是c语言,本文不会对汇编进行深入剖析,有兴趣的朋友可以在网上了解,不过不太建议深入学,除非真的有需要。

总结:此步骤会检测我们的语法问题,也会进行一些优化处理,最终生成汇编语言文件

汇编阶段

汇编阶段会将我们上一步的汇编语言编译成目标文件。这个文件的内容为机器可以识别的二进制代码,非超底层程序员根本就不会看的懂这部分的代码,因为发明c语言这些可视化语言的目的就是为了防止与二进制代码直接打交道(头痛~)。

此步在windows下生成.obj,在Linux平台下生成.o

gcc -c testCmp.s -o testCmp.o

此步生成的文件是不能通过查看普通文件的方式被查看的,要使用专门的二进制查看软件查看,我这台电脑上没有查看器,所以也就不在这里列出了,朋友们只需要知道是一堆二进制代码就对了。

总结:生成机器可以识别的二进制代码

链接阶段

链接的目的是将一些依赖的库与我们的源文件合成最终生成可执行文件。我们将链接分为静态链接和动态链接,静态链接是依靠一个独立的链接器,将库文件与代码源文件合成生成一个独立的程序,这个有个弊端就是会导致我们的可执行程序很大,因为库文件本身就很大;另一个动态链接,动态链接库文件不会被编译到最终的软件中,而是在可执行软件中生成一个描述信息,我们根据信息去调用外部的动态链接库。

.lib和.a分别是windows和Linux下的静态链接库拓展名,.dll和.so分别是windows和Linux下的动态链接库拓展名。

Linux下执行生成最终目标文件

gcc testCmp.o -o testCmp

总结:此步分别使用动态链接库和静态链接库支持我们的软件,并生成最终软件

发布了45 篇原创文章 · 获赞 63 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Groot_Lee/article/details/104884586
今日推荐