C代码编译成可执行程序的过程

C代码通过编译器编译成可执行代码,经历了四个阶段,依次为:预处理、编译、汇编、链接。

接下来详细讲解各个阶段

一、预处理

1、任务:进行宏定义展开、头文件展开、条件编译,不检查语法。

2、命令:gcc -E [源文件]  -o [预处理文件]

3、案例:用gcc编译器预处理demo1.c代码,预处理后的文本放到demo1.i中。(gcc -E demo1.c -o demo1.i)

demo1.c代码如下:

 1 #include <stdio.h>
 2
 3 #define add(a, b) (a + b)
 4 #define sub(a, b) (a - b)
 5
 6 int main(void)
 7 {
 8     int a, b, c, d;
 9 #ifndef __cplusplus
10     a = b = c = d = 1;
11 #else
12     a = b = c = d = 2;
13 #endif
14     printf("num = %d\n", sub(add(a, b), add(c, d)));
15     return 0;
16 }

demo1.c

生成的demo1.i代码如下:

 1 # 1 "demo.c"
 2 # 1 "<command-line>"
 3 # 1 "/usr/include/stdc-predef.h" 1 3 4
 4 # 1 "<command-line>" 2
 5
 6 此处省略800行...
 7
 8 extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
 9 # 943 "/usr/include/stdio.h" 3 4
10
11 # 2 "demo.c" 2
12
13
14 int main(void)
15 {
16     int a, b, c, d;
17
18     a = b = c = d = 1;
19
20
21
22     printf("num = %d\n", ((a + b) - (c + d)));
23     return 0;
24 }

demo1.i

通过案例可以发现:#define宏定义、stdio.h头文件、#ifdef条件编译都被替换了,并且你还可以故意写一句有语法错误的代码,但是却不会报错。由于stdio.h头文件长达800多行,因此在demo1.i中只截取开头和结尾的几行。

    

二、编译

1、任务:检查语法,将预处理过的文件编译生成汇编文件。

2、命令:gcc -S [源文件] -o [汇编文件]

3、案例;用gcc编译器编译demo2.c代码,编译后的汇编代码放到demo2.s中。(gcc -S demo2.c  -o demo2.s)

demo2.c代码如下:

1 #include <stdio.h>
2
3 int main(int argc, char *argv[])
4 {
5     printf("hello world\n");
6     return 0;
7 }

demo2.c

生成的demo2.s代码如下:

 1     .file    "demo2.c"
 2     .section    .rodata
 3 .LC0:
 4     .string    "hello world"
 5     .text
 6     .globl    main
 7     .type    main, @function
 8 main:
 9 .LFB0:
10     .cfi_startproc
11     pushq    %rbp
12     .cfi_def_cfa_offset 16
13     .cfi_offset 6, -16
14     movq    %rsp, %rbp
15     .cfi_def_cfa_register 6
16     subq    $16, %rsp
17     movl    %edi, -4(%rbp)
18     movq    %rsi, -16(%rbp)
19     movl    $.LC0, %edi
20     call    puts
21     movl    $0, %eax
22     leave
23     .cfi_def_cfa 7, 8
24     ret
25     .cfi_endproc
26 .LFE0:
27     .size    main, .-main
28     .ident    "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
29     .section    .note.GNU-stack,"",@progbits

demo.s

通过案例可以看出,我们写的c代码被编译成了汇编代码。故意写错一个语法点,编译器将报错。

三、汇编

1、任务:将汇编文件生成目标文件(2进制文件)。

2、命令:gcc -s [源文件] -o [目标文件]

3、案例:用gcc编译器汇编demo3.c代码,编译后的二进制代码放到demo3.o中。(gcc -c demo3.c  -o demo3.o)

demo3.c代码如下:

1 #include <stdio.h>
2
3 int main(int argc, char *argv[])
4 {
5     printf("hello world\n");
6     return 0;
7 }

demo3.c

生成的demo3.o代码如下:

通过汇编阶段,文本代码变成了二进制代码,也就是计算机可以识别的代码。c语言中,二进制代码文件是以.o为后缀名的。

四、链接

1、任务:找到依赖的库文件,将目标文件链接为可执行程序。

2、命令:gcc -c [目标文件] -o [可执行程序] -l[动态库名]

3、案例:通过gcc编译器让demo4链接自己制作的libadd.so动态库,并把demo4编译成可执行程序。gcc demo4.c -o demo4 -L./ -ladd

demo4.c代码如下:

1 #include <stdio.h>
2 #include "add.h"
3
4 int main(int argc, char *argv[])
5 {
6     printf("add = %d\n", add(1, 1));
7     return 0;
8 }

demo4.c

通过file命令查看可执行程序的信息:

运行结果:add = 2

还可以通过“size [可执行程序]”命令,来查看程序的text段、data段、bss段的大小。

