? ?C语言设置了?些预定义符号,可以直接使用,预定义符号也是在预处理期间处理的。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
//源文件的位置,文件名
printf("%s\n", __FILE__);
//这条代码的行号
printf("%d\n", __LINE__);
//日期
printf("%s\n", __DATE__);
//时间
printf("%s\n", __TIME__);
//检测是否支持ansic标准
printf("%d\n", __STDC__);
//电脑显示未定义,就是不是支持,支持的话会打印出1,
//vs2022不支持
}
? ? ? ? ? ? ? ?#define 被替换的变量替换内容
如:
#define a 10+2
#define str hello world
//可以是常量,字符串,甚至是一串代码
//替换关键字
#define reg register
//自动补加break
#define CASE break;case
//替换一段代码
#define DEBUG_PRINT printf("file:%s\t" line:%d\t \date:%s\t time:%s\n",\__FILE__,__LINE__,__DATE__,__TIME__)
特别注明:#define 被替换 替换后面最好不加分号;(避免出现格式问题)。
#define机制包括了?个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏 (definemacro)。它允许在代码中创建符号常量或简单的函数替换。
#define name(参数)? 表达式
#define MACRO_NAME value
其中,MACRO_NAME
?是宏的名称,value
?是宏的值或替换文本。
#define SQUARE(x) ((x)*(x))
#define square(a) (a)*(a)
#include<stdio.h>
int main()
{
int a = 5;
printf("%d\n", square(a + 1));
}
?结果是36.
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
例如:
#define a 1
b=a+1;//a=1,无改变a值
b=++a;//a=2,永久性改变a值
简单的文本替换:当预处理器遇到宏的名称时,会将其替换为宏的值。这是最基本的宏替换规则。
参数替换:如果宏定义包含参数,预处理器会将调用宏时提供的实际参数替换到宏定义中的参数位置。
宏展开:预处理器会递归地展开宏。这意味着,如果宏的值本身包含其他宏,预处理器会继续展开这些嵌套的宏,直到没有更多的宏需要展开为止。
#include <stdio.h>
#define MULTIPLY(a, b) ((a) * (b))
#define SQUARE(x) MULTIPLY(x, x)
int main()
{
int result = SQUARE(3 + 2);
printf("Result: %d\n", result);
return 0;
}
? ? ? 在这个例子中,SQUARE
宏调用了MULTIPLY
宏。当预处理器展开SQUARE(3 + 2)
时,它会进行如? ? ? ? ? 下的宏展开过程:
SQUARE(3 + 2)
?展开为?MULTIPLY(3 + 2, 3 + 2)
MULTIPLY(3 + 2, 3 + 2)
?展开为?((3 + 2) * (3 + 2))
((3 + 2) * (3 + 2))
,得到最终的表达式?((3 + 2) * (3 + 2))
宏的作用域:宏的作用域从宏定义的位置开始,一直到文件末尾或宏的#undef
指令为止。在作用域内,预处理器会对宏进行替换。
可以传类型,如
#define MALLOC(num, type)\
(type *)malloc(num sizeof(type))
...
//使?
MALLOC(10, int);//类型作为参数
//预处理器替换之后:
(int *)malloc(10 sizeof(int));
#运算符将宏的?个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。?#运算符所执行的操作可以理解为“字符串化”。
如
#include<stdio.h>
#define PRINT(val,format) printf("the value of " #val " is "format"\n",val)
//相当于printf("the value of""a""is""%d""\n",a)
int main()
{
int a = 10;
PRINT(a, "%d");
return 0;
}
## 可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。 ## 被称 为记号粘合这样的连接必须产生?个合法的标识符。否则其结果就是未定义的。
宏定义一个加法函数。
#include<stdio.h>
#define GENERIC_MAX(type) type type##_max(type x, type y)\
{\
return (x>y?x:y);\
}
//##相当于把int和_max连起来,语法,不连起来type_max是一个整体,替换不了
//int int_max(int x,int y){return (x>y?x:y)}
GENERIC_MAX(int)
int main()
{
int m = int_max(3, 5);
printf("%d\n", m);
return 0;
}
用于移除一个宏定义
此时,我们可以发现第二次打印显示MAX未声明,说明MAX的宏定义已经被移除。
? ? ? 在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
#include<stdio.h>
#define __a__
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#ifdef __a__
printf("%d\n", arr[i]);
#endif __a__
}
}
当我们注释掉#define __a__时,#ifdef __a__和#endif __a__之间的语句不会被执行。a可以是其他表达式。
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
? ? ? ? ? #include"add.c"
? ? ? ? ? #include<stdio.h>
假如有一个本地文件被重复包含、引入,编译压力就会很大,这时,我们就可以引用条件编译解决。
或者,在引入前,加上
#pragma once