2026/08/04 C语言的宏

一、C语言的宏定义

基本概念

C语言中可以利用宏定义实现文本的快速替换,注意:宏定义是单纯的文本替换,不检查语法是否合法。

C语言标准中提供了很多的预处理指令,比如#include、#pragma......以#开头的都属于预处理指令。

预处理指令指的是在gcc编译套件中的cpp预处理器对程序进行编译之前所做的一些动作,比如#include预处理指令就是在程序编译之前由预处理器把包含的头文件中的代码拷贝一份到源文件对应的位置,如果包含的文件中还有其他的预处理指令,会递归执行!

C语言标准中还提供了#define预处理指令,define是C语言关键字之一,中文具有定义的含义,所以利用#define预处理指令可以对某些表达式、某些常量、某些函数进行定义,其实就是给这些内容起一个可读性较高的名称。

定义格式
#define 宏名称(大写) 替换列表 换行(按下回车)
**宏名称就是一个用户命名的特定的标识符,一般实际开发中宏名称都采用大写(潜规则)。 **
宏名称后面就是用来替换宏名称的替换列表,这个替换列表可以是常量、表达式、if语句以及函数等。
例: #define PI 3.14

用户在源文件中某个位置使用了宏,不管使用了多少次,在程序编译之前,预处理器都会把宏用替换列表进行替换,当然要注意,宏替换就是单纯的文本替换,预处理器并不会做任何检查,比如替换之后是否符合语法,语法的检查是由编译器在编译阶段进行的。

#include <stdio.h>
#define N 2
#define M N+1
#define NUM (M+1)*M/2	//(M+1)*M/2=(N+1+1)*N+1/2=(2+1+1)*2+1/2=8   整数除法只保留整数,舍弃小数

int main(int argc, char const *argv[])
{
	int i=0;
	while(i<NUM){printf("%d",i++);}		//NUM=8,输出0-7
	printf("\n");

	return 0;
}

运行结果:
image

使用规则
宏定义的作用域是针对整个文件有效,所以应该定义在源文件的开头部分,这样才可以在其他的函数中使用宏定义,另外,宏不是语句,所以不需要在末尾添加分号,如果添加分号,则分号也会被一起替换。
C语言中宏定义的方案有三种,分别是无参数宏、带参数宏、无替换列表宏
(1)无参数的宏定义
注意:除了用户自定义的宏之外,系统中也存在一些已经定义好的宏,比如常用的NULL就是一个宏,当然,C99标准中还有一些常用的系统预定义的宏:
————DATE———— 输出日期
————FILE———— 输出当前源文件名称
————LINE———— 输出当前语句的行号
————TIME———— 输出时间

#include <stdio.h>

int main(int argc, char const *argv[])
{
	printf("file name is %s\nline number is %d\n",__FILE__,__LINE__);		//一般在调试程序时会用到
	printf("the time is %s\nthe date is %s\n",__TIME__,__DATE__);

	return 0;
}

当前设备时间:
image

运行结果:
image

(2)带参数的宏定义
C语言标准中支持定义带参数的宏,带参数的宏的使用在语法上类似于函数调用,宏的参数由括号()进行包含,括号中如果有多个参数则需要通过逗号来分隔,另外,带参数的宏在定义的时候宏名称和参数列表之间不能空格。
带参数的宏和函数的形式很像,但是却完全不同,带参数的宏会在程序所有出现的位置进行展开,缺点是浪费了内存空间,但是节约了函数切换的时间。

#include <stdio.h>
#define MAX(a,b) (((a)>(b))?(a):(b))
int main(int argc, char const *argv[])
{
	printf("MAX = %d\n",MAX(10,20));

	return 0;
}

运行结果:
image

例:利用宏定义交换两个变量的数值

#include <stdio.h>
#define SWAP(a,b) {a=a+b;b=a-b;a=a-b;}	//多条独立语句需要{}包含

int main(int argc, char const *argv[])
{
	int a=10;
	int b=20;
	printf("交换前 a = %d,b = %d\n",a,b);
	SWAP(a,b)
	printf("交换后 a = %d,b = %d\n",a,b);

	return 0;
}

运行结果:
image

(3)无替换列表的宏

C语言中也允许只定义一个宏,这个宏可以没有替换列表,一般实际开发中都是对程序进行条件编译的情况下来使用。
条件编译指的是可以选择性的编译程序中的某段代码,也就是预处理器可以根据具体的条件来保留或者删除某段源程序。
可以理解为是类似于C语言的判断语句,只不过是使用C语言中的预处理指令来判断宏的有效性,有效性指的是宏是否为真以及宏是否存在,C语言中提供了多种预处理指令来实现条件编译。

A.#if 用于判断常量表达式是否成立,遵循“非0即真”原则,#if预处理指令作为条件编译。一般#if和#endif是结合一起使用的,经常用于程序中的调试,可以选择保留或注释代码块!

#if 1 

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

#endif

#if 0

#define MIX (((a)<(b))?(a):(b))      //该语句不会被编译

#endif

B.#ifdef 用于判断宏是否被定义,如果宏是提前定义好的,则该预处理指令是有效的,也需要和#endif一起使用

#ifdef MACRO		//当宏MACRO被定义时,则该代码块是有效的,如果MACRO未定义,则该代码块无效

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

#endif

C.#if和#elif和#else和#endif 用于条件编译,可以通过常量表达式的多种状态来选择保留或者删除某些代码块

#define MACRO 5
#if  (MACRO == 1)				//无效
void func1()
{}
#elif (MACRO == 2)				//无效
void func2()
{}
#elif (MACRO ==3)				//无效
void func3()
{}
#else							//有效
void func4()
{}
#endif

D.#ifndef和#endif 用于判断宏是否未定义,如果宏定义,则该代码块会被删除,如果宏未被定义,则该代码块可以保留。头文件中的#ifndef #define #endif 是预处理指令,用于条件编译,可以防止重复定义,也可以防止头文件重复包含。

#define MACRO

#ifndef MACRO		//MACRO已经定义,该代码块不会参与编译

void func()
{}

#endif
#ifndef MACRO		//MACRO未被定义时,该代码块会执行

#define MACRO		//宏未定义时有效,定义相关宏,防止重复定义

#endif

E.C语言标准中提供的预处理指令#undef,可以提前终止某个宏的作用域。

#include <stdio.h>

#define PI 3.14	//定义宏

float func1(float r)
{
	return (PI*r*r);
}

#undef PI		//提前终止了PI的作用域

float func2(float r)
{
	return (PI*r*r);	//此时PI未定义
}

int main(int argc, char const *argv[])
{
	printf("%lf",func1(5));
	printf("%lf",func2(5));
	
	return 0;
}

image

posted @ 2026-08-04 16:59  !城北徐公.!  阅读(4)  评论(0)    收藏  举报