精华内容
下载资源
问答
  • C语言_预处理

    2021-01-30 13:15:22
    C的预处理是在程序被编译之前执行的,包括将其他文件包含进正在编译的文件,定义符号常量和宏,条件编译和有条件的执行预处理命令。预处理命令都以 # 开头。 1.#include 功能:将指定文件的一个副本包含到命令所在...

    C语言_预处理

    0.引言

    C的预处理是在程序被编译之前执行的,包括将其他文件包含进正在编译的文件,定义符号常量和宏,条件编译和有条件的执行预处理命令。预处理命令都以 # 开头。

    1.#include

    功能:将指定文件的一个副本包含到命令所在位置上

    形式:#include<filename>

    #include"filename"

    区别:查找文件的起始位置不同。

    使用引号时:会从待编译文件所在的目录里开始查找,一般用于程序员自己定义的头文件

    使用尖括号:用于标准函数库的头文件,一般会在预先指定的编译器和系统目录中开始查找

    2.#define (符号常量)

    格式:#define indentifier replacement_text

    功能:其后出现的所有标识符(indentifier)会在编译前被替换为后面的替换文本

    p.s.习惯上只用大写字母和下划线给符号常量命名

    ​ 这也是一种宏,此处拆开只是为了单独说明,而且它也更常用

    ​ 得到符号常量的另一途径:利用关键字const进行修饰

    3.#define(宏)

    宏 ,是#define预处理命令定义的一种标识符,宏的定义可以带实参,也可以不带。不带实参的宏和上面的符号常量类似;而带实参的宏会把实参带入宏的替换文本中,进行展开

    例子(带实参):

    #define RECTANGLE_AREA(x,y) ( (x) * (y) )
    
    rectArea = RECTANGLE_AREA(a + 4, b + 7);
    
    //in fact, it will be replaced by:
    rectArea = (a + 4) * (b + 7);
    

    p.s.宏可以用 #undef撤销

    4.条件编译

    功能:只对满足条件的代码进行编译

    形式:#if, #ifdef, #ifndef

    1)#if

    和if,else,else if的使用很像,方法如下:

     #if 整型常量表达式1
      代码块1
    #elif 整型常量表达式2
      代码块2
    #elif 整型常量表达式3
      代码块3
    #else
      代码块4
    #endif 
    

    2)#ifdef

    #ifdef  宏名
        代码块1
    #else
        代码块2
    #endif
    

    意思是 如果所给出的宏已经被定义过(即存在),则判定为真,编译1,否则编译2

    3)#ifndef

    #ifndef 宏名
        代码块1 
    #else 
        代码块2 
    #endif
    

    与#ifdef类似,区别在于 宏名 处的判定是判断宏是否未定义,如果未定义,编译1;否则编译2

    5.#error和#pragma预处理命令

    格式:#error tokens

    功能:打印出包含命令中指定标记的信息,具体内容和系统的实现有关。

    ​ tokens是用空格分隔的一个字符序列

    格式:#pragma tokens

    功能:执行一个系统实现中已经定义好的操作,如果没找到就忽略这个命令

    6.#和##运算符

    #:#运算符需用在带实参的宏中,其操作数就是宏的实参,它将替换文本中的标记转换成一个用引号引起来的字符串。

    例子:

    #define HELLO(x)  printf("hello," # "\n");
    
    //when we meet"HELLO(Mary)", it become:
    
    printf("hello," "Mary" "\n");
    //==
    printf("hello,Mary\n");
    

    ##:用于将两个标记拼接在一起

    例子:

    #define EXAMPLE(x,y)  x##y
    
    //in the below code,when meet:
    EXAMPLE(O,K)
    //it will be replaced by:
    EXAMPLE(OK)
    

    例子:

    #define EXAMPLE(x,y)  x##y
    
    //in the below code,when meet:
    EXAMPLE(O,K)
    //it will be replaced by:
    EXAMPLE(OK)
    

    注意:##运算符必须有两个操作数

    展开全文
  • C语言预处理功能之宏定义

    千次阅读 2012-07-12 12:53:57
     宏定义是C提供三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译  1. 不带参数宏定义:  宏定义又称为宏代换、宏替换,简称“宏”。  格式: #define 标识符 字符串  其中...

    宏定义

      宏定义是C提供的三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译

      1. 不带参数的宏定义:

      宏定义又称为宏代换、宏替换,简称“宏”。

      格式: #define 标识符 字符串

      其中的标识符就是所谓的符号常量,也称为“宏名”。

      预处理(预编译)工作也叫做宏展开:将宏名替换为字符串。

    掌握"宏"概念的关键是“换”。一切以换为前提、做任何事情之前先要换,准确理解之前就要“换”。

      即在对相关命令或语句的含义和功能作具体分析之前就要换:

      例:   #define PI 3.1415926   把程序中出现的PI全部换成3.1415926

      说明:

      (1)宏名一般用大写

      (2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义

      (3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。

      (4)宏定义末尾不加分号;

      (5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。

      (6)可以用#undef命令终止宏定义的作用域

      (7)宏定义可以嵌套

      (8)字符串" "中永远不包含宏

      (9)宏定义不分配内存,变量定义分配内存。

      2. 带参数的宏定义:

      除了一般的字符串替换,还要做参数代换

      格式:   #define 宏名(参数表) 字符串

      例如:#define S(a,b) a*b

      area=S(3,2);第一步被换为area=a*b; ,第二步被换为area=3*2;

      类似于函数调用,有一个哑实结合的过程:

      (1)实参如果是表达式容易出问题

      #define S(r) r*r

      area=S(a+b);第一步换为area=r*r;,第二步被换为area=a+b*a+b;

      正确的宏定义是#define S(r) ((r)*(r))

      (2)宏名和参数的括号间不能有空格

      (3)宏替换只作替换,不做计算,不做表达式求解

      (4)函数调用在编译后程序运行时进行,并且分配内存。宏替换在编译前进行,不分配内存

      (5)宏的哑实结合不存在类型,也没有类型转换。

      (6)函数只有一个返回值,利用宏则可以设法得到多个值

      (7)宏展开使源程序变长,函数调用不会

      (8)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值)

      3. 宏定义其他冷门、重点知识

      #define用法

      1、 用无参宏定义一个简单的常量

      #define LEN 12

      这个是最常见的用法,但也会出错。

      比如下面几个知识点你会吗?可以看下:

      (1) #define NAME "zhangyuncong"

      程序中有"NAME"则,它会不会被替换呢?

      (2) #define 0x abcd

      可以吗?也就是说,可不可以用把标识符的字母替换成别的东西?

      (3) #define NAME "zhang

      这个可以吗?

      (4) #define NAME "zhangyuncong"

      程序中有上面的宏定义,并且,程序里有句:

      NAMELIST这样,会不会被替换成"zhangyuncong"LIST

      四个题答案都是否定的。

      第一个,""内的东西不会被宏替换。这一点应该大都知道。

      第二个,宏定义前面的那个必须是合法的用户标识符

      第三个,宏定义也不是说后面东西随便写,不能把字符串的两个""拆开。

      第四个:只替换标识符,不替换别的东西。NAMELIST整体是个标识符,而没有NAME标识符,所以不替换。

      也就是说,这种情况下记住:#define 第一位置第二位置

      (1) 不替换程序中字符串里的东西。

      (2) 第一位置只能是合法的标识符(可以是关键字)

      (3) 第二位置如果有字符串,必须把""配对。

      (4) 只替换与第一位置完全相同的标识符

      还有就是老生常谈的话:记住这是简单的替换而已,不要在中间计算结果,一定要替换出表达式之后再算。

      2、 带参宏一般用法

      比如#define MAX(a,b) ((a)>(b)?(a):(b))

      则遇到MAX(1+2,value)则会把它替换成:

      ((1+2)>(value)?(1+2):(value))

      注意事项和无参宏差不多。

      但还是应注意

      #define FUN(a) "a"

      则,输入FUN(345)会被替换成什么?

      其实,如果这么写,无论宏的实参是什么,都不会影响其被替换成"a"的命运。

      也就是说,""内的字符不被当成形参,即使它和一模一样。

      那么,你会问了,我要是想让这里输入FUN(345)它就替换成"345"该怎么实现呢?

      请看下面关于#的用法

      3、 有参宏定义中#的用法

      #define STR(str) #str

      #用于把宏定义中的参数两端加上字符串的""

      比如,这里STR(my#name)会被替换成"my#name"

      一般由任意字符都可以做形参,但以下情况会出错:

      STR())这样,编译器不会把“)”当成STR()的参数。

      STR(,)同上,编译器不会把“,”当成STR的参数。

      STR(A,B)如果实参过多,则编译器会把多余的参数舍去。(VC++2008为例)

    STR((A,B))会被解读为实参为:(A,B),而不是被解读为两个实参,第一个是(A第二个是B)。  

        4、 有参宏定义中##的用法

      #define WIDE(str) L##str

      则会将形参str的前面加上L

      比如:WIDE("abc")就会被替换成L"abc"

      如果有#define FUN(a,b) vo##a##b()

      那么FUN(id ma,in)会被替换成void main()

      5、 多行宏定义:

      #define doit(m,n) for(int i=0;i<(n);++i)\

      {\

      m+=i;\

      }

    展开全文
  • C语言的预处理

    2015-08-10 00:49:36
    1.1.预处理的功能... 3 1.2预处理的工作方式... 3 二.预处理指令... 4 2.1.预处理指令... 4 2.2.指令规则... 4 三.宏定义命令----#define. 4 3.1.无参数宏... 4 3.2带参数宏... 5 3.3.预处理操作符#和##. 6 ...
    目录
     
    一.预处理的工作方式... 3
    1.1.预处理的功能... 3
    1.2预处理的工作方式... 3
    二.预处理指令... 4
    2.1.预处理指令... 4
    2.2.指令规则... 4
    三.宏定义命令----#define. 4
    3.1.无参数的宏... 4
    3.2带参数的宏... 5
    3.3.预处理操作符#和##. 6
    3.3.1.操作符#. 6
    3.3.2.操作符##. 6
    四.文件包含------include. 6
    五.条件编译... 7
    5.1使用#if 7
    5.2使用#ifdef和#ifndef 9
    5.3使用#defined和#undef 10
    六.其他预处理命令... 11
    6.1.预定义的宏名... 11
    6.2.重置行号和文件名命令------------#line. 11
    6.3.修改编译器设置命令 ------------#pragma. 12
    6.4.产生错误信息命令 ------------#error 12
    七.内联函数... 13
     在嵌入式系统编程中不管是内核的驱动程序还是应用程序的编写,涉及到大量的预处理与条件编译,这样做的好处主要体现在代码的移植性强以及代码的修改方便等方面。因此引入了预处理与条件编译的概念。在C语言的程序中可包括各种以符号#开头的编译指令,这些指令称为预处理命令。预处理命令属于C语言编译器,而不是C语言的组成部分。通过预处理命令可扩展C语言程序设计的环境。
    一.预处理的工作方式
     1.1.预处理的功能
     在集成开发环境中,编译,链接是同时完成的。其实,C语言编译器在对源代码编译之前,还需要进一步的处理:预编译。预编译的主要作用如下:
    ●将源文件中以”include”格式包含的文件复制到编译的源文件中。
    ●用实际值替换用“#define”定义的字符串。
    ●根据“#if”后面的条件决定需要编译的代码。
     1.2预处理的工作方式
     预处理的行为是由指令控制的。这些指令是由#字符开头的一些命令。
    #define指令定义了一个宏---用来代表其他东西的一个命令,通常是某一个类型的常量。预处理会通过将宏的名字和它的定义存储在一起来响应#define指令。当这个宏在后面的程序中使用到时,预处理器”扩展”了宏,将宏替换为它所定义的值。
    #include指令告诉预处理器打开一个特定的文件,将它的内容作为正在编译的文件的一部分“包含”进来。例如:下面这行命令:
    #include<stdio.h>
    指示预处理器打开一个名字为stdio.h的文件,并将它的内容加到当前的程序中。预处理器的输入是一个C语言程序,程序可能包含指令。预处理器会执行这些指令,并在处理过程中删除这些指令。预处理器的输出是另外一个程序:原程序的一个编辑后的版本,不再包含指令。预处理器的输出被直接交给编译器,编译器检查程序是否有错误,并经程序翻译为目标代码。
     二.预处理指令
     2.1.预处理指令
     大多数预处理器指令属于下面3种类型:
    ●宏定义:#define 指令定义一个宏,#undef指令删除一个宏定义。
    ●文件包含:#include指令导致一个指定文件的内容被包含到程序中。
    ●条件编译:#if,#ifdef,#ifndef,#elif,#else和#dendif指令可以根据编译器可以测试的条件来将一段文本包含到程序中或排除在程序之外。剩下的#error,#line和#pragma指令更特殊的指令,较少用到。
     2.2.指令规则
     ●指令都是以#开始。#符号不需要在一行的行首,只要她之前有空白字符就行。在#后是指令名,接着是指令所需要的其他信息。
    ●在指令的符号之间可以插入任意数量的空格或横向制表符。
    ●指令总是第一个换行符处结束,除非明确地指明要继续。
    ●指令可以出现在程序中德任何地方。我们通常将#define和#include指令放在文件的开始,其他指令则放在后面,甚至在函数定义的中间。
    ●注释可以与指令放在同一行。
     三.宏定义命令----#define   
    使用#define命令并不是真正的定义符号常量,而是定义一个可以替换的宏。被定义为宏的标示符称为“宏名”。在编译预处理过程时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。在C语言中,宏分为有参数和无参数两种。
     3.1.无参数的宏
        其定义格式如下:
    #define 宏名  字符串
    在以上宏定义语句中,各部分的含义如下:
    ● #:表示这是一条预处理命令(凡是以“#”开始的均为预处理命令)。
    ●define:关键字“define”为宏定义命令。
    ●宏名:是一个标示符,必须符合C语言标示符的规定,一般以大写字母标示宏名。
    ●字符串:可以是常数,表达式,格式串等。在前面使用的符号常量的定义就是一个无参数宏定义。
    Notice:
    预处理命令语句后面一般不会添加分号,如果在#define最后有分号,在宏替换时分号也将替换到源代码中去。在宏名和字符串之间可以有任意个空格。
    Eg:#define PI 3.14
    在使用宏定义时,还需要注意以下几点:
    ●宏定义是宏名来表示一个字符串,在宏展开时又以该字符串取代宏名。这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。
    ●宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。
    ●宏名在源程序只能够若用引号括起来,则预处理程序不对其作宏替换。
    ●宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层替换。
    ●习惯上宏名可用大写字母表示,以方便与变量区别。但也允许用小写字母。
     3.2带参数的宏
     #define命令定义宏时,还可以为宏设置参数。与函数中的参数类似,在宏定于中的参数为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,还要用实参去代换形参。
    带参宏定义的一般形式为:
    #define 宏名(形参表)  字符串
    在定义带参数的宏时,宏名和形参表之间不能有空格出现,否则,就将宏定义成为无参数形式,而导致程序出错。
    Eg:#define ABS(x) (x)<0?-(x):(x)
     以上的宏定义中,如果x的值小于0,则使用一元运算符(-)对其取负,得到正数。带参的宏和带参的函数相似,但其本质是不同的。使用带参宏时,在预处理时将程序源代码替换到相应的位置,编译时得到完整的目标代码,而不进行函数调用,因此程序执行效率要高些。而函数调用只需要编译一次函数,代码量较少,一般情况下,对于简单的功能,可使用宏替换的形式来使用。
     3.3.预处理操作符#和##
     3.3.1.操作符#
      在使用#define定义宏时,可使用操作符#在字符串中输出实参。Eg:#define AREA(x,y) printf(“长为“#x”,宽为“#y”的长方形的面积:%d\n”,(x)*(y));
    3.3.2.操作符##
     与操作符#类似,操作符##也可用在带参宏中替换部分内容。该操作符将宏中的两个部分连接成一个内容。例如,定义如下宏:
    #define VAR(n)   v##n
    当使用一下方式引用宏:
    VAR(1)
    预处理时,将得到以下形式:
    V1
    如果使用以下宏定义:
    #define FUNC(n)  oper##n
    当实参为1时,预处理后得到一下形式:
    oper1
     四.文件包含------include
     
         当一个C语言程序由多个文件模块组成时,主模块中一般包含main函数和一


    些当前程序专用的函数。程序从main函数开始执行,在执行过程中,可调用当前


    文件中的函数,也可调用其他文件模块中的函数。
        如果在模块中要调用其他文件模块中的函数,首先必须在主模块中声明该函


    数原型。一般都是采用文件包含的方法,包含其他文件模块的头文件。
    文件包含中指定的文件名即可以用引号括起来,也可以用尖括号括起来,格式如


    下:
    #include< 文件名>

    #include“文件名”
    如果使用尖括号<>括起文件名,则编译程序将到C语言开发环境中设置好的 


    include文件中去找指定的文件。
    因为C语言的标准头文件都存放在include文件夹中,所以一般对标准头文件采用


    尖括号;对编程自己编写的文件,则使用双引号。如果自己编写的文件不是存放


    在当前工作文件夹,可以在#include命令后面加在路径。
     #include命令的作用是把指定的文件模块内容插入到#include所在的位置,当


    程序编译链接时,系统会把所有#include指定的文件链接生成可执行代码。文件


    包含必须以#开头,表示这是编译预处理命令,行尾不能用分号结束。
      #include所包含的文件,其扩展名可以是“.c”,表示包含普通C语言源程序。


    也可以是 “.h”,表示C语言程序的头文件。C语言系统中大量的定义与声明是以


    头文件形式提供的。
    通过#define包含进来的文件模块中还可以再包含其他文件,这种用法称为嵌套


    包含。嵌套的层数与具体C语言系统有关,但是一般可以嵌套8层以上。
     


    五.条件编译
     
    预处理器还提供了条件编译功能。在预处理时,按照不同的条件去编译程序的不


    同部分,从而得到不同的目标代码。使用条件编译,可方便地处理程序的调试版


    本和正式版本,也可使用条件编译使程序的移植更方便。
    5.1使用#if
    与C语言的条件分支语句类似,在预处理时,也可以使用分支,根据不同的情况


    编译不同的源代码段。
    #if 的使用格式如下:
    #if 常量表达式
       程序段
    #else
      程序段
    #endif
    该条件编译命令的执行过程为:若常量表达式的值为真(非0),则对程序段1进行


    编译,否则对程序段2进行编译。因此可以使程序在不同条件下完成不同的功能



    Eg:
    #define DEBUG 1
     
    int main()
    {
       int i,j;
       char ch[26];
      
       for(i='a';j=0;i<='z';i++,j++)
       {
           ch[j]=i;
           #if DEBUG
              printf("ch[%d]=%c\n",j,ch[j]);
           #endif
       }
       for(j=0;j<26;j++)
       {
           printf("%c",ch[j]);
       }
       return 0;
    }
    #if预编译命令还可使用多分支语句格式,具体格式如下:
    #if 常量表达式 1
        程序段 1
    #elif 常量表达式 2
        程序段 2
    … …
    #elif 常量表达式 n
        程序段 n
    #else
        程序段 m
    #endif
     
    关键字#elif与多分支if语句中的else if类似。
    Eg:
    #define os win
    #if os=win
        #include"win.h"
    #elif os=linux
        #include"linux.h"
    #elif os=mac
        #include"mac.h"
    #endif
     
    #if和#elif还可以进行嵌套,C89标准中,嵌套深度可以到达8层,而C99允许嵌


    套达到63层。在嵌套时,每个#endif,#else或#elif与最近的#if或#elif配对



    Eg:
    #define MAX 100
    #define OLD -1
     
    int main()
    {
    int i;
    #if MAX>50
    {  
        #if OLD>3
        {
            i=1;
        {
        #elif OLD>0
        {
            i=2;
        }
        #else
        {
            i=3;
        }
        #endif
    }
    #else
    {
        #if OLD>3
        {
            i=4;
        }
        #elif OLD>4
        {
            i=5;
        }
        #else
        {
            i=6;
        }
        #endif
    }
    #endif
    return 0;
    }
     


    5.2使用#ifdef和#ifndef
     
    在上面的#if条件编译命令中,需要判断符号常量定义的具体值。在很多情况下


    ,其实不需要判断符号常量的值,只需要判断是否定义了该符号常量。这时,可


    不使用#if命令,而使用另外一个预编译命令———#ifdef.
    #ifdef命令的使用格式如下:
    #ifdef 标识符
    程序段 1
    #else
        程序段 2
    #endif
    其意义是,如果#ifdef后面的标识符已被定义过,则对“程序段1”进行编译;


    如果没有定义标识符,则编译“程序段2”。一般不使用#else及后面的“程序2


    ”。
    而#ifndef的意义与#ifdef相反,其格式如下:
    #ifndef 标识符
        程序段 1
    #else
        程序段 2
    #endif
    其意义是:如果未定义标识符,则编译“程序段1”;否则编译“程序段2”。
     


    5.3使用#defined和#undef
     
    与#ifdef类似的,可以在#if命令中使用define来判断是否已定义指定的标识符


    。例如:
    #if defined 标识符
    程序段 1  
    #endif
    与下面的标示方式意义相同。
    #ifdef 标识符
        程序段 1
    #endif
    也可使用逻辑运算符,对defined取反。例如:
    #if ! define 标识符
        程序段 1
    #endif
    与下面的标示方式意义相同。
    #ifndef 标识符
        程序段 1
    #endif
    在#ifdef和#ifndef命令后面的标识符是使用#define进行定义的。在程序中,还


    可以使用#undef取消对标识符的定义,其形式为:
    #undef 标识符
    Eg:
    #define MAX 100
    ……
    #undef MAX
    在以上代码中,首先使用#define定义标识符MAX,经过一段程序代码后,又可以


    使用#undef取消已定义的标识符。使用#undef命令后,再使用#ifdef max,将不


    会编译后的源代码,因为此时标识符MAX已经被取消定义了。
     


    六.其他预处理命令
     
     6.1.预定义的宏名
     
           ANSI C标准预定义了五个宏名,每个宏名的前后均有两个下画线,避免


    与程序员定义相同的宏名(一般都不会定义前后有两个下划线的宏)。这5个宏名


    如下:
    ●  __DATE__:当前源程序的创建日期。
    ●  __FILE__:当前源程序的文件名称(包括盘符和路径)。
    ●  __LINE__:当前被编译代码的行号。
    ●  __STDC__:返回编译器是否位标准C,若其值为1表示符合标准C,否则不是标


    准C.
    ● __TIME__:当前源程序的创建时间。
    Eg:
    #include<stdio.h>
     
    int main()
    {
       int j;
       printf("日期:%s\n",__DATE__);
       printf("时间:%s\n",__TIME__};
       printf("文件名:%s\n",__FILE__);
       printf("这是第%d行代码\n",__LINE__);
       printf("本编译器%s标准C\n",(__STD__)?"符合":"不符合");
       return 0;
    }
     


    6.2.重置行号和文件名命令------------#line
     
    使用__LINE__预定义宏名赈灾编译的程序行号。使用#line命令可改变预定义宏


    __LINE__与__FILE__的内容,该命令的基本形如下:
     #line number[“filename”]
    其中的数字为一个正整数,可选的文件名为有效文件标识符。行号为源代码中当


    前行号,文件名为源文件的名字。命令为#line主要用于调试以及其他特殊应用



    Eg:
    1:#include<stdio.h>
    2:#include<stdlib.h>
     
    4:#line 1000
     
    6:int main()
    7:{
    8:    printf("当前行号:%d\n",__LINE__);
    9:    return 0;
    10:}
    在以上程序中,在第4行中使用#line定义的行号为从1000开始(不包括#line这行


    )。所以第5行的编号将为1000,第6行为1001,第7行为1002,第8行为1003.
     


    6.3.修改编译器设置命令 ------------#pragma
     
    #pragma命令的作用是设定编译器的状态,或者指示编译器完全一些特定的动作


    。#pragma命令对每个编译器给出了一个方法,在保持与C语言完全兼容的情况下


    ,给出主机或者操作系统专有的特征。其格式一般为:
    #pragma Para
    其中,Para为参数,可使用的参数很多,下面列出常用的参数:
    Message参数,该参数能够在编译信息输出窗口中输出对应的信息,这对于源代


    码信息的控制是非常重要的,其使用方法是:
    #pragma message(消息文本)
    当编译器遇到这条指令时,就在编译输出窗口中将消息文本显示出来。
    另外一个使用比较多得pragma参数是code_seg.格式如:
    #pragma code_seg([“section_name”[,section_class]])
    它能够设置程序中函数代码存放的代码段,在开发驱动程序的时候就会使用到它



    参数once,可保证头文件被编译一次,其格式为:
    #pragma once
    只要在头文件的最开始加入这条指令就能够保证头文件被编译一次。
     


    6.4.产生错误信息命令 ------------#error
     
    #error命令强制编译器停止编译,并输出一个错误信息,主要用于程序调试。其


    使用如下:
    #error 信息错误
    注意,错误信息不用双括号括起来。当遇到#error命令时,错误信息将显示出来



    例如,以下编译预处理器命令判断预定义宏__STDC__,如果其值不为1,则显示一


    个错误信息,提示程序员该编译器不支持ANSI C标准。
    #if __STDC__!=1
       #error NOT ANSI C
        #endif
     


    七.内联函数
     
    在使用#define定义带参数宏时,在调用函数时,一般需要增加系统的开销,如


    参数传递,跳转控制,返回结果等额外操作需要系统内存和执行时间。而使用带


    参数宏时,通过宏替换可再编译前将函数代码展开导源代码中,使编译后的目标


    文件含有多段重复的代码。这样做,会增加程序的代码量,都可以减少执行时间



    在C99标准钟,还提供另外一种解决方法:使用内联函数。
    在程序编译时,编译器将程序中出现的内联函数的调用表达式用内联函数的函数


    体来进行替代。显然,这种做法不会产生转去转回得问题。都是由于在编译时将


    函数体中的代码被替代到程序中,因此会增加目标代码量,进而增加空间的开销


    ,而在时间开销上不像函数调用时那么大,可见它是以增加目标代码为代码来换


    取时间的节省。
    定义内联函数的方法很简单,只要在定义函数头的前面加上关键字inline即可。


    内联函数的定义与一般函数一样。例如,定于一个两个整数相加的函数:
    #include<stdio.h>
    #include<stdlib.h>
     
    inline int add(int x,int y);
     
    inline int add(int x,int y)
    {
       return x+y;
    }
     
    int main()
    {
       int i,j,k;
       printf("请输入两个整数的值:\n");
       scanf("%d %d",&i,&j);
       k=add(i,j);
       printf("k=%d\n",k);
       return 0;
    }
    在程序中,调用函数add时,该函数在编译时会将以上代码复制过来,而不是像


    一般函数那样是运行时被调用。
    内联函数具有一般函数的特性,它与一般函数所不同之处在于函数调用的处理。


    一般函数进行调用时,要讲程序执行权转导被调函数中,然后再返回到调用到它


    的函数中;而内联函数在调用时,是将调用表达式用内联函数体来替换。在使用


    内联函数时,应该注意如下几点:
    ●    在内联函数内部允许用循环语句和开关语句。
    ●    内联函数的定义必须出现在内联函数第一次被调用之前。
    其实,在程序中声明一个函数为内联时,编译以后这个函数不一定是内联的,
    即程序只是建议编译器使用内联函数,但是编译器会根据函数情况决定是否使用


    内联,所以如果编写的内联函数中出现循环或者开关语句,程序也不会提示出错


    ,但那个函数已经不是内联函数了。
        一般都是讲一个小型函数作为内联函数。
    展开全文
  • C语言的预处理和条件编译指令 预处理简介 C语言由源代码生成的各阶段如下: C源程序->编译预处理->编译->优化程序->汇编程序->链接程序->可执行文件 其中 编译预处理阶段,读取c源程序,...

    C语言的预处理和条件编译指令

    在这里插入图片描述

    预处理简介

    • C语言由源代码生成的各阶段如下:

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

    其中 编译预处理阶段,读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。或者说是扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。预处理过程先于编译器对源代码进行处理。

    ​ 在C 语言中,并没有任何内在的机制来完成如下一些功能:在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码。要完成这些工作,就需要使用预处理程序。尽管在目前绝大多数编译器都包含了预处理程序,但通常认为它们是独立于编译器的。预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并 对源代码进行响应的转换。预处理过程还会删除程序中的注释和多余的空白字符。

    • 伪指令(或预处理指令)定义:

    ​ 预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。下面是部分预处理指令:

    指令 用途

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

    预处理指令

    预处理指令主要包括以下四个方面:

    条件编译指令

    程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。条件编译指令将决定那些代码被编译,而哪些是不被编译的。可以根据表达式的值或者某个特定的宏是否被定义来确定编译条件。下面介绍常用的条件编译指令。

    • #ifdef、#else#endif 指令

      示例:

      #ifdef MAVIS
           #include "horse.h" //如果已经用#define定义了MAVIS,则执行下面的指令
      	 #define STABLES 5
      #else
      	#include "cow.h"    //如果没有用#define定义MAVIS,则执行下面的指令
          #define STABLES 15
      #endif
      

      上面的缩进格式只有侥幸的编译器和ANSI标准才支持,太旧的编译器,必须左对齐所有的指令或至少左对齐#号。

      #ifdef指令说明,如果预处理器已经定义了后面的标识符(MAVIS),则则行#else#endif指令之前所有指令并编译所有C代码。如果预处理器未定义MAVIS,且有#else指令,则执行#else#endif指令之间的所有代码。

    • **#ifndef指令 **

      #ifndef指令与ifdef指令用法类似,也可以和#else、#endif一起使用,但是他们的逻辑相反。#ifndef指令判断后面的标识符是否未定义,常用于定义之前未定义的常量。通常,包含多个头文件时,其中的头文件可能包含了相同的宏定义,**#ifndef指令可以防止相同的宏被重复定义。**在首次定义一个宏的头文件中用#ifndef指令激活定义,随后的其他头文件中的定义都被忽略。如下示例:

      /*arrays.h*/
      #ifndef SIZE 100
              #define SIZE 100
      #endif
      

      **#ifndef指令还有另一种用法。**假设有上面的arrays.h头文件,然后把下面一行代码放入一个头文件中:

      #include "arrays.h"

      SIZE被定义为100。但是如果把下面的代码放入该头文件:

      #define SIZE 10
      #include "arrays.h"
      

      因为Size已经被设置为10,因此会调过arrays.h中的#define SIZE 100这行diamante,防止重复宏定义。**#ifndef指令通常用于防止多次包含一个文件。**因此应该像下面这样设置头文件:

      /*things.h*/
      #ifndef THING_H_
      	#define THINGS_H_
          /*省略了头文件中其他内容*/
      #endif
      

      假设该文件被包含了多次,当预处理器首次发现该文件被包含时,THINGS_H_是未定义的,所以定义了THINGS_H_,并接着处理该文件的其他部分。当预处理器第二次发现该文件被包含时,THINGS_H_是已定义的,所哟预处理器调过了该文件的其他部分。

      如何确保待测试表示符没有在别处定义?实现的供应商这样做:用文件名作为标识符、使用大写字符、下划线代替文件名中的点字符、用下划线字符做前缀或后缀。例如stdio.h头文件,有许多类似的代码:

      #ifndef _STDIO_H
      #define _STDIO_H
      //省略了文件的内容
      #endif
      

      我们也可以这样做,但是由于标准中使用了下划线作为前缀,因此,我们自己的代码汇总不要这样写,避免与标准头文件中的宏发生冲突。应该像下面是一个示例那样写:

      // names.h --避免重复包含的头文件写法
      
      #ifndef NAMES_H_
      #define NAMES_H_
      
      // constants
      #define SLEN 32
      
      // structure declarations
      struct names_st
      {
          char first[SLEN];
          char last[SLEN];
      };
      
      // typedefs
      typedef struct names_st names;
      
      // function prototypes
      void get_names(names *);
      void show_names(const names *);
      char * s_gets(char * st, int n);
      
      #endif
      
    • **#if#elif**指令

      #if指令检测跟在制造另关键字后的常量表达式。如果表达式为真,则编译后面的代码,知道出现#else、#elif#endif为止;否则就不编译。#endif用于终止#if预处理指令。

      示例:

      #if SYS ==1
      	#include "ibmpc.h"
      #elif SYS ==2
      	#include "vax.h"
      #elif SYS ==3
      	#include "mac.h"
      #else
      	#include "general.h"
      #endif
      

      还有一种写法。define是一个预处理运算符,如果它的参数是用#define定义过,则返回1;否则返回0。这样它可以与#elif一起使用。因此上面的代码还可以写成下面的形式:

      #if define (IBMPC)
      	#include "ibmpc.h"
      #elif define (VAX)
      	#include "vax.h"
      #elif define (MAC)
      	#include "mac.h"
      #else
      	#include "general.h"
      #endif
      

      如果在VAX机上运行着几行代码,那么应该在文件前面用下面的代码定义VAX:

      #define VAX

    条件还有一个好处是让程序更容易移植,改变文件开头的几个关键定义,即可根据不同的系统设置不同的值和包含不同的文件。

    预定义宏

    预编译程序可以识别一些特殊的符号。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。C标准规定了一些预定义宏,如下表所示:

    表1 预定义宏

    含义
    __DATE__ 预处理的日期("Mmm dd yyyy"形式的字符串字面量,如Nov 23 2013)
    __FILE__ 表示当前源代码文件名的字符串字面量
    __LINE__ 表示当前源代码文件中行号的整型常量
    __STDC__ 设置为1时,表明实现遵循C标准
    STDC_HOSTED 本机环境设置为1;否则设置为0
    __STDC_VERSION__ 支持C99标准,设置为199901L;支持C11设置为201112L
    __TIME__ 翻译代码的时间,格式为"hh:mm:ss"

    其中一些预定义宏时C99新增的,如果使用GCC编译器,必须设置-std=c99或-std=c11。

    示例:

    // predef.c -- predefined identifiers
    #include <stdio.h>
    void why_me();
    
    int main()
    {
        printf("The file is %s.\n", __FILE__);
        printf("The date is %s.\n", __DATE__);
        printf("The time is %s.\n", __TIME__);
        printf("The version is %ld.\n", __STDC_VERSION__);
        printf("This is line %d.\n", __LINE__);
        printf("This function is %s\n", __func__);
        why_me();
        
        return 0;
    }
    
    void why_me()
    {
        printf("This function is %s\n", __func__);
        printf("This is line %d.\n", __LINE__);
    }
    

    该程序的输出如下:

    The file is predef.c.
    The date is Jun 14 2017.
    The time is 14:06:55.
    The version is 199901.
    This is line 11.
    This function is main
    This function is why_me
    This is line 21.
    

    #line和#error指令

    #line指令重置__LINE____FILE__宏报告的行号和文件名。可以这样使用#line:

    #line 1000    //把当前行号重置为1000
    #line 10 "cool.c"  //把行号重置为10,把文件名重置为cool.c
    

    #error指令让预处理器发出一条错误信息,该信息包含指令中的文本。如果可能的话,编译过程应该中断。可以这样使用#error指令:

    #if __STDC_VERSION_!=201112L
    #error Not C11
    
    #endif
    

    编译以上代码后,输出如下:

    $ gcc newish.c
    newish.c:14:2: error: #error Not C11
    $ gcc -std=c11 newish.c
    $
    

    如果编译器支持旧版标准,则会编译失败,如果支持C11标准,就能成功编译。


    参考资料:

    [1] 史蒂芬・普拉达. C Primer Plus (第6版) 中文版[M]. 人民邮电出版社, 2016.

    [2] C语言条件编译及编译预处理阶段.–来源

    展开全文
  • 预处理是C语言的一个重要的功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理命令部分做处理,处理完毕自动进入对源程序的编译。任何C语言程序都有一个预处理...
  • c语言的预处理功能

    2013-05-26 23:24:54
    详细介绍c语言的预处理功能
  • C语言——预处理

    2021-05-21 09:41:54
    一、预处理 1.预处理定义 预处理是指在进行编译(词法扫描和...合理地使用预处理功能编写程序便于阅读、修改、 移植和调试,也有利于模块化程序设计。 4.C语言编译分为几个部分: 第一阶段:预处理: .c —> .i
  • c语言的预处理#,##的详细功能说明****1.对于#号的预处理说明**** 在C语言的宏中,#的功能举例来说 1.#define int int32; 即在预处理过程中将int32替换为int 2.#define g(x) #x #用于把宏定义中的参数x两端加上字符...
  • C语言之预处理指令和宏定义 一.预处理命令基本介绍 1.以#号开头命令称为预处理命令。...5.C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等,合理使用它们会使编写程序便于阅读、修改、
  • C语言中预处理命令#define的用法 define是C语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程...预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预
  • C语言预处理指令

    2020-04-24 00:13:29
    C语言的预处理指令如下: 1:#开头 2:define,undef,include,if ifdef,ifndef,else,elif,endif,line,error,pragma 3:实参 4:换行符 预处理器主要功能如下:注意预处理器只处理源文件 1:条...
  • C语言中,并没有任何内在机制来完成如下一些功能:在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码。要完成这些工作,就需要使用预处理程序。尽管在目前绝大多数编译器都包含
  • 这篇博客主要讲下C语言预处理那些事,其实做事还是很多,这里都来一一理清。说句题外话,最近开学了,可能每天花大量时间整理以前学习知识了,但还是会坚持每天写一篇,积累就是财富。 2、预处理的作用...
  • C语言-预处理

    2014-03-26 09:43:10
    预处理 概述  在前面各章中,已多次使用过以“#”号开头的预处理命令。如包含命令# include,宏定义命令# define...预处理是C语言的一个重要功能, 它由预处理程序负责完成。当对一个源文件进行编译时, 系统将自动引
  • c语言预处理命令

    2020-02-10 21:59:27
    c提供的预处理功能有: 宏定义 文件包含 条件编译 为了与其她c语句区分,命令经常以符号“#”开头。 宏定义 #define 标识符 字符串 可以避免反复输入字符串,后面不加;宏定义在默认时的有效范围是全部。也可以...
  • C语言预处理

    2014-08-20 11:41:27
    1.1.预处理的功能... 3 1.2预处理的工作方式... 3 二.预处理指令... 4 2.1.预处理指令... 4 2.2.指令规则... 4 三.宏定义命令----#define. 4 3.1.无参数宏... 4 3.2带参数宏... 5 3.3.预处理操作符#...
  • C语言的程序中可包括各种以符号#开头的编译指令,这些指令称为预处理命令。预处理命令属于C语言编译器,而不是C语言的组成部分。通过预处理命令可扩展C语言程序设计的环境。 一.预处理的工作方式 1.1.预处理的...
  • C语言预处理命令

    千次阅读 2014-03-22 22:45:28
    1.前沿  ANSI C标准规定可以在C源程序中加入一些“预处理命令”,以改进程序设计环境,提高编程效率。这些预处理命令不是C语言本身的组成部分,不能直接对它们进行编译。...C提供的预处理功能主要有以下3种: 1.宏定
  • 预处理功能常用主要以下3种: 1. 宏定义 2. 文件包含 3. 条件编译 这些功能分别是用宏定义指令,文件包含指令和条件编译指令来实现,为了与C语言相区别,这些指令以符号“#”开头,指令后
  • c语言预处理命令

    2015-01-11 13:13:06
    在前面各章中,已多次使用过以“#”号开头的预处理命令。如包含命令#include,宏...预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分
  • c语言预处理

    2007-03-28 22:38:00
    C语言提供了多种预处理功能,如宏定义、文件包含、 条件编译等。 宏定义 在C语言源程序中允许用一个标识符来表示一个字符串, 称为“宏”。被定义为“宏”标识符称为“宏名”。在编译预处理时,对程序中所有出现...
  • C语言三种预处理功能

    千次阅读 2018-08-12 02:09:14
    C语言三种预处理功能   伪指令(或预处理指令)定义: 预处理指令是以#号开头代码行。#号必须是该行除了任何空白字符外第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数空白字符。整行...
  • C08 C语言预处理命令

    2018-12-26 10:46:00
    C语言的预处理功能有: 宏定义 文件包含 条件编译 预处理命令以符号“#”开头。、 宏定义 不带参数的宏定义 宏定义又称为宏代换、宏替换,简称“宏”。格式: #define 标识符 文本 其中的...

空空如也

空空如也

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

c语言的预处理功能

c语言 订阅