一、什么是宏定义?
宏定义是C语言预处理器提供的强大功能,它允许开发者在编译前对代码进行文本替换和条件编译。相当于代码的"自动修正带",在编译前修改源代码。
二、宏定义核心指令大全
指令
作用描述
#define
定义宏(常量宏或函数宏)
#undef
取消已定义的宏
#ifdef
检查宏是否已定义
#ifndef
检查宏是否未定义(常用于头文件保护)
#if
根据条件决定是否编译代码
#else
条件编译的else分支
#elif
条件编译的else if分支
#endif
结束条件编译块
#include
文件包含(<>用于系统头文件,""用于自定义头文件)
#error
强制产生编译错误
#pragma
编译器特定指令(如#pragma once)
#
字符串化操作符(将宏参数转换为字符串)
##
连接操作符(拼接两个标识符)
三、基础宏定义详解
1. 常量宏
#define PI 3.1415926
#define MAX_SIZE 100
特点:
简单的文本替换
通常全大写命名
替换发生在编译前
2. 函数式宏
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x)*(x))
注意事项:
每个参数和整个表达式都要加括号
避免使用自增/自减运算符,可能产生副作用(如SQUARE(i++))
3. #undef
#undef 宏名
典型示例
#include
#define TEMP 100
#undef TEMP // 取消定义后TEMP不可再用
int main()
{
// printf("%d", TEMP); // 取消注释会导致编译错误
return 0;
}
4. #ifdef条件编译
#ifdef 宏名
// 宏已定义时编译的代码
#endif
典型示例
#include
#define DEBUG_MODE
#ifdef DEBUG_MODE
printf("调试信息: 程序进入调试模式\n");
#endif
int main()
{
return 0;
}
5. #ifndef
#ifndef通常用于头文件中,防止重复包含。因为当多个源文件包含同一个头文件时,可能会导致:重复定义错误,如头文件中有变量或函数定义,多次包含会引发编译错误。
#ifndef 宏名
// 宏未定义时编译的代码
#endif
典型示例
#ifndef __HEADER_NAME_H__ // 如果未定义这个宏
#define __HEADER_NAME_H__ // 立即定义它
// 头文件的实际内容...
#endif // 结束条件编译
6. #if条件判断
#include
#define VERSION 2
#if VERSION >= 2
printf("高级功能已启用\n");
#endif
int main()
{
return 0;
}
版本控制示例
#include
#define VERSION 2
#if VERSION >= 2
printf("高级功能已启用\n");
#endif
int main()
{
return 0;
}
7. #else分支
#if 条件
// 条件成立代码
#else
// 条件不成立代码
#endif
典型示例
#include
// #define DEBUG
#ifdef DEBUG
printf("调试模式\n");
#else
printf("生产模式\n");
#endif
int main()
{
return 0;
}
8. #elif多条件
#if 条件1
// 代码块1
#elif 条件2
// 代码块2
#else
// 默认代码块
#endif
典型示例
#include
#define VERSION 2
#if VERSION == 1
printf("基础版\n");
#elif VERSION == 2
printf("专业版\n");
#else
printf("未知版本\n");
#endif
int main()
{
return 0;
}
9. #include文件包含
#include <标准头文件.h> // 系统目录查找
#include "自定义头文件.h" // 当前目录优先查找
典型示例
#include
#include "config.h" // 包含自定义配置文件
10. #error错误指令
#error 错误信息
强制检查示例
#ifndef REQUIRED_MACRO
#error "必须定义REQUIRED_MACRO宏"
#endif
11. #pragma编译器指令
常用pragma指令
指令
功能描述
#pragma once
防止头文件重复包含
#pragma pack(n)
设置结构体对齐方式
#pragma warning
控制编译器警告
内存对齐示例
#pragma pack(push, 1) // 1字节对齐
struct Packet {
char header;
int data;
};
#pragma pack(pop) // 恢复默认对齐
13. 字符串化(#)
将宏参数直接转换为字符串常量
#define STRINGIFY(x) #x
字符串化示例
#include
#define STRINGIFY(x) #x
#define PRINT_VAR(x) printf(#x " = %d\n", x)
int main()
{
int count = 42;
PRINT_VAR(count); // 输出:count = 42
// 直接转换数字
printf("%s\n", STRINGIFY(3.14)); // 输出:"3.14"
return 0;
}
避坑指南
1.仅对宏参数有效
2.会忽略参数中的空格:
//无论hello world中间有多少个空格,输出只会保留一个空格
STRINGIFY(hello world) // 输出"hello world"(保留一个空格)
3.无法直接字符串化宏本身:
#define TEST 123
STRINGIFY(TEST) // 输出"TEST",不是"123"
14. 字符串连接(##)
将两个标识符拼接成一个新的标识符
#define CONCAT(a,b) a##b
标识符连接示例
#include
#define VAR_NAME(n) var_##n
int main()
{
int var_1 = 10, var_2 = 20;
// 动态访问变量
printf("%d\n", VAR_NAME(1)); // 输出:10
printf("%d\n", VAR_NAME(2)); // 输出:20
return 0;
}
特殊用法:
// 连接多个符号
#define MAKE_FUNC(type,name) type name##_##type()
MAKE_FUNC(int,foo) { return 42; } // 生成:int foo_int()
// 与字符串化结合
#define DEBUG_LOG(var) printf(#var "=%d", var##_value)
int age_value = 25;
DEBUG_LOG(age); // 输出:age=25
关注微信公众号「芯动视界」,解锁代码世界的核心奥秘!获取更多资料!
🔧 深耕C语言精髓:从指针迷宫到内存管理,系统化讲解,助你夯实底层基础。 ⚡️ 探索嵌入式实战:剖析项目案例,分享避坑指南,让开发效率翻倍。
无论你是初无论你是初探嵌入式的新手,还是寻求进阶的老兵,这里都有你需要的硬核干货与技术洞见。一起用代码“芯”动视界!