10.25上午与10.28早晚 宏的初识

发布于 29 天前  212 次阅读


//接上文

#define 在 C 语言中确实常用于定义常量,但它也可以用于定义宏(即带参数的代码片段)

1. 常量定义

常量是一个固定的值,通常使用 #define 定义。例如:

#define PI 3.14(中间不加赋值运算符,空格隔开即可)

在这个例子中,PI 是一个常量,预处理器会在编译时将所有出现的 PI 替换为 3.14

2. 代码片段定义

宏是一个可以接受参数的代码片段,宏定义(Macro Definition)是C语言预处理器的一部分,通常用于简化代码或实现一些简单的功能。例如:

#define SQUARE(x) ((x) * (x))

在这个例子中,SQUARE 是一个宏,它接受一个参数 x,并在使用时被替换为 ((x) * (x))(两个括号,第一个里面是形参列表,第二个是内容)。这使得你可以在代码中方便地计算一个数的平方

  • 带参数的宏可以接受输入并在预处理阶段进行文本替换

以下是 C 语言宏定义的语法及用法的表格汇总:


基本宏 #define 宏名 宏值 定义一个简单的宏,用于替换文本。例如:#define PI 3.14


带参数的宏 #define 宏名(参数列表) 宏体 定义一个带参数的宏,用于替换文本。例如:#define SQUARE(x) ((x) * (x))
条件编译宏 #ifdef 宏名#ifndef 宏名、#endif,用于检查宏是否被定义。例如:#ifdef DEBUG

宏取消定义 #undef 宏名 取消宏定义,使宏名不再有效。例如:#undef PI
宏扩展 #define 宏名(x) (x) 在宏体中使用宏参数。例如:#define DOUBLE(x) ((x) * 2)
多行宏 #define 宏名 (参数列表) \
宏体行1
宏体行2 定义多行宏,需要在行末加反斜杠\进行续行!!!。例如:#define MAX(a, b) \
(((a) > (b)) ? (a) : (b))
宏函数 #define 宏名(参数1, 参数2) 宏体 定义带多个参数的宏。例如:#define ADD(x, y) ((x) + (y)


宏定义可以用来定义代码片段,从而减少重复的代码,提高维护性

#define SQUARE(x) ((x) * (x))

int main() {
    int num = 4;
    printf("Square: %d\n", SQUARE(num));
    return 0;
}

带参数的宏允许在宏定义中使用参数,类似于函数调用,但不同的是宏在预处理阶段进行文本替换

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

int main() {
    int x = 5, y = 10;
    printf("Max: %d\n", MAX(x, y));
    return 0;
}

宏不可与变量名冲突,否则编译器会报错

宏嵌套是指在一个宏定义中使用另一个宏定义。这种方式可以提高宏定义的灵活性和可重用性

#define DOUBLE(x) ((x) + (x))
#define QUADRUPLE(x) (DOUBLE(DOUBLE(x)))

int main() {
    int num = 5;
    printf("Quadruple: %d\n", QUADRUPLE(num));
    return 0;
}
//输出Quadruple: 20

特别注意,宏替换是简单的文本替换(被调用时这在预处理时整体运行,且遵循顺序结构),自身没有运算优先级的检查,因此编写宏时必须特别注意括号的使用!!!

#define ADD(x, y) x + y
int result = ADD(3, 2 * 5);

预处理器将 ADD(3, 2 * 5) 替换为 3 + 2 * 5这是宏的操作原理,ADD(3,2*5)是一个整体被替换,但效果与函数类似,所以也称为宏函数),宏自己只做文本替换,替换后编译器再根据运算优先级规则,结果是 3 + 10,而不是预期的 (3 + 2) * 5。这是因为 + 运算符的优先级低于 * 运算符(这也容易被我们接受)

#define ADD(x, y) ((x) + (y))

这和上面的结果都是13,但是区别是这个是在预处理文本替换阶段加上了括号,即:预处理时使用了括号调整运算优先级,而不是编译器自己来做这件事(替换时大的括号里面加的括号也一起替换过去了)

同时,宏的形参列表不需要像函数一样加上定义类型,因为实际它就等同于函数体中文本的替换,同时,宏本身并没有返回值的概念,这不同于函数


出错的宏:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define ex(a b) (b a)//错误1

int main() {
	int a, b;
	scanf("%d %d", &a, &b);
	int result = ex(a b);//错误2
	printf("%d", result);
	return 0;
}

首先,正确的宏定义应该使用逗号分隔参数而不是空格(目的是进行空格分开,两参数分开,但是宏定义有自己的规范要求)

错误1的第二个括号也是错误的。调用宏ex,传入参数ab。根据宏的定义,这将被替换为(b a),但是这在C语言中是无效的表达式,因此会导致编译错误

错误2,极其重要!这个宏定义了一个交换操作,但它并不返回任何值,因此 int result = ex(a, b); 的用法是错误的

宏没有返回值,因此 result 将未定义。你不能将宏的结果赋值给一个变量

 ex(a, b);  // 直接调用宏

这是修改代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define ex(a,b) int temp=a;\
a=b;\
b=temp;

int main() {
	int a, b;
	scanf("%d %d", &a, &b);
	ex(a, b);
	printf("%d %d",a,b);
	return 0;
}

1.多行宏的最后一行可以不加反斜杠

2.多行宏的宏体也要像函数体那样加;(且多加反斜杠)

3.编译器按顺序结构执行ex(a,b)时,ex(a,b)文本替换成多行宏体内容,可类比函数

4.顺序结构很重要,若ex(a,b)放在printf后面,则输出结果不会变,因为是在打印结果的后面执行的宏


加括号是为了考虑运算优先级,加分号是为了函数体使用宏的文本替换,同时请确保每行末尾以反斜杠作为最后字符

//宏定义中的换行符 \ 用于连接多行代码,确保它们在预处理时被视为一行

届ける言葉を今は育ててる
最后更新于 2024-10-28