预处理命令
基础知识
预处理命令简介
C语言的预处理命令是指编译之前由预处理器执行的指令,用于在源代码中进行一些预处理操作。
常见预处理命令
(1) #define
定义一个宏,用于替换源代码中的标识符为指定的文本。
#define MAX_NUM 100
int arr[MAX_NUM];// 替换后就是:int arr[100];
(2) #include
包含一个头文件,头文件中的内容插入到源代码中。
#include <stdio.h> // 使用尖括号,从系统目录中查找头文件
#include "myHeader.h" // 使用双引号,从当前目录查找头文件
(3)#ifdef
与#ifndef
用于条件编译,根据指定的宏是否已经定义来决定是否编译某段代码。
#ifndef DEBUG
printf("Debug mode is off\n");
#endif
(4)#if / #elif / #else / #endif
用于条件编译,根据指定的表达式来决定是否编译某段代码。
#define NUM 100
#if NUM > 50
printf("NUM is greater than 50\n");
#elif NUM < 50
printf("NUM is less than 50\n");
#else
printf("NUM is equal to 50\n");
#endif
(5)undef
主要用于取消宏定义,使得后面可以再次定义相同名宏定义
#define PI 3.1415
#undef PI//取消宏
(6)#pragma
用于向编译器发出特殊指令。
#pragma warning(disable: 4996)//(禁用指定警告)
宏定义
宏的三种用法
(1)定义符号常量
#define PI 3.1415926
#define MAX_N 100000
(2)定义傻瓜表达式
#define MAX(a, b) ((a) > (b) ? (a) : (b))//求较大值
#define S(a, b) ((a) * (b))//求乘积
(3)定义代码段
宏只允许实现在一行,需要多行的话可以用
\
拼接。
#define P(a) {\printf("%d\n", a);\
}
预定义宏
_ _ DATE _ _:日期:Mmm dd yyyy
_ _TIME _ _ : 时间:hh:mm:ss
_ _LINE _ _:行号:
_ _FILE _ _:文件名
_ _func _ _ : 函数名(非标准)
_ _ FUNC _ _ :函数名(非标准)
_ _ PRETTY_FUNCTION _ _更详细的函数信息
变参宏
(1)相关介绍
- 一种宏定义方式,允许宏接受可变数量的参数。它的作用类似于函数中的可变参数函数(例如printf函数),可以在宏中处理不定数量的参数。
(2)两种实现方式
- 变参宏的实现原理主要涉及两个预处理器运算符:
__VA_ARGS__
和##
。__VA_ARGS__
是一个特殊标识符,表示宏的可变参数部分(即省略号 …)。它在宏展开时会被替换为实际的参数列表。##
运算符将前面的标记与后面的标记连接在一起,可以用于在可变参数宏中正确地处理参数之间的逗号。
// ##
方式
#define LOG(fmt, arg...) {\printf("[FILE: %s FUNC: %s LINE: %d]\t", __FILE__, __func__, __LINE__ );\printf(fmt, ##arg);\printf("\n");\
}int main(){int num = 10, a = 123;LOG("num = %d, a = %d\n", num, a);LOG("Hello word\n");
}
//特殊宏 _ _ VA_ARGS _ _
/*实现变参宏*/
#include<stdio.h>
#define LOG(...) {\printf("[FILE: %s FUNC: %s LINE: %d]\t", __FILE__, __func__, __LINE__);\printf(__VA_ARGS__);\
}int main(){int num = 10, a = 123;LOG("num = %d, a = %d\n", num, a);LOG("Hello word\n");
}
条件式编译
(1)简单介绍
#ifdef DEBUG
:是否定义了DEBUG
这个宏;ifndef DEBUG
:是否没定义DEBUG宏;if MAX_N == 5
:宏MAX_N
是否等于5#else
:其他情况#endif
: 结束当前条件判断
(2)实现一个LOG宏
#include<stdio.h>
#ifdef DEBUG
#define LOG(fmt, arg...) {\printf("[FILE: %s FUNC: %s LINE: %d] ", __FILE__, __func__, __LINE__ );\printf(fmt, ##arg);\printf("\n");\
}
#else
#define LOG(fmt, arg...)
#endifint main(){int a = 12;LOG("%d",a);return 0;
}
扩展知识
(1)__typedef()
关键字
__typeof()
关键字是C语言的一个扩展,它的作用是获取表达式或变量的类型信息而不执行实际的操作。__typeof()
的具体实现是通过编译器在编译阶段进行静态分析来确定表达式或变量的类型,并将其替换为相应的类型。
//实现swap操作
#define swap(a, b) {\__typedef((a)) __c = (a);\(a) = (b);\(b) = __c;\
}
(2)__attribute__
属性
__attribute__((constructor))
是GCC和一些兼容的编译器提供的一个特殊属性,它用于指定在程序启动时自动执行的函数。这个特性可以用来实现在程序运行前执行一些初始化操作的需求。- 当函数被标记为__attribute__((constructor))时,编译器会将该函数添加到一个特殊的构造函数表中。在程序启动时,链接器会在运行时初始化阶段执行这个构造函数表中的函数。具体来说,这些带有constructor属性的函数将在main()函数之前被自动调用
#include<stdio.h>
char *usr;
char *phone;
__attribute__((constructor))
void init() {usr = "mofei";phone = "132312123";return ;
}
int main() {printf("usr : %s \nphone : %s\n", usr, phone);return 0;
}
(3)#
的作用
- #是一种预处理器运算符,称为字符串化运算符。
- 它的作用是将宏参数转换为字符串常量。
- 当使用#运算符在宏定义中对参数进行字符串化时,它会在编译时将参数表示为一个字符串。
#include<stdio.h>
#define str(a) {\printf("%s : %d\n", #a, a);\
}
int main() {int A = 123;str(A);return 0;
}