1 不带参宏定义

这种用法是最简单最常见的一种方式,如下:

#define PAI 3.14

注意

  • 宏定义原则上用大写表示,虽然这并不是规定
  • 由于宏定义是预处理指令,并非是C语言语句,所以末尾不必加分号

它的功能是在程序中若出现了PAI,就把它替换3.14,示例程序如下:

#include <stdio.h>
#define PAI 3.14 // 不带参宏定义

int main() {
    double r = 2;
    printf("半径为%.2lf的圆的周长为%.2lf", r, 2*PAI*r);
    return 0;
}

2 带参宏定义

使用方法如下:

#define ADD(a,b) (a+b)

它的功能是计算a+b的结果,示例程序如下:

#include <stdio.h>
#define ADD(a,b) (a+b)
 
int main() {
    int a = 1;
    int b = 2;
    printf("a+b=%d", ADD(a, b));
    return 0;
}

这里需要注意的是,我们习惯上会把后面表达式添上括号,如(a+b),在本例中去掉这个括号也是可以的,但有些情况下会出现问题,如下:

#include <stdio.h>
#define ADD(a,b) a+b
#define SUB(a,b) a-b
 
int main() {
    int a = 1;
    int b = 2;
    printf("%d", ADD(a,b)*SUB(a,b)); // ab的和与差相乘
    return 0;
}

这个程序的功能是计算a+b乘以a-b的值,实际上程序的输出结果为1,我们稍加思考发现本例中a+b>0a-b<0答案必然是负数,结果却是正数,显然出了问题,那么再把表达式的括号加上试试看:

#define ADD(a,b) (a+b)
#define SUB(a,b) (a-b)

程序运行后输出结果为-3,可以口算发现此结果是正确的,出现问题的原因就要从#define预编译指令的原理说起,在编译一个C语言程序时,第一步执行的是预编译这个过程中会把#define中定义的宏进行替换,如不加括号的程序中就替换成了如下:

printf("%d", a+b*a-b); // 输出结果为1

显然根据数学运算规则会先算乘法后算加法,所以结果为1,那么加上括号的程序中替换后结果如下:

printf("%d", (1+2)*(1-2)); // 输出结果为-3

先算括号里的式子,整个式子的结果显然为-3,通过本例可以发现为了避免替换后出现类似这种错误,尽量将表达式加上括号,特别是表达式中有多个变量时

带参宏定义和函数有点像,他们的区别如下

  • 函数参数传递时会计算实参的值再传递给形参,如执行函数func(1+2),会先计算1+2的值为3再交给函数体,而带参宏定义不进行任何计算,直接替换
  • 根据上面例子可以发现,带参宏定义对数据类型没有定义,可以使用多种合法类型带入,但函数要根据定义函数时声明的参数类型传递值
  • 带参宏定义仅占用预编译的时间,而函数的执行会占用空间和时间,如分配内存空间,入栈保留现场,返回值等

3 与字符串有关的用法

3.1 字符串转换

使用方法如下,在s前加上#,它的作用是将传入的s转换为字符串

#define STR(s) #s

示例程序如下,给出参数Wuhan jia you,最后以字符串的形式输出来

#include <stdio.h>
#define STR(s) #s
 
int main() {
    puts(STR(Wuhan jia you));
    return 0;
}

需要注意的是,转换的是s,而不是s里的值,见下面两个程序:

// 程序1
#include <stdio.h>
#define STR(s) #s
 
int main() {
    const char *s = "Wuhan jia you";
    puts(STR(s));
    return 0;
}
// 程序2
#include <stdio.h>
#define STR(s) #s
 
int main() {
    int n = 2020;
    puts(STR(n));
    return 0;
}

第一个程序输出结果为s,第二个程序的输出结果为n,可见这个用法就是你给它什么,它就把它直接转换为字符串,不管它是不是变量

3.2 字符转换

使用方法如下,在c的前面加上#@,它的作用是把c转换为字符

#define STR(c) #@c

注意:本用法不支持gcc编译器,如在gcc编译器下编译,会出现下面的错误信息error: '#' is not followed by a macro parameter,这个用法仅支持微软系列(Microsoft Specific)

示例程序如下,给出参数1,最后把1转换为字符1输出:

#include <stdio.h>
#define STR(c) #@c
 
int main() {
    putchar(STR(1));
    return 0;
}

关于它的转换规则和3.1 字符串转换的类似

3.3 字符连接

使用方法如下,在两个参数中间添加##,它的作用是连接前后两个参数,把它们变成一个参数

#define CAT(a,b) a##b

示例程序如下

#include <stdio.h>
#define CAT(a,b) a##b
 
int main() {
    int n = CAT(123, 456);
    printf("%d", n);
    return 0;
}

输出结果为123456