
目录
一.预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前行号
__DATE__ //文件被编译日期
__TIME__ //文件被编译的具体时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
举例:
#include<stdio.h>
int main()
{
printf("%s\n",__FILE__);
printf("%d\n",__LINE__);
printf("%s\n",__DATE__);
printf("%s\n",__TIME__);
// printf("%d\n",__STDC__);
return 0;
}

二.#define
1.定义常量
注意不加分号!!!
eg1. 常量MAX为999
#define MAX 999
eg2.当定义的stuff过长,可以使用反斜杠\(续行符)写在每一行的最后位置(最后一行除外),从而实现分行。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \ date:%s\ttime:%s\n" ,\ __FILE__,__LINE__ , \ __DATE__,__TIME__ )
2.定义宏
(1)形式:
#define name(parament-list) stuff
//parament-list:由参数组成(类似于自定义函数中的参数),可能出现在stuff中
//stuff:替换内容
//注意:左括号要与name紧邻
eg1.
#define ADD(x) x+x定义ADD,参数为x,当在主函数中写ADD(1),则会被替换为1+1
在定义宏时存在运算,要多使用小括号:
eg2.
#include<stdio.h> #define SQUARE(x) x*x int main() { int m = SQUARE(1 + 2); printf("%d", m); return 0; }
输出结果为5,却不是我们想的3*3=9,为什么呢?
因为SAUARE(1+2)被替换为1+2*1+2=5(乘法优先级高于加法)
所以我们需使用小括号使得算式要先进行加法运算:
#include<stdio.h> #define SQUARE(x) (x)*(x) int main() { int m = SQUARE(1 + 2); printf("%d", m); return 0; }
eg3.
#include<stdio.h> #define ADD(x) (x)+(x) int main() { int m = 3*ADD(1); printf("%d", m); return 0; }
输出结果为4,同样也不是我们预期的结果3*2=6
我们可以试试将替换后的式子写出来:
3*1+1,此时乘法优先级高于加法,所以先进行乘法运算
此时我们需要小括号来改变运算顺序:
#include<stdio.h> #define ADD(x) ((x)+(x)) int main() { int m = 3 * ADD(1); printf("%d", m); return 0; }
因此在写宏定义时,不要吝啬你的括号(内括号和外括号)。
(2)带有副作用的宏参数
如果宏参数在定义中使用超过一次,且参数带有副作用,会导致不可预测的结果。
x+1//不带有副作用参数
x++//带有副作用参数(即每使用一次自身值x改变)
比如:
#include<stdio.h> #define MAX(a,b) ((a)>(b)?(a):(b)) int main() { int x = 5; int y = 8; int z = MAX(x++, y++); printf("x=%d,y=%d,z=%d\n", x, y, z); return 0; }我们正常思考,z = ( (x++) > (y++) ? (x++) : (y++)),x = 6 , y = 9 , z = 8
但结果为:
为什么呢?因为我们没考虑自加带来的副作用:
首先在 (x++) > (y++)比较过程中,x变为6,y变为9,然后该不等式不成立,则执行y++,此时y变成10,而z = y++为后置加加,因此z = 9。
(3)宏替换规则
注意:
~ 宏不能出现递归
~字符串常量中的内容如果有宏定义的常量,此时该常量不能被替换。
(4)宏与函数对比
优势:
~ 当代码少量时,宏比函数在运行效率上更高。
~ 宏参数的类型不确定(与python相似):
#include<stdio.h> #define ADD(x,y) x+y int main() { int a = ADD(1, 2);//整型 float b = ADD(1.1, 2.2);//浮点型 printf("a = %d , b = %f", a, b); return 0; }
~宏参数不仅仅局限于系统给定的类型:比如整型,浮点型,字符型,布尔等等:
#define MALLOC(num, type)\ (type )malloc(num sizeof(type)) ... //使⽤ MALLOC(10, int);//类型作为参数 //预处理器替换之后: (int *)malloc(10 sizeof(int));
劣势:
~ 无法调试
~ 宏参数与类型无关,因此不够严谨
总结:
三.#与##
1.#
是字符串操作符,将原始文本(非计算后的值)转换为字符串(也可称“字符串化”),仅在宏定义中生效。
eg1.
#include<stdio.h> #define PRINT(n) printf("the value of "#n" is %d",n); int main() { int a = 1; PRINT(a); return 0; }

此时的#n字符串化,变为a。
2.##
可以将两边符号合成一个符号,因此##被称为记号粘合。
eg2.
#include<stdio.h> #define GENERIC_MAX(type) \ type type##_max(type x, type y)\ { \ return (x>y?x:y); \ } GENERIC_MAX(int) //替换到宏体内后int##_max ⽣成了新的符号 int_max做函数名 GENERIC_MAX(float) //替换到宏体内后float##_max ⽣成了新的符号 float_max做函数名 int main() { //调⽤函数 int m = int_max(2, 3); printf("%d\n", m); float fm = float_max(3.5f, 4.5f); printf("%f\n", fm); return 0; }
四.#undef
用来移除宏定义
#include<stdio.h>
#define MAX 10
int main()
{
printf("%d", MAX);//可打印
#undef MAX;
printf("%d", MAX);//报错
return 0;
}

五.命名行定义
许多C编译器可以在命令行中定义符号
比如:
#include <stdio.h>
int main()
{
int array [ARRAY_SIZE];
int i = 0;
for(i = 0; i< ARRAY_SIZE; i ++)
{
array[i] = i;
}
for(i = 0; i< ARRAY_SIZE; i ++)
{
printf("%d " ,array[i]);
}
printf("\n" );
return 0;
}
代码中数组大小ARRAY_SIZE未定义,此时我们可以在linux环境中进行定义具体值: gcc -D ARRAY_SIZE=10 programe.c
六.条件编译
1. #if ...#endif
#include<stdio.h> int main() { #if 0 //条件一直为假 printf("hello world\n");//不执行该语句 #endif return 0; }因此#if 0...#endif在这里效果等于注释效果
2. #if ... #elif ... #else ... #endif 多条件分支语句
#include<stdio.h> #define M 3 int main() { #if M == 0 printf("0"); #elif M == 1 printf("1"); #elif M == 2 printf("2"); #else printf("3"); #endif return 0; }
3.是否定义
(1)被定义:
#if defined( ... ) 或 #ifdef ...
#endif#include<stdio.h> #define MAX 999 int main() { #if defined(MAX) printf("hello\n"); #endif return 0; }#include<stdio.h> int main() { #ifdef MAX printf("hello"); #endif return 0; }(2)不被定义:
#if !defined( ... ) 或 #ifndef ...
#endif
#include<stdio.h> int main() { #if !defined(MAX) printf("hello\n"); #endif return 0; }#include<stdio.h> int main() { #ifndef MAX printf("hello\n"); #endif return 0; }
4.嵌套指令
#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
七.头文件
(1)双引号:#include"test.c"
先在源文件所在目录下查找该文件,若不在则会像查找库函数的标准头文件那样在标准位置查找头文件。
(2)#include<stdio.h>
直接在标准路径下查找
因此关于系统定义好的头文件我们使用< >符号,自己定义的使用双引号,这样查找效率会比都用双引号效率高一些。
文章到这里就结束了,创造不易,如果喜欢的话点个关注,点个赞,谢谢大家!
转载自CSDN-专业IT技术社区






为什么呢?因为我们没考虑自加带来的副作用:


