xml地图|网站地图|网站标签 [设为首页] [加入收藏]

语言预处理,C语言条件编译及编译预处理阶段

来源:http://www.ccidsi.com 作者:集成经验 人气:142 发布时间:2019-07-31
摘要:一、C语言由源代码生成的各品级如下:     C源程序-编译预处理-编译-优化程序-汇编程序-链接程序-可执行文件 阅读前的扩展: 在嵌入式系统一编写程中,不管是基本的驱动

一、C语言由源代码生成的各品级如下:

 

 

C源程序->编译预处理->编译->优化程序->汇编程序->链接程序->可执行文件

阅读前的扩展:

在嵌入式系统一编写程中,不管是基本的驱动程序依旧应用程序的编写制定,都事关到大方的预管理与规范编写翻译,那样做的益处重要反映在代码的移植性强以及代码的修改方便等特征,因而引进了预管理与准则编写翻译的定义。在C语言的程序中可归纳各样以符号#初步的编写翻译指令,那一个指令称为预管理命令。预处理命令属于C语言编写翻译器,并不是C语言的组成都部队分。通过预管理命令可扩充C语言程序设计的意况。

       在那之中 编写翻译预管理阶段,读取c源程序,对里面包车型地铁伪指令(以#发端的吩咐)和特殊符号实行拍卖。或许说是扫描源代码,对其展开先导的转移,爆发新的源代码提要求编写翻译器。预管理进程先于编写翻译器对源代码进行管理。

规格编写翻译#ifdef #elif #ifndef #if #else #endif 的用法,及 #define #undef 的用法。

 

       在C 语言中,并从未其他内在的建制来完毕如下一些功力:在编写翻译时富含其余源文件、定义宏、根据法规决确定人员编制写翻译时是或不是含有有个别代码。要马到成功那么些干活儿,就须要运用预处理程序。纵然在当前相当多编写翻译器都富含了预管理程序,但常见感到它们是单身于编写翻译器的。预管理进度读入源代码,检查包罗预管理指令的讲话和宏定义,并 对源代码举行响应的转变。预管理进程还恐怕会删除程序中的注释和剩余的空白字符。

#ifdef/#elif/#ifndef/#if/#else/#endif 那多少个条件编写翻译指令是在开始展览标准编写翻译的时候使用的。

 

二、伪指令(或预管理指令)定义

  学习条件编写翻译首先对C语言的预处理举办学习,C语言由源代码生成的各阶段如下:

注:该类别内容整理自以下链接。

      预处理指令是以#号初始的代码行。#号必须是该行除了任何空白字符外的首先个字符。#后是命令关键字,在注重字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将要编写翻译器举行编写翻译在此之前对源代码做一点调换。上边是一对预管理指令:

C源程序->编写翻译预管理->编写翻译->优化程序->汇编制程序序->链接程序->可实行文件

    指令         用途
    #           空指令,无任何效果
    #include    包含一个源代码文件
    #define     定义宏
    #undef      取消已定义的宏
    #if         如果给定条件为真,则编译下面代码
    #ifdef      如果宏已经定义,则编译下面代码
    #ifndef     如果宏没有定义,则编译下面代码
    #elif       如果前面的#if给定条件不为真,当前条件为真,则编译下面代码,其实就是else if的简写
    #endif      结束一个#if……#else条件编译块
    #error      停止编译并显示错误信息

  个中 编写翻译预管理阶段,读取c源程序,对内部的伪指令(以#开班的下令)和特殊符号进行管理。大概说是扫描源代码,对其进行早先的调换,产生新的源代码提需要编写翻译器。预管理进度先于编写翻译器对源代码进行拍卖。且条件编写翻译是预管理程序的功效,实际不是编写翻译器的功效。

 

三、预管理指令首要富含以下多个地点:

  在C 语言中,并不曾其余内在的编写制定来形成如下一些功能:在编译时满含其余源文件、定义宏、依据标准决定编写翻译时是不是带有有些代码(条件编写翻译)。要做到那些干活儿,就须要使用预处理程序。

预管理的办事方法

1、宏定义指令

  就算在前段时间好多编写翻译器都带有了预管理程序,但常见以为它们是单独于编写翻译器的。预处理进程读入源代码,检查满含预处理指令的话语和宏定义,并对源代码实行对应的更改。

 

      宏定义了一个意味一定内容的标志符。预管理进度会把源代码中冒出的宏标记符替换来宏定义时的值。宏最常见的用法是概念代表有些值的全局符号。宏的第三种用 法是概念带参数的宏(宏函数),那样的宏能够象函数相同被调用,但它是在调用语句处展开宏,并用调用时的实际参数来代替定义中的格局参数。

  预管理进程还有可能会删除程序中的注释和剩余的空白字符。

1.1 预处理的魔法

 

  预管理指令(伪指令)定义:

在合龙开辟情状中,编写翻译,链接是还要到位的。其实,C语言编写翻译器在对源代码编写翻译在此以前,还索要更加的的拍卖:预编写翻译。预编写翻译的至关重大功能如下:

1.1 #define指令
1.1.1 #define预管理指令用来定义宏。该指令最简易的格式是:声美赞臣(Meadjohnson)个标记符,给出那么些标志符代表的代码(比方像圆周率那样的数)。在末端的源代码中,大家就足以接纳定义的宏代替要选取的代码,比方如下:

  预管理指令是以#号起初的代码行。#号必须是该行除了任何空白字符外的首先个字符。#后是指令关键字,在重大字和#号之间允许存在放肆个数的空白字符。整行语句构成了一条预管理指令,该指令将要编写翻译器进行编写翻译在此之前对源代码做一些转变。

  • 将源文件中以”include”格式满含的公文复制到编写翻译的源文件中。
  • 用实际值替换用“#define”定义的字符串。
  • 根据“#if”前面包车型客车原则决定供给编写翻译的代码。
//例1
#define MAX_NUM 10
int array[MAX_NUM];
for(i=0;i<MAX_NUM;i  ) 

  下边是有些预处理指令:

1.2 预管理的劳作措施
 
预管理的作为是由指令调控的。那些指令是由#字符开始的一对限令。
#define指令定义了八个宏,用来代表任杨晓伟西的多少个发令,平日是某贰个类型的常量。预管理会通过将宏的名字和它的定义存款和储蓄在共同来响应#define指令。当这么些宏在前边的程序中使用到时,预管理器”扩充”了宏,将宏替换为它所定义的值。
#include指令告诉预管理器张开八个一定的文本,将它的始末作为正在编写翻译的公文的一某些“包罗”进来。比方:上面那行命令:
  #include<stdio.h>

    在那几个例子中,对于阅读该程序的人的话,符号MAX_NUM就有一定的含义,它表示的值给出了数组所能容纳的最大因素数目。程序中得以频仍应用那一个值。作为一种约定,习于旧贯上连接一切用大写字母来定义宏,那样轻便把程序的宏标识符和一般变量标志符区别开来。假诺想要改动数组的深浅,只要求更动宏定义相提并论复编写翻译程序就能够。

 1     指令         用途
 2     #           空指令,无任何效果
 3     #include    包含一个源代码文件,用于文件引用
 4     #import    (OC中文件引用,可防止重复包含)
 5     #define     定义宏
 6     #undef      取消已定义的宏
 7     #if            如果给定条件为真,则编译下面代码
 8     #ifdef       如果宏已经定义,则编译下面代码
 9     #ifndef     如果宏没有定义,则编译下面代码
10     #elif       如果前面的#if给定条件不为真,当前条件为真,则编译下面的代码,其实就是else if的简写
11     #else      如果前面的#if给定条件不为真,则编译下面的代码
12     #endif      结束一个#if……#else条件编译块
13     #error      停止编译并显示错误信息

指令预管理器张开一个名叫stdio.h的公文,并将它的内容加到当前的先后中。

1.1.2 使用宏的补益有两点:

  预管理指令首要不外乎4个地方:

预管理器的输入是叁个C语言程序,程序大概含有指令。预管理器会实行那一个指令,并在管理进度中去除那几个指令。预管理器的输出是别的贰个程序:原程序的一个编写制定后的本子,不再包涵指令。预管理器的输出被一向交给编写翻译器,编写翻译器检查程序是或不是有荒唐,并经程序翻译为对象代码。  

一是使用方便。如下:

  1.宏定义指令

 

//例2
#define PAI 3.1415926

  宏定义了二个意味一定内容的标志符。预管理进度会把源代码中冒出的宏标记符替换来宏定义时的值。

预管理指令

PAI显著比3.1415926写着方便。

  宏最广大的用法是概念代表有个别值的全局符号。宏的第三种用法是概念带参数的宏(宏函数),那样的宏能够象函数一样被调用,但它是在调用语句处张开宏,并用调用时的实际上参数来代替定义中的形式参数。

 2.1.预甩卖指令

二是概念的宏有了意思,可读性强。如例1,MAX_NUM,望文生意便知是最大数据的情趣,比单独施用10这些数字可读性要强的多。

  1.1#define 指令

大多预管理器指令属于下边3种档期的顺序:

三是轻便修改。如例1,倘使在程序中有几12回会使用到MAX_NUM,修改只需求在宏定义里面修改一遍就足以,不然你会修改到崩溃。

  1.1.1#define 预管理指令用来定义宏。该指令最简易的格式是:声澳优(Ausnutria Hyproca)个标志符,给出这些标记符代表的代码(譬喻像圆周率那样的数)。在前边的源代码中,我们就足以选拔定义的宏替代要运用的代码,比如如下:

  • 宏定义:#define 指令定义三个宏,#undef指令删除一个宏定义。
  • 文件包涵:#include指令导致贰个点名文件的剧情被含有到程序中。
  • 规范编写翻译:#if,#ifdef,#ifndef,#elif,#else和#endif指令能够依照编译器能够测量试验的准则来将一段文本包蕴到程序中或解除在程序之外。

1.1.3 宏表示的值能够是二个常量表明式,允许宏嵌套(必须在前方已定义)。比方:

1 #define kPI 3.1415926
2 
3 #define kMAX_NUM 10
4 int array[kMAX_NUM]
5 for(int i = 0; i < kMAX_NUM; i  )

剩下的#error,#line和#pragma指令更破例的一声令下,比较少用到。

//例3
#define ONE 1
#define TWO 2
#define SUM(ONE TWO)

  在那么些事例中,对于阅读该程序的人来讲,符号kMAX_NUM命名就有了肯定的意思,它代表的值给出了数组所能容纳的最概略素数目。

2.2.命令准则
 
一声令下都以以#开始。#标记无需在一行的行首,只要她此前有空白字符就行。在#后是指令名,接着是指令所急需的任何消息。在命令的号子之间能够插入放肆数量的空格或横向制表符。指令总是第二个换行符处截止,除非明显地指明要持续。
一声令下能够出现在程序中的任什么地方方。大家家常便饭将#define和#include指令放在文件的起先,别的指令则放在后边,乃至在函数定义的高级中学级。
评释可以与指令放在同一行。

此地必要专注两点:
一是小心上边的宏定义使用了括号。固然它们实际不是必须的。但出于谨严思量,依然应当加上括号的。举例:
            six=THREE*TWO;
    预管理进度把下面的一行代码转变到:
            six=(ONE TWO)*TWO;
    若无拾壹分括号,就退换来six=ONE TWO*TWO;了。

  程序中得以频仍使用这些值。

 

也等于说预管理仅是回顾的字符替换,要时时细心这点,比较多荒谬都会为此应际而生。

  作为一种约定,习于旧贯上接连以小写字母k起始后边全数用大写字母来定义宏,那样便于把程序的宏标记符和一般变量标记符差异开来。

宏定义命令#define

二是即使我们比如用了#define ONE 1 那一个事例,可是一般必要宏定义要有其实际意义,#define ONE 1这种没意义的宏定义是不推荐的。(大致是那般个意思,忘记具体怎么说了)

  1.1.2应用宏定义的好处

使用#define命令并非的确的定义符号常量,而是定义一个方可替换的宏。被定义为宏的标识符称为“宏名”。在编写翻译预管理进度时,对先后中保有出现的“宏名”,都用宏定义中的字符串去代换,那称之为“宏代换”或“宏张开”。

1.1.4 宏仍是能够象征二个字符串常量,举个例子:
            #define VERSION "Version 1.0 Copyright(c) 2003"

  一是书写方便。

 

1.2 带参数的#define指令(宏函数)
    带参数的宏和函数调用看起来有些相似。看二个例子:

  二是概念的宏命名代表了它发挥的意思,可读性越来越强。

在C语言中,宏分为有参数和无参数二种。

//例4
#define Cube(x) (x)*(x)*(x)

  三是轻易修改,例如程序代码中多处用到了同三个宏,当必要修改宏的值的时候只需在宏定义处做一次修改,就能够改换全体宏的值。

3.1.无参数的宏

    可以时其余数字表达式以致函数调用来代替参数x。这里再次提示大家小心括号的施用。宏展开后通通满含在一对括号中,何况参数也蕴藏在括号中,那样就保险了宏和参数的完整性。看一个用法:

  1.2#运算符

其定义格式如下:
  #define 宏名  字符串

//例4用法
int num=8 2;
volume=Cube(num);

  出现在宏定义中的#运算符会把跟在以后的参数调换到二个字符串。偶然把这种用法的#称为字符串化运算符,宏定义中的#运算符告诉预管理程序,把源代码中任何传递给该宏的参数转变来一个字符串。

在上述宏定义语句中,各部分的意义如下:

    张开后为(8 2)*(8 2)*(8 2);
    若无那多少个括号就形成8 2*8 2*8 2了。
    下边包车型大巴用法是不安全的:
            volume=Cube(num );
    若是Cube是两个函数,上面的写法是能够精晓的。可是,因为Cube是叁个宏,所以会时有发生副效用。这里的书写不是粗略的表明式,它们将产生意料之外的结果。它们进行后是那般的:
            volume=(num )*(num )*(num );
    很显然,结果是10*11*12,而不是10*10*10;
    那么什么样安全的使用Cube宏呢?非得把恐怕产生副功能的操作移到宏调用的外场举办
            int num=8 2;
            volume=Cube(num);
            num ;

  使用如下:

#:表示那是一条预管理命令(凡是以“#”初阶的均为预管理命令)。

宏函数使用不当会冒出部分麻烦察觉的荒唐,请稳重使用。

1 定义:#define kMONTAGE(str) "CHM"#str
2 
3 打印:NSLog(@"%s", kMONTAGE(HML));
4 
5 输出:2017-04-17 20:39:03.340 ddddddd[6557:1081988] CHMHML

define:关键字“define”为宏定义命令。

1.3 #运算符
    出现在宏定义中的#运算符把跟在事后的参数转换来贰个字符串。有时把这种用法的#名为字符串化运算符。举例:

  1.3##运算符

宏名:是八个标示符,必须符合C语言标示符的分明,一般以大写字母标示宏名。

//例5
#define PASTE(n) "adhfkj"#n
int main()
{
       printf("%sn",PASTE(15));
       return 0;
}
//输出adhfj15

   ##运算符用于把参数连接到一块。预管理程序把出现在##两边的参数合併成叁个标识。

字符串:能够是常数,表明式,格式串等。在如今使用的标志常量的概念正是二个无参数宏定义。

宏定义中的#运算符告诉预管理程序,把源代码中其余传递给该宏的参数转变来三个字符串。所以输出应该是adhfkj15。

定义:#define kNUM(a, b, c) a##b##c

打印: NSLog(@"%d", kNUM(1, 2, 3));

输出:2017-04-17 20:47:35.865 ddddddd[6598:1087951] 123

只顾:预管理命令语句前边一般不会助长分号,即便在#define最终有分集团,在宏替换时分号也将替换成源代码中去。在宏名和字符串之间能够有私下个空格。

1.4 ##运算符(很少用)
    ##运算符用于把参数连接到一起。预管理程序把现身在##两边的参数合併成一个标志。看下边包车型大巴例证:

  2.头文本富含指令

例如:

//例6
#define NUM(a,b,c) a##b##c
#define STR(a,b,c) a##b##c
int main()
 {
       printf("%dn",NUM(1,2,3));
       printf("%sn",STR("aa","bb","cc"));
       return 0;
 }
//最后程序的输出为:
123
aabbcc

  选用头文件的指标根本是为了使一些定义能够供多个例外的C源程序选拔。因为在须求用到那些概念的C源程序中,只需加上一条#include语句就能够(OC 中运用#import),而毋庸再在此文件少校这个概念再度一次。预编写翻译程序将把头文件中的定义统统都参加到它所发出的输出文件中,以供编写翻译程序对之进行拍卖。

  #define PI 3.14

 

   #include预处理指令的功效是在指令处张开被含有的公文。饱含能够是种类的,也正是说二个被含有的文书中还是可以分包其余文件。规范C编写翻译器至少协理八重嵌套满含。预管理进度不检查在转移单元中是或不是业已包括了某些文件并堵住对它的频仍分包。(#import 能够卫戍重复包罗)

在选用宏定义时,还索要注意以下几点:

2、条件编写翻译指令。

  在程序中满含头文件有三种格式:
  #include <my.h>
  #include "my.h"
   第一种方法是用尖括号把头文件括起来。这种格式告诉预管理程序在编写翻译器自带的或外部库的头文件中搜索被含有的头文件。首先会寻觅编写翻译器自带的头文件。

宏定义是宏名来代表七个字符串,在宏展开时又以该字符串取代宏名。这只是一种简单的转移,字符串中能够含别的字符,能够是常数,也足以是表明式,预管理程序对它不作任何检查。如有错误,只好在编写翻译已被宏打开后的源程序时开采。

      技术员能够因而定义区别的宏来决定编写翻译程序对什么代码举行管理。条件编写翻译指令将决定那个代码被编写翻译,而哪些是不被编写翻译的。能够凭仗表达式的值也许某些特定的宏是或不是被定义来确定编写翻译条件。

  第二种格局是用双引号把头文件括起 来。这种格式告诉预管理程序在时下被编写翻译的应用程序的源代码文件中寻觅被含有的头文件,假设找不到,再寻找编写翻译器自带的头文件。
  选用三种分歧包蕴格式的说辞在于,编写翻译器是安装在公共子目录下的,而被编译的应用程序是在它们本身的私有子目录下的。叁个应用程序既饱含编写翻译器提供的公共头文件,也隐含自定义的私有头文件。采纳三种区别的含有格式使得编译器能够在广大头文件中区别出一组公共的头文件。

宏定义必须写在函数之外,其效用域为宏定义命令起到源程序甘休。

2.1 #if/#endif/#else/#elif指令
    #if指令质量评定跟在炮制另关键字后的常量表明式。假若表达式为真,则编写翻译后边的代码,知道出现#else、#elif或#endif甘休;不然就不编写翻译。
    #endif用于终止#if预管理指令。
    #else指令用于有个别#if指令之后,当前边的#if指令的法规不为真时,就编写翻译#else后边的代码。

   3.特殊符号

宏名在源程序只可以够若用引号括起来,则预管理程序不对其作宏替换。

//例7
#define DEBUG       //此时#ifdef DEBUG为真
//#define DEBUG 0  //此时为假
int main()
{
   #ifdef DEBUG
      printf("Debuggingn");
   #else
      printf("Not debuggingn");
   #endif
   printf("Runningn");
   return 0;
}

  预编写翻译程序能够识别部分非正规的号子。预编写翻译程序对于在源程序中冒出的那些串将用方便的值实行交流。

宏定义允许嵌套,在宏定义的字符串中得以动用已经定义的宏名。在宏打开时由预管理程序层层替换。

那样我们就足以兑现debug成效,每一次要出口调节和测试消息前,只需求#ifdef DEBUG判定三次。没有须求了就在文书初叶定义#define DEBUG 0

  #error指令将使编译器突显一条错误消息,然后结束编译。

习于旧贯上宏名可用大写字母表示,以福利与变量分化。但也同意用小写字母。

#elif预管理指令综合了#else和#if指令的机能。

  #line指令退换_LINE_与_FILE_的开始和结果,它们是在编写翻译程序中先行定义的标志符。
#pragma指令未有正式的概念。编写翻译器可以自定义其用途。规范的用法是不准或允许一些烦人的警戒音讯。

3.2带参数的宏

//例8
#define TWO
int main()
{
   #ifdef ONE
          printf("1n");
   #elif defined TWO
          printf("2n");
   #else
          printf("3n");
   #endif
}
//输出结果是2。 
 1 注意,是双下划线,而不是单下划线 。
 2 __FILE__ 包含当前程序文件名的字符串
 3 __LINE__  表示当前行号的整数
 4 __DATE__ 包含当前日期的字符串
 5 __STDC__  如果编译器遵循ANSI C标准,它就是个非零值
 6 __TIME__ 包含当前时间的字符串
 7 
 8 NSLog(@"%s", __FILE__);
 9 NSLog(@"%d", __LINE__);
10 NSLog(@"%s", __DATE__);
11 NSLog(@"%d", __STDC__);
12 NSLog(@"%s", __TIME__);
13 
14 打印:
15 2017-04-17 21:00:17.764 ddddddd[6631:1095088] /Users/jay/Desktop/Practise/ddddddd/ddddddd/AppDelegate.m
16 2017-04-17 21:00:17.765 ddddddd[6631:1095088] 31
17 2017-04-17 21:00:17.765 ddddddd[6631:1095088] Apr 17 2017
18 2017-04-17 21:00:17.765 ddddddd[6631:1095088] 1
19 2017-04-17 21:00:17.765 ddddddd[6631:1095088] 21:00:10

//在文件顶部定义 #line 100 ,下面__FILE__ 和 __LINE__ 打印所发生的变化可与上面做对比
1 #line 100
2 
3 NSLog(@"%s", __FILE__);
4 NSLog(@"%d", __LINE__);
5 
6 打印:
7 2017-04-17 21:06:43.373 ddddddd[6652:1099248] /Users/jay/Desktop/Practise/ddddddd/ddddddd/AppDelegate.m
8 2017-04-17 21:06:43.373 ddddddd[6652:1099248] 120

#define命令定义宏时,还足感觉宏设置参数。与函数中的参数近似,在宏定于中的参数为情势参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不独有要宏张开,还要用实参去代换形参。

2.2 #ifdef和#ifndef

  预编写翻译程序所变成的相当多是对源程序的“替代”职业。经过此种替代,生成贰个并未有宏定义、未有条件编写翻译指令、未有特殊符号的出口文件。那么些文件的意思同没有经过预管理的源文件是一模二样的,但剧情有所不一样。下一步,此输出文件将用作编写翻译程序的输出而被翻译成为机器指令。

带参宏定义的一般格局为:
  #define 宏名(形参表)  字符串

这四头首要用来幸免重复包括。大家一般在.h头文件前边加上那样一段:

上边讲明预编写翻译指令中比较常用的第4有的--条件编写翻译指令:

在概念带参数的宏时,宏名和形参表之间不可能有空格现身,不然,就将宏定义成为无参数格局,而招致程序出错。

//头文件防止重复包含
//funcA.h
#ifndef FUNCA_H
#define FUNCA_H
//头文件内容
#end if

  4.法则编写翻译指令

例如:

这么,假若a.h满含了funcA.h,b.h富含了a.h、funcA.h,重复富含,会出现局地type redefination之类的失实。
#if defined等价于#ifdef; #if !defined等价于#ifndef

  4.1规范编写翻译定义:

  #define ABS(x) (x)<0?-(x):(x)

本文由68399皇家赌场发布于集成经验,转载请注明出处:语言预处理,C语言条件编译及编译预处理阶段

关键词: 68399皇家赌场 C语言

最火资讯