关注

C语言:预处理详解

目录

一.预定义符号

二.#define

三.#与##

四.#undef

五.命名行定义

六.条件编译

七.头文件


一.预定义符号

__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技术社区

原文链接:https://blog.csdn.net/yyyzc_/article/details/158569901

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--