• C 语言中的预处理


    C 语言中以 # 开头的就是预处理指令,例如 #include

    预处理指令的用途

    所有的预处理指令都会在 GCC 编译过程的预处理步骤解析执行,替换为对应的内容。在下一步编译过程中,看不到任何预处理信息,只需要对独立的程序文件进行操作即可。

    预处理指令的类型

    • 包含类 #include 头文件
      通过 #include 引入的头文件,会在 GCC 编译过程中的预处理步骤进行展开,替换为完整的头文件内容。
    • 宏定义类 #define 宏名 宏体
      定义的宏在预处理时不进行语法检查,原样替换。等到编译的时候再进行语法检查。宏体如果有多个组成部分时,通常要加括号,防止发生优先级的问题。
      • 宏变量:支持表达式。例如 #define MY_VAR 3+4,使用的时候如果是这样 int i = MY_VAR * 5,会在预处理时替换为 int i = 3+4 * 5,最终得到的 i 是23。而 #define MY_VAR (3+4)则无次问题
      • 宏函数:可以将定义的宏当做函数处理。例如 #define MY_FUN(x) (3+x),使用 int i = MY_FUN(4)*5 得到的 i 是 35。
    • 条件编译 #ifdef #ifndef #else #endif
    • 预定义宏:全部以两个下划线开始和结束,方便调试
      • FILE:文件名
      • FUNCTION:函数名
      • LINE:行号,数值
      • DATE:编译的日期
      • TIME:编译的时刻

    例如,对于这个 C 文件:

    #include <stdio.h>
    
    int main()
    {
        printf("file is:%s, function is:%s, line is:%d, %s, %s", __FILE__, __FUNCTION__, __LINE__, __DATE__, __TIME__);
    }
    

    执行结果如下:

    file is:define.c, function is:main, line is:5, Jan 19 2019, 15:10:57
    

    宏定义

    预处理时,会把所有出现宏名的地方自动替换为对应的宏值。

    #define 宏名 宏值
    

    宏定义的规则:

    • 宏值中可以有其他宏的名字,预处理时也会被替换,例如:
    #define PI 3.14
    #define PI2 PI * 2
    
    • 宏值超过一行时,可以用 换行,例如:
    #define PRT printf("%f ", PI); 
    			printf("%f", PI2)
    
    • 如果不需要宏值,可以定义无值的宏:
    #define DEBUG
    
    • 预定义宏可以在任何地方直接使用。
    • 宏定义结尾不能加分号 ;,否则会被替换导致异常。

    综合示例:

    #include <stdio.h>
    
    #define PI 3.14
    #define PI2 PI*2
    #define PRT printf("%f ", PI); 
    			printf("%f
    ", PI2)
    #define MIN(a, b) ((a) < (b) ? (a) : (b))
    
    int main()
    {
    	PRT;
    	printf("%d", MIN(2+3*5, 666));
    }
    

    有参数的宏

    可以带一个或多个参数。有参数的宏的原则是:

    一切地方都有加括号,放在因为运算的优先级导致异常

    • 整个宏值要加括号
    • 每个参数都有加括号
    #include <stdio.h>
    
    #define MIN(a, b) ((a) < (b) ? (a) : (b))
    #define FUN1(a) (a * 5)
    #define FUN2(a) (a) * 5
    
    int main()
    {
    	printf("%d
    ", MIN(2+3*5, 666));
    	printf("%d
    ", FUN1(5 + 6));		//5 + 6 * 5 = 35
    	printf("%d
    ", 100 / FUN2(5));	//100 / 5 * 5 = 100
    }
    

    宏定义的优缺点

    • 优点:有参数的宏定义比函数速度快,无参数的宏定义可以在C语言的各个版本中使用。
    • 缺点:无法进行参数类型的检查,可能编译通过但运行时报错。

    可以使用 inline 内联函数替代有参数的宏定义,速度快的同时还可以检查参数类型。

    对于 C99 之后的版本,可以使用常量定义来代替无参数的宏定义:

    const double PI 3.14;
    

    条件预处理

    通过条件预处理指令,可以在预处理阶段控制要编译的代码,从而实现条件预编译。对编译器来说这个步骤是透明的,它只需要编译所有它看见的代码即可。例如:

    #include <stdio.h>
    #define DEBUG
    
    int main()
    {
    #ifdef DEBUG
    	printf("debug info");// 只有定义了 DEBUG,才会执行这段代码
    #endif
    	printf("hello world
    ");
    	return 0;
    }
    

    当然,在文件中直接定义宏的话,每次都有修改源文件,不灵活。GCC 提供了一个指定宏定义的选项 gcc -D,例如 gcc -DDEBUG -o build 1.c 会自动在代码中添加一段定义 DEBUG 的指令 #define DEBUG,这样就可以在上面的源代码中删除 #define DEBUG 这一行了。

    预处理中 # 和 ## 的区别

    在宏体中(宏名中不可以使用)可以使用一个或两个 # 作为前缀

    • #:字符串化,如果需要向宏方法中传字符串参数时可以使用
    • ##:连接符号,用于连接字符串和变量

    示例:

    #include <stdio.h>
    #define MY1(x) #x		//传入的参数转为字符串
    #define MY2(x) day##x 	//传入的参数自动拼接到 day 后面,例如出入1时为 day1
    
    int main()
    {
    	int day1 = 666;
    	
    	printf(MY1(abc
    )); // 打印 abc 字符串后换行
    	printf("%d", MY2(1));// 打印 666,即变量 day1
    }
    
  • 相关阅读:
    eclipse集成JBPM
    一个简单的NoSQL内存数据库—Berkeley DB基本操作的例子
    Berkely DB Java Edition学习笔记
    jsp和java获取文件或路径
    【Bzoj 1835 基站选址】
    【The Time Traveller's Wife】
    【Codeforces Round #430 (Div. 2) A C D三个题】
    【AIM Tech Round 4 (Div. 2) D Prob】
    【Codeforces AIM Tech Round 4 (Div. 2) C】
    【QAQ的Minecraft】
  • 原文地址:https://www.cnblogs.com/kika/p/10851512.html
Copyright © 2020-2023  润新知