精华内容
下载资源
问答
  • C语言的编译模式

    2019-03-10 20:36:10
    编译模式指如何在内存中放置程序代码及数据,如何分配堆栈,并确认占用内存大小及如何存取它们,当指定内存模式(编译模式)以后,语言编译程序将按事先选择好内存模式编译组织程序。C 语言中提供了6种编译...

    编译模式是指如何在内存中放置程序代码及数据,如何分配堆栈,并确认占用的内存大小及如何存取它们,当指定内存模式(编译模式)以后,语言编译程序将按事先选择好的内存模式编译组织程序。C 语言中提供了6种编译模式,这6种模式是:微模式(Tiny),小模式(Small),中模式(Medium),紧凑模式(Compact),大模式(Large)和巨模式(Huge)。用户可以按照自己的程序大小及需要进行选择。

    所谓小程序就是指程序只有一个程序段,大小不超过64KB,缺省的码(函数)指针是near(近程指针)。所谓大程序就是指程序有多个程序段,每个程序段不超过64KB,但总程序量可超过64KB,缺省的码指针是far(远程指针)。小数据就是指数据只有一个数据段,缺省的数据指针是near。大数据就是指数据有多个数据段,缺省的数据指针是far。

    C语言编译模式—微模式(Tiny)--near指针
    在微模式下程序中的数据及代码均放在同一段内,即它们不超过 64KB。在微模式下代码段、堆栈段和数据段的段地址均相同,即CS=DS=SS=ES。

    C语言编译模式—小模式(Small)
    在小模式下,程序中的代码放在64KB的代码段内,数据放在64KB的数据段内。在小模式下,栈段、附加数据段和数据段均指向同一地址,它们合三为一,即DS=SS=ES,指针都是near,一般程序均采用小模式编译。

    C语言编译模式—中模式(Medium)
    在中模式下,所有数据放在64KB的数据段内,因而数据段内使用near,代码量可以大于64KB(允许达到1MB),因而可以在不同的代码段内,代码段使用(far远程指针)来自不同源文件的码模块放在不同的码段内。

    C语言编译模式—紧凑模式(Compact)
    在紧凑模式下,数据量超过64KB时,可放在多个数据段中,数据段内的指针是(far)。代码量不超过64KB,在一个段内,因而代码段内指针为近程的(near)。但在该模式下,静态数据仍不能超过64KB,堆用far指针来存取。代码、静态数据、堆栈、堆各有自己的段。堆只有远堆,没有近堆。

    C语言编译模式—大模式(Large)
    大模式下,代码及数据均采用far指针,且都可达到1MB。静态数据、堆栈、堆同紧凑模式,代码同中模式。静态数据仍跟紧凑模式一样,不能超过64KB。

    C语言编译模式—巨模式(Huge)
    巨模式下,代码段及数据段均用far指针,代码分布在不同的代码段内,数据也分布在不同的数据段内,它们来自不同的源程序,大堆栈只有一个。而且静态数据大小允许超过64KB

    紧凑模式、大模式、巨模式数据区大小均允许超过64KB,即可以用数据far指针对不同数据段内的数据进行存取,它们同称为大数据存储模式。但有一点不同:紧凑模式和大模式按 C 的规定,其静态数据,即如数组、结构或其他类型的数据被定义为静态类型时,其数据量不能超过64KB,而只有巨模式才允许超过64KB。在大数据存储模式下,堆和栈分别在不同段内,多以动态数据和局部变量的形式存放,这样就不受64KB大小的限制,栈的增长不会影响堆的空间。

      无论采用哪一种编译模式,C源程序编译生成的代码和数据量都不能超过64KB,对于超过的源程序,可以视代码或数据多少将其分解成两个或多个程序分别编译。大代码量程序要选用大代码编译模式(中模式、大模式和巨模式),大数据量程序应选用大数据编译模式(紧凑模式、大模式和巨模式),这样编译生成的.obj 文件将会带给连接程序信息,将代码和数据安排在不同段内。这样生成的.exe 文件在加载时将告诉 DOS 该程序应如何装入代码段和数据段,如何初始化寄存器。这样,就可确定在不同编译模式下开辟数据区的大小,即大于64KB,或不超过64KB。

    在tiny、small模式下,所有的函数定义、全局变量定义和指针变量的定义,如果没有显示的加上far、near、huge等关键字,都默认为使用了near关键字;在medium模式下,函数定义默认使用了far关键字,变量定义默认使用了near关键字;在compact模式下函数定义模式使用了near关键字,变量定义默认使用了far关键字;large模式下函数定义和变量定义模认使用了far关键字;huge模式下函数定义模认使用了far关键字,变量定义默认使用了huge关键字。

        near、far、huge关键字的真正含义是什么?这三个关键字只能用于修改函数、全局变量和指针变量,对于非指针类型的局部变量,这些关键字没有实际意义。这些关键字用于修饰函数时,huge的含义与far相同,用于指明该函数的调用方式为far调用方式,即调用时需要一个段值和一个段偏移组成的32bits调用地址,使用far call进行跳转,跳转前先压栈保存当前CS:IP。near修饰函数时,用于指明该函数的调用方式为near调用方式,调用时只需要一个16bits的近地址,即当前CS的段内偏移。

    展开全文
  • 因为 C语言是可以移植,所以它在许多环境中可用,其中包括 UNIX,Linux,Windows等等 。 不过,让我们首先来看一看许多环境所共有一些方面。你完全不必知道运行一个 C 程序后面事情,但了解一点是一个很好...

    编程机制

    编写程序时必须遵循确切步骤主要是取决于你的计算机环境。因为 C语言是可以移植的,所以它在许多环境中可用,其中包括 UNIX,Linux,Windows等等 。

    不过,让我们首先来看一看许多环境所共有的一些方面。你完全不必知道运行一个 C 程序后面的事情,但了解一点是一个很好的背景知识。它还可以帮助你理解为什么编写一个 C 程序必须经过一些特定步骤。

    用 C 语言编写一个程序时,你将编写的内容保存在一个被称为源代码文件的文本文件中。

    大多数 的系统,都需要该文件的名称以 .c 结尾。例如,hello world.c 。名称中小点前的部分被称为基本名,小点后的部分被称为扩展名。因此,hello world 是一个基本名,c 是一个扩展名。组合在一起的 hello world.c 是文件名。

    这样,在我们提到名称时内容就可以更具体,我们假定有一个名为 hello world.c 的源文件,其源代码如下面所示。

    #include

    int main ()

    {

    printf("hello world! ");

    return 0;

    }

     

    目标代码文件、可执行文件和库

    C语言编程的基本策略是使用程序将源代码文件转换为可执行文件,此文件包含可以运行机器语言代码。

    它分两步完成这一工作:编译和链接。

    编译器将源代码转换为中间代码,链接器将此中间代码与其他代码相结合生成可执行文件。C 使用被划分为两部分的这一方法使程序便于模块化。

    你可分别编译各个模块,然后使用链接器将编译过的模块结合起来。这样,如果需要改变一个模块,则不必重新编译所有其他的模块。同时,链接器将你的程序与预编译的库代码结合起来。

    中间文件的形式有多种选择。最一般的选择,同时也是我们这里讲述的实现方式所采取的选择,是将源代码转换为机器语言代码,将结果放置在一个目标代码文件中。

    虽然目标文件包含机器语言代码,但该文件还不能运行。目标文件包含源代码的转换结果,但它还不是一个完整的程序。

    目标代码文件中所缺少的第一个元素是一种叫做启动代码(start-up code)的东西,此代码相当于你的程序和操作系统之间的接口。

    例如,你可以在DOS或Linux下运行一个 IBM PC兼容机,在两种情况中硬件是相同的,所以会使用同样的目标代码,但DOS与Linux要使用不同的启动代码,因为这两种系统处理程序的方式是不同的。

    所缺少的第二个元素是库例程的代码。几乎所有C程序都利用标准C库中所包含的例程(称为函数)。

    例如,前面的 concrete.c 使用了函数 printf()。目标代码文件不包含这一函数的代码,它只包含声明使用 printf()函数的指令。实际代码存储在另一个称为“库”的文件中。库文件中包含许多函数的目标代码。

    链接器的作用是将【目标代码】、【系统的标准启动代码】和【库代码】这3个元素结合在一起,并将它们存放在单个文件,即可执行文件中。对库代码来说,链接器只从库中提取你所使用的函数所需要的代码,如下图中的简单示例:

    简而言之,目标文件和可执行文件都是由机器语言指令组成的。但目标文件只包含你所编写的代码转换成的机器语言,而可执行文件还包含你所使用的库例程以及启动代码的机器代码。

     

    Windows系统的集成开发环境

    因为 C 编译器不是标准 Windows 包的一部分,所以需要获得并安装一个 C 编译器。

    许多厂商都会提供基于 Windows的集成开发环境(IDE) 。

    所有编译器都具有用来装配C程序的快速,集成的开发环境。关键的一点是,它们都具有内置的编辑器,可用来编写C程序。

    这类开发环境一般都提供了让你可以命名和保存源代码文件的菜单,以及让你可以不离开IDE就能编译和运行程序的菜单。如果编译器发现任何错误,会返回到编辑器中,而且编辑器可以标出有问题的行,并将它们与相应的错误消息匹配起来,例如VC6.0、QT、Visual Studio(简称vs)。

    Windows IDE最初可能让人有一点望而生畏,因为它们提供多种目标,也就是说,提供了多种可让程序在其中运行的环境。

    例如,它们可能提供16位Windows程序,32位Windows程序,动态链接库文件(DLL)等等让你选择。许多目标都需要引入Windows图形界面的支持。为了管理这些选项,通常需要创建一个项目,以便随后向其中添加将要使用的源代码文件名。

    一般来说,首先使用文件菜单来创建一个新的项目。重要的是选取正确的项目形式。本书中的例子是一般性的例子,设计目的是在一个简单的命令行环境中运行。

    因为 Widnows IDE一般可处理 C 和 C++,所以你应该指明你需要一个C程序。在某些产品,可以使用项目类型来指明希望使用 C。

    而在其他一些产品,如 Microsoft Visual C++中,可以使用.c文件扩展名来指明希望使用 C 而不是 C++。然而,大多数 C 程序也可以作为 C++程序运行。

    可能大家在写完一个程序之后会遇到一个问题:显示程序执行的窗口在程序终止时突然消失,也就是程序运行后窗口一闪而过。

    如果遇到这种情况,那么可以使程序暂停,直到按下 Enter键。要做到这一点,请在程序的末尾,恰好在 return 语句之前,添加下面的一行:

    getchar();

    该行读取一次按键,因此程序将暂停直到按下 Enter 键时。有时,根据程序函数的需要,可能已经有一个等待按键的指令。在这种情况下,需要使用 getchar()两次:

    getchar();

    getchar();

    例如,如果程序最后做的事情是请你输入你的体重,那么就应当键入你的体重并按 Enter 键以输入数据。

    程序将读取体重,第1个getchar()将读取 Enter键,第2个getchar()将导致程序暂停,直到再次按下 Enter键。如果现在你对此还不太理解,那么在学习更多关于 C 输入的知识后你就明白了。

    虽然各种IDE都有许多共同的原则,但在细节方面会因产品而异,而在一个产品系列中,又会因版本而异。你必须要经过一些实践,才能知道编译器的正确工作方式。

     

    语言标准

    目前,有许多 C 实现方式可用。理想情况下,编写 C 程序时,假如该程序末使用机器特定的编程技能,则它在任何实现方式中的运行应该是相同的。要在实践中做到这一点,不同的实现方式需要遵守一个公认的标准。

    首先说明一点,C语言并没有官方的标准。

    不过,Brian Kernighan 和 Dennis Ritchie 编写的 The C Programming Language 第1版(1978)成为大家接受的标准,通常称为 K&R C 或经典 C。

    第1个 ANSI/ISO C 标准

    随着 C语言的发展和更加广泛地用于更多种类的系统上,使用 C 的群体意识到它需要一个更加全面,新颖和严格的标准。

    为了满足这一要求,美国国家标准代组织(ANSI)在1983年设立了一个委员会以发展一个新的标准,该标准于1989年正式采用。

    这个新标准(ANSI C)定义了语言和一个标准 C 库。国标标准化组织于1990年采用一个 C 标准 (ISO C )。

    ISO C 和 ANSI C 实质上是同一个标准。ANSI/ISO标准最终版本通常被称为 C89 (因为 ANSI于1989年批准了该标准) 或 C90(因为 ISO 于1990年批准了该标准)。然而,因为 ANSI版本是首先出现的,所以人们通常使用ANSI C这一术语。

     

    C99 标准

    1994年,修订标准的工作开始了,这一努力的结果是产生了 C99标准。

    一个联合 ANSI/ISO委员会签署了 C90标准的最初原则,包括保持言语短小而简单。他们的意图不是为语言添加新的特性,而是为了满足新的目标。

    新目标之一是支持国际化编程。

    例如,提供了处理国际字符集的方法。第二个目标是“整理现有的惯例以解决明显的缺点”。因此,在遇到需要将 C 移植到 64 位处理器时,委员会根据在真实生活中处理问题的人的经验来添加标准。第三个目标是针对科学和工程项目的重要数字计算改进 C 的适应能力。

    国际化,修正其不足和改进计算的实用性这三点是主要的面向改变的目标。形成在关于更改的计划在性质上更加保守,例如,让与 C90 和 C++ 的不兼容性达到最小,让语言在概念上保持简单。肩带来说就是希望 C++成为重要的和强有力的语言。

    结果是 C99 的修改保持了 C 的本质特性,C 继续是一种简短,清楚,高效的语言。

    书中指出了 C99中的许多修改。因为目前大多数编译器没有完全实现所有 C99的修改,所以你可以会发现一些修改在你的系统上不可用。或者你可能会发现,只有修改编译器的设置以后,才能够看到一些 C99 的特性。

     

    总结

    C 是一种强大,简洁的编程语言。之所以流行是因为它提供了有用的编程工具和对硬件良好的控制,还因为 C 程序在从一个系统向另一个系统移植方面比大多数程序更容易。C 是一种需要编译的语言。C 编译器和链接器是将 C 语言源代码转换成可执行代码的程序。

    用 C 编程可能很费力,困难并让你感到灰心,但这一工作也可能让你着迷,兴奋和感到满意。

    最后,不管你是转行也好,初学也罢,进阶也可,如果你想学编程~

    值得关注】我的 C/C++编程学习交流俱乐部!【点击进入】

    问题答疑,学习交流,技术探讨,还有超多编程资源大全,零基础的视频也超棒~

    展开全文
  • C语言文件的编译与执行四个阶段

    千次阅读 2016-07-03 22:49:11
    C语言的编译链接过程要把我们编写一个c程序(源代码)转换成可以在硬件上运行程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式目标文件过程。链接把目标文件、操作系统...

    C语言文件的编译与执行的四个阶段并分别描


    C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织形成最终生成可执行代码的过程。过程图解如下:

      从图上可以看到,整个代码的编译过程分为编译和链接两个过程,编译对应图中的大括号括起的部分,其余则为链接过程。

      编译过程

      编译过程又可以分成两个阶段:编译和会汇编。

      编译

      编译是读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,源文件的编译过程包含两个主要阶段:

      第一个阶段是预处理阶段,在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。如#include指令就是一个预处理指令,它把头文件的内容添加到.cpp文件中。这个在编译之前修改源文件的方式提供了很大的灵活性,以适应不同的计算机和操作系统环境的限制。一个环境需要的代码跟另一个环境所需的代码可能有所不同,因为可用的硬件或操作系统是不同的。在许多情况下,可以把用于不同环境的代码放在同一个文件中,再在预处理阶段修改代码,使之适应当前的环境。

      主要是以下几方面的处理:

      (1)宏定义指令,如 #define a b

      对于这种伪指令,预编译所要做的是将程序中的所有a用b替换,但作为字符串常量的 a则不被替换。还有 #undef,则将取消对某个宏的定义,使以后该串的出现不再被替换。

      (2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等。

      这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

      (3) 头文件包含指令,如#include "FileName"或者#include 等。

      在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条#include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在 /usr/include目录下。在程序中#include它们要使用尖括号(< >)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在#include中要用双引号("")。

      (4)特殊符号,预编译程序可以识别一些特殊的符号。

      例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

      预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输出而被翻译成为机器指令。

      第二个阶段编译、优化阶段,经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main,if,else,for,while,{,}, +,-,*,\等等。

      编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。

      优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。

      对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。

      后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放的有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。

      汇编

      汇编实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段:

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

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

      UNIX环境下主要有三种类型的目标文件:

      (1)可重定位文件

      其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。

      (2)共享的目标文件

      这种文件存放了适合于在两种上下文里链接的代码和数据。第一种是链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文件;第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。

      (3)可执行文件

      它包含了一个可以被操作系统创建一个进程来执行之的文件。汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这个就是链接程序的工作了。

      链接过程

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

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

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

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

      (1)静态链接

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

      (2) 动态链接

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

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

      我们在linux使用的gcc编译器便是把以上的几个过程进行捆绑,使用户只使用一次命令就把编译工作完成,这的确方便了编译工作,但对于初学者了解编译过程就很不利了,下图便是gcc代理的编译过程:

      从上图可以看到:

      预编译

      将.c 文件转化成 .i文件

      使用的gcc命令是:gcc –E

      对应于预处理命令cpp

      编译

      将.c/.h文件转换成.s文件

      使用的gcc命令是:gcc –S

      对应于编译命令 cc –S

      汇编

      将.s 文件转化成 .o文件

      使用的gcc 命令是:gcc –c

      对应于汇编命令是 as

      链接

      将.o文件转化成可执行程序

      使用的gcc 命令是: gcc

      对应于链接命令是 ld

      总结起来编译过程就上面的四个过程:预编译、编译、汇编、链接。Lia了解这四个过程中所做的工作,对我们理解头文件、库等的工作过程是有帮助的,而且清楚的了解编译链接过程还对我们在编程时定位错误,以及编程时尽量调动编译器的检测错误会有很大的帮助的。


    展开全文
  • C语言文件的编译到执行四个阶段

    千次阅读 2016-07-15 13:37:47
    C语言的编译链接过程要把我们编写一个c程序(源代码)转换成可以在硬件上运行程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式目标文件过程。链接把目标文件、操作系统...

    C语言文件的编译与执行的四个阶段并分别描述

     

     

    C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织形成最终生成可执行代码的过程。

     

     

      第一个阶段:

                   预处理阶段,在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。如#include指令就是一个预处理指令,它把头文件的内容添加到.cpp文件中。这个在编译之前修改源文件的方式提供了很大的灵活性,以适应不同的计算机和操作系统环境的限制。一个环境需要的代码跟另一个环境所需的代码可能有所不同,因为可用的硬件或操作系统是不同的。在许多情况下,可以把用于不同环境的代码放在同一个文件中,再在预处理阶段修改代码,使之适应当前的环境。

      主要是以下几方面的处理:

      (1)宏定义指令,如 #define a b

      对于这种伪指令,预编译所要做的是将程序中的所有ab替换,但作为字符串常量的 a则不被替换。还有 #undef,则将取消对某个宏的定义,使以后该串的出现不再被替换。

      (2)条件编译指令,如#ifdef#ifndef#else#elif#endif等。

      这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

      (3) 头文件包含指令,如#include "FileName"或者#include 等。

      在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条#include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在 /usr/include目录下。在程序中#include它们要使用尖括号(< >)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在#include中要用双引号("")

      (4)特殊符号,预编译程序可以识别一些特殊的符号。

      例如在源程序中出现的LINE标识将被解释为当前行号(十进制数)FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

      预编译程序所完成的基本上是对源程序的替代工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输出而被翻译成为机器指令。

     

      第二个阶段:

               编译、优化阶段,经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main,if,else,for,while,{,}, +,-,*,\等等。

      编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。

      优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。

      对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。

      后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放的有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISCCISCVLIW)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。

       第三阶段:

     

           汇编

      汇编实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段:

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

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

      UNIX环境下主要有三种类型的目标文件:

      (1)可重定位文件

      其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。

      (2)共享的目标文件

      这种文件存放了适合于在两种上下文里链接的代码和数据。第一种是链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文件;第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。

      (3)可执行文件

      它包含了一个可以被操作系统创建一个进程来执行之的文件。汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这个就是链接程序的工作了。

    第四阶段:

     

      链接过程

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

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

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

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

      (1)静态链接

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

      (2) 动态链接

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

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

      我们在linux使用的gcc编译器便是把以上的几个过程进行捆绑,使用户只使用一次命令就把编译工作完成,这的确方便了编译工作,但对于初学者了解编译过程就很不利了,下图便是gcc代理的编译过程:

     

     

     

      从上图可以看到:

      预编译

      将.c 文件转化成 .i文件

      使用的gcc命令是:gcc –E

      对应于预处理命令cpp

      编译

      将.c/.h文件转换成.s文件

      使用的gcc命令是:gcc –S

      对应于编译命令 cc –S

      汇编

      将.s 文件转化成 .o文件

      使用的gcc 命令是:gcc –c

      对应于汇编命令是 as

      链接

      将.o文件转化成可执行程序

      使用的gcc 命令是: gcc

      对应于链接命令是 ld

      

     


    展开全文
  • 提到C语言,我们知道C语言和其他高级语言的最大的区别就是C语言是要操作内存的!我们需要知道——变量,其实是内存地址的一个抽像名字罢了。在静态编译的程序中,所有的变量名都会在编译时被转成内存地址。机器是不...
  • C语言——预编译

    2017-05-31 22:13:26
    ... 在C 语言中,并没有任何内在机制来完成如下一些功能:在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码。要完成这些工作,就需要使用预处理程序。尽管在目前绝大多
  • C语言的编译链接过程要把我们编写一个c程序(源代码)转换成可以在硬件上运行程序(可执行代码),需要进行编译和链接。编译是把文本形式源代码翻译为机器语言形式目标文件过程。链接把目标文件、操作系统...
  • C语言文件的编译与执行四个阶段并分别描述

    万次阅读 多人点赞 2016-10-12 21:38:24
    C语言的编译链接过程要把我们编写一个c程序(源代码)转换成可以在硬件上运行程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式目标文件过程。链接把目标文件、操作系统...
  • 然而随着 C 语言编写的软件逐渐庞大,越来越多地需要引入一些其他语言中的特性,来帮助更高效地进行开发,Linux kernel 一个典型例子。 在动态类型的语言里面,往往有 typeof 这种语法,来获取变量的数据类型,...
  • 该库C语言编写,并提供了其他语言的绑定计划。 该库面向平台游戏,射击游戏,角色扮演游戏和冒险游戏。 在Ubuntu上,您需要以下软件包sudo apt-get install physfs-dev libyaml-dev libxmu-dev libxi-dev
  • 可以读取文件,而给出词法分析结果,我们老师叫我们编。。。。我花了两个星期。。。适合初学同学参考,思路明了。。。一看就懂。...我C语言。...如果需要其它语言,只要稍微改一下就OK了
  • C语言的编译过程

    2010-10-11 09:58:00
    C语言的编译过程”我在大一时学习C语言这门课第一课内容,当时刚接触到编程语言,同时对计算机底层运作原理以前不同。所以这第一节课让我感到甚迷茫。在经过几年学习后我再次翻开"c语言程序设计"这...
  •  C语言是可以直接编译成为操作系统可识别执行机器语言,不经过第二次编译,所以C语言是编译语言; Java语言(解释型语言):  java第一次编译编译成为自己可执行.class文件(即字节码),而在程序真正...
  • c语言和css语言的区别是什么?下面本篇文章就来给大家介绍一下...C语言是仅产生少量机器语言以及不需要任何运行环境支持便能运行高效率程序设计语言。尽管C语言提供了许多低级处理功能,但仍然保持着跨平台...
  • 学过C语言的人都应该知道,我们所编辑的C语言程序不能直接放到机器上运行,它只不过一个带".c"后缀文件(也称为源代码)而已,需要经过一定处理才能转换成机器上可运行可执行文件。我们将对C语言的这种...
  • 然而随着C语言编写的软件逐渐庞大,越来越多地需要引入一些其他语言中的特性,来帮助更高效地进行开发,Linux kernel一个典型例子。 在动态类型的语言里面,往往有typeof这种语法,来获取变量的数据类型,比如...
  • 然而随着C语言编写的软件逐渐庞大,越来越多地需要引入一些其他语言中的特性,来帮助更高效地进行开发,Linux kernel一个典型例子。 在动态类型的语言里面,往往有typeof这种语法,来获取变量的数据类型,比如...
  • 生成一个可执行文件通常需要经过以下几个步骤: 预处理你源代码,去掉注释,以及其他技巧性工作就像在 C 中展开宏。...把汇编语言转换为机器语言──是的,我们在说位元和字节,就是1和0。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,246
精华内容 898
关键字:

c语言是需要编译的语言

c语言 订阅