zoukankan      html  css  js  c++  java
  • 《笨办法学C》笔记之C预处理器

    预处理(或称预编译)是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理指令指示在程序正式编译前就由编译器进行的操作,可放在程序中任何位置。

    预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。

    C语言的预处理器主要可以做到:

    • 文件包含
    • 条件编译
    • 宏定义

    合理使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。

    常用的预处理器指令

    序号 指令 描述
    1 #define 定义宏
    2 #include 包含一个源代码文件
    3 #undef 取消已定义的宏
    4 #ifdef 如果宏已经定义,则返回真
    5 #ifndef 如果宏没有定义,则返回真
    6 #if 如果给定条件为真,则编译下面代码
    7 #else #if 的替代方案
    8 #elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
    9 #endif 结束一个 #if……#else 条件编译块
    10 #error 当遇到标准错误时,输出错误消息
    11 #pragma 使用标准化方法,向编译器发布特殊的命令到编译器中

    文件包含

    C语言使用#include标识符,来表明.c文件内将要调用.h头文件所定义的数据结构或函数。

    #include <stdio.h>
    

    条件编译

    C语言的预处理器,通过前表中第2-9项指令能够做到:对.c文件中的代码或者对.h文件中的定义有选择的编译。

    其编译的条件是:首先满足宏定义的条件,然后才会执行编译。

    #ifdef XXX
       /* <C code> */
    #endif
    

    如果编译器读取到这段代码的时候,XXX已经定义了,那么<C code>也会得到编译,否则编译器就会忽略这段代码,直到#endif位置。

    宏定义

    预定义宏

    ANSI C 定义了许多宏。在编程中可以使用这些宏,但是不能直接修改这些预定义的宏。

    描述
    DATE 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。
    TIME 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。
    FILE 这会包含当前文件名,一个字符串常量。
    LINE 这会包含当前行号,一个十进制常量。
    STDC 当编译器以 ANSI 标准编译时,则定义为 1。
    # include <stdio.h>
    
    main()
    {
       printf("File :%s
    ", __FILE__ );
       printf("Date :%s
    ", __DATE__ );
       printf("Time :%s
    ", __TIME__ );
       printf("Line :%d
    ", __LINE__ );
       printf("ANSI :%d
    ", __STDC__ );
    
    }
    

    当上面的代码(在文件 test.c 中)被编译和执行时,它会产生下列结果:

    File :test.c
    Date :Jun 2 2012
    Time :03:36:24
    Line :8
    ANSI :1
    

    自定义宏

    常量定义

    最简单的宏定义就是常量定义。

    #define CONST 123
    #define MESSAGE "This is a pre-defined msg"
    

    这句定义就指定后文中的CONST都要被替换为123。如果遇到#undef CONST,那后面的CONST就不会被这样替换了。

    需要注意的是,宏定义的常量没有占用内存。编译器会在编译后面的代码中,直接将所有CONST替换为123,将所有MESSAGE替换为"This is a pre-defined msg"。编译后的二进制可执行文件不会意识到CONSTMESSAGE的存在。因为,这些定义过的宏在编译的第一步——“预处理”中就被替换完毕了。

    函数模版

    C预处理器还能够预定义函数模版。例如:

    #define MAX(x,y) ((x) > (y) ? (x) : (y))
    
    int main() {
    	int i = 12;
    	int j = 33;
    	int res = MAX(i, j); 
    	return res;
    }
    

    上例中,i,j被带入MAX宏中进行计算,res存储着计算结果。

    这和函数调用很像,然而两者并不相同。

    编译器在预处理阶段,直接将预定义的函数模版在代码中展开。在二进制文件执行过程中,并不存在函数调用过程中的栈帧调度、参数传递以及结果回传等等,也不会占用内存空间。如果去查看等价的汇编代码,也不会发现call指令。因为预处理阶段结束后,函数模版在嵌入实际参数之后,成为代码的一部分,而不是函数形式。

    小小总结

    显而易见,宏定义不占用内存空间,不涉及函数的栈帧切换,在性能方面有很强的优势。

    但是,宏定义也有严重的问题。那就是这里面没有任何类型检查。编译器会无条件的直接做替换,而如果存在类型不匹配,只有在编译时替换完成后才能发现。

    参考资料

  • 相关阅读:
    序列化-请求数据校验。。。。。。
    python自动化测试-D5-学习笔记之二(常用模块之加密模块)
    python自动化测试-D5-学习笔记之二(常用模块之os,sys,random,string,time)
    python自动化测试-D5-学习笔记之二(常用模块之json模块)
    python自动化测试-D5-学习笔记之一(argv的使用)
    python自动化测试-D5-学习笔记之一(函数补充,内置函数,map,filter,eval)
    python习题:写一个函数打印两个字典中不一样的key和value
    python习题:用文件方式编写购物车程序,添加,查看和删除
    python习题:时间格式转换
    python习题:双色球
  • 原文地址:https://www.cnblogs.com/rim99/p/6415564.html
Copyright © 2011-2022 走看看