[经验分享] C语言:预处理详解

[复制链接]
2|0
荣陶陶 发表于 2026-7-4 14:22 | 显示全部楼层 |阅读模式
一.预定义符号
__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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
您需要登录后才可以回帖 登录 | 注册

本版积分规则

151

主题

531

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部
0