在程序还没运行前,我们是可以确定它的text段、data段、bss段的大小。

时间: 2024-10-27 08:12:04

C代码编译成可执行程序的过程的相关文章

文章阅读:计算机体系-计算机将代码编译和持续运行过程中需要考虑的问题,以及具体的实现原理讲解

文章太棒,我无法理解和评价,备份一下. 1.编程漫游 - Mr.Riddler's Puzzle http://blog.mrriddler.com/2016/12/15/%E7%BC%96%E7%A8%8B%E6%BC%AB%E6%B8%B8/ 注:讲了什么是函数,什么是数据.太抽象了,不过看了之后有很深的感触,还有不懂的感触. 2.计算机体系-编译体系漫游 - Mr.Riddler's Puzzle http://blog.mrriddler.com/2017/02/10/%E8%AE%A1

将python代码编译成.so文件

https://moonlet.gitbooks.io/cython-document-zh_cn/content/ch1-basic_tutorial.html add_num.pyx文件 def add_nums(ls): total = 0. for l in ls: total += l return total set_up.py from distutils.core import setup from Cython.Build import cythonize setup( ext

python如何调用c编译好可执行程序

    以下总结出几种在Python 中调用 C/C++ 代码的方法 -------------------------------------------------------------------- 发现做便捷的是使用popen from os import popen popen('/home/pengyan/Downloads/VIC/sanxia/vicNl -g /home/pengyan/Downloads/VIC/sanxia/xings_global') popen('/h

【CLR via C#】CSC将源代码编译成托管模块

下图展示了编译源代码文件的过程.如图所示,可用支持 CLR 的任何一种语言创建源代码文件.然后,用一个对应的编译器检查语法和分析源代码.无论选用哪一个编译器,结果都是一个托管模块(managedmodule).托管模块是一个标准的 32 位 Microsoft Windows 可移植执行体(PE32)文件 6 ,或者是一个标准的 64 位Windows 可移植执行体(PE32+)文件,它们都需要 CLR 才能执行.顺便说一句,托管的程序集总是利用了 Windows 的数据执行保护(Data Ex

Java前端编译:Java源代码编译成Class文件的过程

Java前端编译:Java源代码编译成Class文件的过程 在上篇文章<Java三种编译方式:前端编译 JIT编译 AOT编译>中了解到了它们各有什么优点和缺点,以及前端编译+JIT编译方式的运作过程. 下面我们详细了解Java前端编译:Java源代码编译成Class文件的过程:我们从官方JDK提供的前端编译器javac入手,用javac编译一些测试程序,调试跟踪javac源码,看看javac整个编译过程是如何实现的. 1.javac编译器 1-1.javac源码与调试 javac编译器是官方

代码编译过程

从上大学开始学习编程至今,十年有余,这次头一回尝试总结一下平时最常见的工作的在计算机中的本质. 从源代码到可执行文件,正常需要经过"预处理器(Preprocessor)"->"编译器(Compiler)"->"链接器(Linker)"这三个过程.如果是嵌入式系统,还需要经过"加载器(Loader)"加载到目标机器的存储器中才能运行. 现在逐个解释每个过程的含义: 预编译器(Preprocessor) 不同的预处理器

Linux | GCC如何实现代码编译&amp;&amp;汇编&amp;&amp;链接过程

  正文: 每次我们程序员所写的 代码 是给程序员看的呢?还是给电脑看的?其实我们所写的代码只是我们程序员之间交流的一样特殊语言,电脑是看不懂的.那么我们如何实现人机交流呢?这就不得不请出我们我们今天的主角人机交换的桥梁---编译器,编译器有很多,我们今天介绍的是一种基于 Linux 系统的 GCC 编译器,以下代码都是在 Linux 系统上 VI 编辑器代码 GCC 编译代码实现的. 1.GCC概述 通常我们所写的代码无论 C \ C++ 等一些高级语言其实电脑是识别不了的.代码只是我们程序员

C代码通过编译器编译成可执行文件, 需经历 预处理、编译、汇编、链接 四个阶段

内容借鉴 于yqzheng 一.预处理 1.任务: 进行宏定义展开.头文件展开.条件编译, 不检查语法 2.命令: gcc -E [源文件] -o [预处理文件] gcc -E hello.c -o hello.i 二.编译 1.任务: 将预处理文件 编译成 汇编文件 检查语法 2.命令: gcc -S [源文件] -o [汇编文件] gcc -S hello.c -o hello.s 三.汇编 1.任务: 将汇编文件 生成 目标文件 2.命令: gcc -s [汇编文件] -o [目标文件]

源代码 到 可执行程序的过程

编译,编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格式的要求链接生成可执行程序.源代码-->预处理-->编译-->优化-->汇编-->链接-->可执行文件Source--(编译)--> Assembly--(汇编)-->Obj--(链接)-->PE/ELF1.编译预处理读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理[析] 伪指令