• C primer plus学习笔记 —— 12、宏定义



    预处理器:在程序运行之前查看程序。
    预处理器把符号替换成表示的内容不做表达式计算和求值,就 是简单的转换文本。

    #define

    格式为#define
    当出现该条语句的时候,他表示把name替换成stuff
    预处理器查找一行中以#开始的预处理器指令。指令可以出现在源文件中的任何地方。他的定义从开始一直到源文件末尾都有效。所以不要在末尾加分号

    #include 
    #define TWO 2        /* you can use comments if you like   */
    #define OW "Consistency is the last refuge of the unimagina\
    tive. - Oscar Wilde" /* a backslash continues a definition */
    /* to the next line                   */
    #define FOUR  TWO*TWO
    #define PX printf("X is %d.\n", x)
    #define FMT  "X is %d.\n"
    
    int main(void)
    {
        int x = TWO;
        
        PX;
        x = FOUR;
        printf(FMT, x);
        printf("%s\n", OW);
        printf("TWO: OW\n");
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    注意宏换行时要与上面一行对齐,不然空格也会算进去。

    使用宏声明数组

    #define LIMIT 20 
    const int LIM = 50; 
    
    static int data1[LIMIT];    // 有效
    static int data2[LIM];     // 无效 
    const int LIM2 = 2 * LIMIT;  // 有效 
    const int LIM3 = 2 * LIM;   // 无效
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    数组的声明可必须是

    1. 整型常量的组合,比如宏定义
    2. 枚举
    3. sizeof表达式
      而const声明的数值不可以。

    宏定义的空格

    #define EIGHT 4 * 8
    #define EIGHT2 4*8
    
    • 1
    • 2

    这两个宏定义是不同的,一个是带空格的,而另一个不带空格。空格也是宏定义中的一部分。

    重定义

    #define LIM 20
    #define LIM 20
    
    • 1
    • 2

    可以重定义宏,但是重定义的值必须和原值相同。如果不同,则有些编译器会报错,有些会给出警告。

    #undef

    取消定义 #undef

    在这里插入图片描述
    移除LIMIT定义后,就可以给LIMIT重新定义成一个新值。如果没有定义LIMIT,取消定义一样有效。

    宏定义中使用参数

    看上去像函数调用,但是和函数调用完全不同。
    看下面实例

    #include 
    #define SQUARE(X) X*X
    #define PR(X)   printf("The result is %d.\n", X)
    int main(void)
    {
        int x = 5;
        int z;
        
        z = SQUARE(x); //z = 5*5
        PR(z); //The result is 25.
        z = SQUARE(2); //z = 2*2
        PR(z); //The result is 4.
        PR(SQUARE(x+2)); //z = 5+2*5+2 The result is 17.
        PR(100/SQUARE(2));//PR(100/2*2) The result is 100.
        printf("x is %d.\n", x); // 5
        PR(SQUARE(++x)); //++x*++x = 6*7 The result is 42.
        printf("After incrementing, x is %x.\n", x); //7
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    宏参数作为字符串

    这个是使用在printf中

    #define PSQR(x) printf("The square of "#x" is %d.\n",((x)*(x)))
     int main(void)
    {
        int y = 5;
        PSQR(y); //The square of y is 25.
        PSQR(2 + 4); //The square of 2 + 4 is 36.
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里的参数会替换字符串中的"#x"

    粘合剂符号##

    #include 
    #define XNAME(n) x ## n
    #define PRINT_XN(n) printf("x"#n" = %d\n", x ## n);
    
    int main(void)
    {
        int XNAME(1) = 14;  // int x1 = 14;
        int XNAME(2) = 20;  // int x2 = 20;
        int x3 = 30;
        PRINT_XN(1);        // printf("x1 = %d\n", x1); x1 = 14
        PRINT_XN(2);        // printf("x2 = %d\n", x2); x2 = 20
        PRINT_XN(3);        // printf("x3 = %d\n", x3); x3 = 30
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可变参数宏 ... _ _VA _ ARGS_ _ 打印

    可变参数宏是在宏中printf使用的

    #include 
    #include 
    #define DEBUG(format, ...)	printf(format, __VA_ARGS__)
     
    #define PR(X, ...) printf("Message " #X " : "  __VA_ARGS__)
    
    int main(void)
    {
        DEBUG("%s: %d\r\n", "debug", 100); //printf("%s: %d\r\n", "debug", 100)    debug: 100
        double x = 48;
        double y;
        
        y = x*x;
        PR(1, "x = %g\n", x); //Message 1: x = 48
        PR(2, "x = %.2f, y = %.4f\n", x, y); // Message 2: x = 48.00, y = 2304.0000
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    条件宏

    #include 
    #define JUST_CHECKING //空定义
    #define LIMIT 4
    
    int main(void)
    {
        int i;
        int total = 0;
    
        for (i = 1; i <= LIMIT; i++)
        {
            total += 2*i*i + 1;
    #ifdef JUST_CHECKING
            printf("i=%d, running total = %d\n", i, total);
    #endif
        }
        printf("Grand total = %d\n", total);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    #ifndef主要有两种作用

    1. 防止某个宏被重复定义,如下代码,如果这个宏没有被定义过,那么我们就定义它。如果定义过就不定义了,这样在多个头文件的时候,就不会有重定义的错误。
    #ifndef SIZE
    	#define SIZE 100
    #endif
    
    • 1
    • 2
    • 3
    1. 在头文件中使用,防止多次包含同一个文件(最常用的)。当包含头文件很多时,那么我们很可能在不同头文件中都包含了一个头文件,那么如果有些声明比如一些结构类型声明,出现两次就会报错。所以我们就使用下面的方式来防止这种情况。
    // names.h --revised with include protection
    #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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    命令行定义

    条件判断#ifdef NAMES我们可以在代码中使用#define NAMES来定义一个宏,还有另一种方式我们可以在编译的时候使用命令行就定义一个宏,而无需在代码中定义。
    比如我设定一个数组大小

    int array[ARRAY_SIZE];
    
    • 1

    我在编译的时候添加选项
    gcc -DARRAY_SIZE=100 main.c
    这时,就相当于我在代码最开始的位置定义了一个宏

    #define ARRAY_SIZE 100
    
    • 1

    宏与函数

    宏非常频繁的用于简单的计算比如

    #define MAX(a,b)  ( (a) > (b) ? (a) : (b) )
    
    • 1

    区别

    1. 代码长度:函数少,而每次使用宏都要插入其中
    2. 执行速度:函数慢调用返回额外开销,宏快,因为直接替换
    3. 操作符优先级:函数符合优先级,宏是从左到右依次执行除非加括号否则产生非预料结果
    4. 参数类型:与类型无关比如上面这个例子,我们不需要指定变量的类型,他可以接受任意类型。而函数需要显示指定参数类型。

    内联函数

    把函数变成内联,编译器可能会把函数代码直接替换掉函数调用。
    目的:把函数变为内联,目的是尽可能快的调用该函数。

    inline int fun(int x)
    {
    	return x * x;
    }
     
    int main()
    {
    	int b = fun(2 + 3);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    内联会把函数变为下面形式

    inline int fun(int x)
    {
    	return x * x;
    }
     
    int main()
    {
    	int b =  (2 + 3) * (2 + 3);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    未给内联函数预留代码块, 所以无法获得内联函数地址,并且内联函数无法在调试器中显示。
    一般我们不在头文件中放可执行代码,但是内联函数是个特例。
    内联函数一般是比较简短的函数,我们可以把它写成内联形式。

    预定义宏

    下面是一些预定义的宏,可以直接使用,在标准库中

    // predef.c -- predefined identifiers
    #include 
    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 Oct 29 2022.
    // The time is 12:42:30.
    // The version is 201710.
    // This is line 11.
    // This function is main
    // This function is why_me
    // This is line 21.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    #include 宏

    当预处理器发现#include指令,就会简单的删掉这条指令然后把该文件包含到当前文件中。
    在这里插入图片描述
    双引号,会查找当前工作目录中如果没有再去系统目录中查找。但是查找还是要看编译器,可能会查找当前源文件所在目录,也可能从项目文件所在目录查找。
    所以<>用于函数库文件的包含,而“”用于本地文件的包含。

    当出现了多重文件包含的情况,我们就是用上面条件宏的处理方式来防止多重头文件包含发生。

    头文件中存放的东西

    在这里插入图片描述
    全局变量
    在这里插入图片描述

    其他宏

    #error

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

    #if _ _STDC_VERSION_ _ != 201112L
    #error Not C11
    #endif
    
    • 1
    • 2
    • 3

    如果判断有问题,则编译会直接报错中断
    $ gcc newish.c
    newish.c:14:2: error: #error Not C11

    #line

    一种用途较小的指令
    指令重置#line

    #line 1000      // 把当前行号重置为1000
    #line 10 "cool.c"  // 把行号重置为10,把文件名重置为cool.c
    
    • 1
    • 2
    #include 
     
    int main(void)
    {
        printf("-- %s: %d -- \n", __FILE__, __LINE__);
    #line 201 "foo.c"
        printf("-- %s: %d -- \n", __FILE__, __LINE__);
    #line 101 "foo.c"
        printf("-- %s: %d -- \n", __FILE__, __LINE__);
     
        return 0;
     }
     -- ./main.c: 5 --
    -- foo.c: 201 --
    -- foo.c: 101 --
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    #pragma

    #pragma指令的作用是:用于指定计算机或操作系统特定的编译器功能。
    他的作用因编译器而异。所以通常他是不可移植的。

    #pragma once
    大家应该都知道:指定该文件在编译源代码文件时仅由编译器包含(打开)一次。
    使用 #pragma once 可减少生成次数,和使用预处理宏定义来避免多次包含文件的内容的效果是一样的,但是需要键入的代码少,可减少错误率,例如:

    //使用#progma once
    #pragma once  
    // Code placed here is included only once per translation unit 
    
    //使用宏定义方式
    #ifndef HEADER_H_ 
    #define HEADER_H_  
    // Code placed here is included only once per translation unit  
    #endif // HEADER_H_  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    https://blog.csdn.net/qq_40569221/article/details/117734115

  • 相关阅读:
    MapReduce的编程开发-格式整理
    学习编程的基本步骤
    圆满完成重保网络防护行动,持安科技获西南兵工致信感谢
    Docker安装入门教程
    慢SQL排查定位
    1,2-二苯基-1,2-二(4-苄溴基苯基)乙烯;TPE-MB结构式
    java毕业设计校园快递代领系统mybatis+源码+调试部署+系统+数据库+lw
    LeetCode[105]从前序与中序遍历序列构造二叉树
    怎样做一个好的汇报
    新能源国标接入随想
  • 原文地址:https://blog.csdn.net/chongbin007/article/details/127504084