一.预定义符号
__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;
}
for(i = 0; i< ARRAY_SIZE; i ++)
{
printf("%d " ,array);
}
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博主「fffzd」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yyyzc_/article/details/158569901
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?注册
×
|