精华内容
下载资源
问答
  • C语言编译流程

    2020-10-23 12:34:41
    本文以Linux下C语言编译过程为例,讲解C语言程序的编译过程。 编写hello world C程序: // hello.c #include <stdio.h> int main(){ printf("hello world!\n"); } 编译过程只需: gcc hello.c # 编译 ./a....

    C语言程序从源代码到二进制行程序都经历了那些过程?本文以Linux下C语言的编译过程为例,讲解C语言程序的编译过程。

    编写hello world C程序:

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

    编译过程只需:

    gcc hello.c # 编译
    ./a.out # 执行
    

    便可输出hello world!

    这个过程如此熟悉,以至于大家觉得编译事件很简单的事。事实真的如此吗?我们来细看一下C语言的编译过程到底是怎样的。

    上述gcc命令其实依次执行了四步操作:1.预处理(Preprocessing), 2.编译(Compilation), 3.汇编(Assemble), 4.链接(Linking)。

    在这里插入图片描述
    为了下面步骤讲解的方便,我们需要一个稍微复杂一点的例子。假设我们自己定义了一个头文件mymath.h,实现一些自己的数学函数,并把具体实现放在mymath.c当中。然后写一个test.c程序使用这些函数。程序目录结构如下:

    ├── test.c
    └── inc
        ├── mymath.h
        └── mymath.c
    

    程序代码如下:

    // test.c
    #include <stdio.h>
    #include "mymath.h"// 自定义头文件
    int main(){
        int a = 2;
        int b = 3;
        int sum = add(a, b); 
        printf("a=%d, b=%d, a+b=%d\n", a, b, sum);
    }
    

    头文件定义:

    // mymath.h
    #ifndef MYMATH_H
    #define MYMATH_H
    int add(int a, int b);
    int sum(int a, int b);
    #endif
    

    头文件实现:

    // mymath.c
    int add(int a, int b){
    	return a+b;
    }
    int sub(int a, int b){
    	return a-b;
    }
    

    1.预处理(Preprocessing)

    预处理用于将所有的#include头文件以及宏定义替换成其真正的内容,预处理之后得到的仍然是文本文件,但文件体积会大很多。gcc的预处理是预处理器cpp来完成的,你可以通过如下命令对test.c进行预处理:

    gcc -E -I./inc test.c -o test.i
    

    或者直接调用cpp命令

    cpp test.c -I./inc -o test.i
    

    上述命令中-E是让编译器在预处理之后就退出,不进行后续编译过程;-I指定头文件目录,这里指定的是我们自定义的头文件目录;-o指定输出文件名。

    经过预处理之后代码体积会大很多:

    X文件名文件大小代码行数
    预处理前test.c146B9
    预处理前test.i17691B857

    预处理之后的程序还是文本,可以用文本编辑器打开。

    2.编译(Compilation)

    这里的编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程。编译的指定如下:

    gcc -S -I./inc test.c -o test.s
    

    上述命令中-S让编译器在编译之后停止,不进行后续过程。编译过程完成后,将生成程序的汇编代码test.s,这也是文本文件,内容如下:

    // test.c汇编之后的结果test.s
        .file   "test.c"
        .section    .rodata
    .LC0:
        .string "a=%d, b=%d, a+b=%d\n"
        .text
        .globl  main
        .type   main, @function
    main:
    .LFB0:
        .cfi_startproc
        pushl   %ebp
        .cfi_def_cfa_offset 8
        .cfi_offset 5, -8
        movl    %esp, %ebp
        .cfi_def_cfa_register 5
        andl    $-16, %esp
        subl    $32, %esp
        movl    $2, 20(%esp)
        movl    $3, 24(%esp)
        movl    24(%esp), %eax
        movl    %eax, 4(%esp)
        movl    20(%esp), %eax
        movl    %eax, (%esp)
        call    add 
        movl    %eax, 28(%esp)
        movl    28(%esp), %eax
        movl    %eax, 12(%esp)
        movl    24(%esp), %eax
        movl    %eax, 8(%esp)
        movl    20(%esp), %eax
        movl    %eax, 4(%esp)
        movl    $.LC0, (%esp)
        call    printf
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret 
        .cfi_endproc
    .LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
        .section    .note.GNU-stack,"",@progbits
    

    3.汇编(Assemble)

    汇编过程将上一步的汇编代码转换成机器码(machine code),这一步产生的文件叫做目标文件,是二进制格式。gcc汇编过程通过as命令完成:

    as test.s -o test.o
    
    gcc -c test.s -o test.o
    

    这一步会为每一个源文件产生一个目标文件。因此mymath.c也需要产生一个mymath.o文件

    4.链接(Linking)

    链接过程将多个目标文以及所需的库文件(.so等)链接成最终的可执行文件(executable file)

    命令大致如下:

    ld -o test.out test.o inc/mymath.o ...libraries...
    

    经过以上分析,我们发现编译过程并不像想象的那么简单,而是要经过预处理、编译、汇编、链接。尽管我们平时使用gcc命令的时候没有关心中间结果,但每次程序的编译都少不了这几个步骤。

    展开全文
  • c语言编译流程简单整理以及简单makefil编写 前言: 最近接触了makefil。为了学习makefil,就去了解了部分需要的知识,目前先简单的整理在这里。 c语言编译过程 C语言在编译过程中一共有四个步骤,通过这个四个步骤,...

    c语言编译流程简单整理以及简单makefile编写

    前言: 最近接触了makefile。为了学习makefile,就去了解了部分需要的知识,目前先简单的整理在这里。
    

    c语言编译过程

    C语言在编译过程中一共有四个步骤,通过这个四个步骤,才能形成正常可执行的程序。
    gcc编译过程

    预处理

    首先需要做的就是预处理,预处理顾名思义,预先处理。预处理会将代码中宏定义以及一些其他的东西替换为原本的代码。

    $ gcc -E main.c -o main.i
    

    通过gcc的-E命令可以对.c做预处理,-o是预处理后生成的文件名。生成的.i文件和原本的.c文件相差不是很大,还是c语言的格式,如果打开一个.i文件还是可以看的懂的。

    编译

    编译就会将整个代码翻译成对应的汇编文件,也就是汇编的代码。

    gcc -S main.i
    

    调用gcc的-S命令可以将.i文件进行编译,编译完会产生对应.i文件名字的.s文件,这时候的代码就是对应机器的汇编代码了。

    汇编

    通过汇编,代码就会转变成对应的二进制代码。

    gcc -C main.s
    

    调用gcc的-C命令可以将.s文件进行编译,编译完会产生对应.o文件名字的.s文件,这时候的文件里面的内容就完全是二进制的代码了。

    链接

    经过上面的步骤,生成的.o文件还不能正常执行,在.c代码开头包含的头文件啊什么的,还没被加载进来,头文件都已经经过编译了,这时候就需要进行链接,来做最后的组装工作,只有正常的组装后,才可以正常执行。

    gcc main.o -o main
    

    通过上面这行命令,就可以生成main文件了,如果不指定名字,默认输出a.out。main文件是和a.out是一样的。

    简单的makefile编写

    了解了基本的编译过程后,单个文件还好说,可以敲几行命令行,可以文件多起来了,这个时候就不好说了,这个时候就需要找到一个解放双手的脚本。
    makefile的作用就是对整个工程文件配置编译过程的文件,相当于工程编译配置。通过这个文件,可以快速的编译整个工程,而不需要每次都写那么长的命令。
    下面的是简单makefile的写法

    基本语法

    makefile的基本语法是由三部分组成的

    目标:依赖
        命令
    

    目标

    目标是命令执行后生成的文件,命令是否执行取决于命令的依赖项是否被改动,如果没改动,就不会执行。

    依赖

    目标生成时需要的依赖文件。

    命令

    命令就是生成目标文件时所需要执行的命令。

    编译一个程序的时候,我们可以这么写

    main.out:main.c
        gcc main.c -o main.out
    

    这样的话,执行make时就可以生成可执行文件main.out。
    这就是makefile的最基本使用。

    两个常用函数

    当原文件多了时,我们当然不希望一次性输入大量的原文件名字。makefile提供两个函数来帮我们简化这个过程。

    1. wildcard
    SRC = $(wildcard ./*.c)
    

    我们可以将所有文件赋值给一个变量,上面的函数会将当前目录下所有后缀为.c的文件赋值给SRC。
    2. patsubst

    OBJ = $(patsubst %.c, %.o, $(SRC))
    

    上面这行代码的意思是将SRC中的.c替换为.o赋值给OBJ。

    有了上面两个函数,我们就可以这样写了。

    SRC = $(wildcard ./*.c)
    OBJ = $(patsubst %.c, %.o, $(SRC))
    
    $(OBJ):$(SRC)
        gcc -C $(SRC) -o $(OBJ)
    main.out:$(OBJ)
        gcc $(OBJ) -o main.out
    

    这里还有一些常用变量可以简化代码

    1. $@:表示目标
    2. $<:依赖中第一个条件
    3. $^:依赖中所有文件

    有了上面这些变量后,就可以简化成下面这样了。

    SRC = $(wildcard ./*.c)
    OBJ = $(patsubst %.c, %.o, $(SRC))
    
    $(OBJ):$(SRC)
        gcc -C $^ -o $@
    main.out:$(OBJ)
        gcc $^ -o $@
    
    THE END:
        目前先整理这样,等继续学习后再来完善makefile部分的语法。
        个人博客地址:https://www.limesea.world/
    
    展开全文
  • 流程: 预处理:展开头文件/宏替换/去掉注释/条件编译 (test.i main .i) 编译:检查语法,生成汇编 (test.s main .s) 汇编:汇编代码转换机器码 ...

    流程:

    1. 预处理:展开头文件/宏替换/去掉注释/条件编译                       (test.i main .i)

    2. 编译:检查语法,生成汇编                                                      ( test.s  main .s)

    3. 汇编:汇编代码转换机器码                                                        (test.o main.o)

    4. 链接:链接到一起生成可执行程序                                              a.out

     

     

    1. 预处理

    预编译过程主要做4件事:

    1)展开头文件

    在写有#include <filename>或#include "filename"的文件中,将文件filename展开,通俗来说就是将fiename文件中的代码写入到当前文件中;

    2)宏替换

    3)去掉注释

    4)条件编译

     

    2. 编译

    编译是读取源程序(字符流),对之进行词法、语法和语义的分析,将高级语言指令转换为功能等效的汇编代码。

    1)词法分析

    识别单词并分类。

    词法分析器读入组成源程序的字符流,并将其组成有意义的词素的序列。形如<token-name, attribute-value>这样的词法单元。

    词法分析的输入是源程序,输出是识别出的记号流.目的是识别单词. 至少分以下几类:关键字(保留字)、标识符、字面量、特殊符号

    2)语法分析

    组词成句及语法错误检查

    语法分析器使用由词法分析器生成的各词法单元的第一个分量来创建树形的中间表示。该中间表示给出了词法分析产生的词法单元的语法结构。常用的表示方法是语法树,树中每个内部节点表示一个运算,而该节点的子节点表示运算的分量。

    输入是词法分析器返回的记号流,输出是语法树.目的是得到语言结构并以树的形式表示.对于声明性语句,进行符号表的查填,对于可执行语句,检查结构合理的表达式运算是否有意义.

    3)语义分析

    分析各种语法成分的语义特征

    语义分析器使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致 。它同时收集类型信息,并存放在语法树或符号表中,以便在中间代码生成过程使用。

    语义分析的一个重要部分就是类型检查。比如很多语言要求数组下标必须为整数,如果使用浮点数作为下标,编译器就必须报错。再比如,很多语言允许某些类型转换,称为自动类型转换。

    根据语义规则对语法树中的语法单元进行静态语义检查,如类型检查和转换等,目的在于保证语法正确的结构在语义分析上也是合法的.

    4)生成中间代码(可选)

    生成一种既接近目标语言,又与具体机器无关的表示,便于代码优化与代码生成.

    5)优化中间代码(可选)

    局部优化、循环优化、全局优化等;优化实际上是一个等价变换,变换前后的指令序列完成同样的功能,但在占用的空间上和程序执行的时间上都更省、更有效

    6)生成汇编代码

     

     

     

    3. 汇编

    汇编实际上指把汇编语言代码翻译成目标机器指令的过程,生成目标文件。

    目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段:

    代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。

    数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

     

     4. 链接

    由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。

    例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。

    链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。

     

    根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:

    (1)静态链接

    在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

    (2) 动态链接

    在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

     

    对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

     

     

     

     

     

    展开全文
  • c语言编译流程(c语言是如何变成可执行文件的) 解释:源代码会经过预编译--->编译---->汇编----->连接几个步骤最终生成可执行文件.1.预编译,把源代码中的宏展开并把包含的文件的源代码插入程序的源代码中.2.编译,把源...

    c语言编译流程(c语言是如何变成可执行文件的) 

    解释:
    源代码会经过预编译--->编译---->汇编----->连接几个步骤最终生成可执行文件.

    1.预编译,把源代码中的宏展开并把包含的文件的源代码插入程序的源代码中.
    2.编译,把源代码编译成二进制的目标文件.但是此时目标文件还是不可执行的.
    3.汇编,这一步主要是处理源代码中的汇编代码
    4.连接,把目标代码和二进制的库文件以及其它内容合并成最终的可执行文件.

     

     

    展开全文
  • 一种c语言编译效率优化的方法,其特征在于,包括如下的步骤:s1、获取每个节点中的节点信息,对节点信息进行保存;S2、根据节点中指令的引用关系,找出每个节点的前置节点和引用节点,S3、获取位于第一个节点的所有...
  • APK编译流程-DEX文件DEX 文件是什么我们知道,虽然我们的应用程序一般由java编写,但最终由Dalivk虚拟机执行的并不是java字节码,而是dalivk字节码。class字节码最终被编译成class文件,而dalivk 字节码最终被优化...
  • C语言编译流程

    2020-12-22 15:26:40
    C语言编译流程 1、预编译 把所有的宏展开 如 #define LED p0^0 LED = 1; 这一步就是将LED替换成p0^0 宏定义可以使得我们代码的可阅读性变强,一定要多用宏定义 处理条件编译 #ifdef SENYUN #ifden HIKCAM #end if ...
  • C语言编译

    2018-05-05 08:35:30
    这一篇文章我们来聊一聊C语言编译过程,就是从 .c 文件到 .exe 文件的过程。关于C语言编译过程,对很多人来说都感到特别苦涩,但是理解了这个过程对大家以后的能力上的提升是很有帮助的,在这里我会努力用最浅显...
  • C语言程序编译流程

    2019-10-27 14:22:33
    编译预处理->编译->汇编->链接 编译预处理: gcc -E hello.c -o hello.i (将头文件里的库函数插入进去...编译: gcc -Shello.i -o hello.s ( 将C语言代码翻译程汇编语言) 汇编: gcc -c hello.s -o hello.o ...
  • 虽然电脑已经很普遍了,但是一些年长的人对电脑的操作不是很熟悉,比如在使用win7系统时一旦遇到c语言编译时就懵了,对于c语言编译处理起来相对来说较简单,按照我们的步骤处理c语言编译很容易上手,c语言编译具体...
  • 我们的代码会经过这4个环节,从而形成最终文件,c语言作为编译语言,用来向计算机发出指令。让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所应当采取的行动。 预处理, 展开头文件/宏...
  • 东北大学编译原理课设,C语言编译器,C语言文法流程图。 东北大学编译原理课设:C语言编译器,绘制的C语言文法流程图。供学弟学妹们参考,希望对你们有所帮助。
  • C语言编译过程 -E 预编译 -S 编译 -C 汇编 链接LinkingC语言编译过程gcc的编译流程分为四个步骤,分别为:・ 预编译(Pre-Processing) ・ 编译(Compiling) ・ 汇编(Assembling) ・ 链接(Linking)-E 预编译...
  • 创建framework 1. File/new/project,选择cocoa touch ...3. 编写c语言程序和头文件。 4. 在“工程名.h”里加入你的.h文件。#import&lt;"你的工程/xxx.h"&gt; 5. 在build phases/headers里...
  • 教详细的讲述了整个代码的编译流程,适合新手,老手学习。有流程图描述各个阶段代码的整个编译流程。非常不错。
  • Ubuntu的C语言编译

    2020-09-25 21:02:55
    Ubuntu的C语言编译vim的安装gcc的安装gcc的编译测试合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右...
  • C语言编译过程

    千次阅读 2019-07-08 11:20:02
    从一个源文件(.c)到可执行程序到底经历了哪几步,我想大多数的人...其实总的流程是这样的。 【第一步】编辑hello.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 int main() 4 { 5 printf("hel...
  • Linux-C C语言编译过程

    千次阅读 2018-07-18 20:32:55
    Linux-C C语言编译过程 一、简述  GCC(GNU Compiler Collection,即 GNU 编译器套装),是一套由 GNU 开发的编程 语言编译器。简单介绍使用gcc编译器将hello.c文件编译成为hello可执行文件的过程。 在伪...
  • C语言编译过程流程

    2020-12-11 16:10:48
  • C语言编译和链接过程简介

    万次阅读 2018-05-27 16:56:58
    C语言编译和链接过程简介一、编译过程简介 编译过程可以分为4部分内容组成 预处理器-&gt;编译器-&gt;汇编器-&gt;链接器 1、预处理器 (1)、处理所有的注释,以空格代替 (2)、讲所有的#define删除,...
  • C语言编译过程详解

    千次阅读 2018-12-07 00:01:08
      前言 C语言程序从源代码到二进制行程序都经历了...本文以Linux下C语言编译过程为例,讲解C语言程序的编译过程。 编写hello world C程序: // hello.c #include &lt;stdio.h&gt; int main(){ p...
  • 我们大部分程序员可能都是从C语言学起的,写过几万行、几十万行、甚至上百万行的代码,但是大家是否都清楚C语言编译的完整过程呢,如果不清楚的话,我今天就带着大家一起来做个解密吧。 C语言相对于汇编语言是一...
  • 1.1 C语言编译过程,gcc参数简介1.1.1 C语言编译过程一、gcc - o a a.c-o:指定文件输出名字二、C语言编译的过程:1.1.1 -E预编译Gcc –E –o a.e a.c预编译a.c文件,生成的目标文件名为a.e预编译是将include包含的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 53,970
精华内容 21,588
关键字:

c语言编译流程

c语言 订阅