【C语言】一篇带你玩转 预处理指令(上)

简介: 【C语言】一篇带你玩转 预处理指令(上)

很多人好奇预处理是什么,C程序中插入传给编译程序的各种指令(宏),这些指令被称为预处理器指令,它们扩充了程序设计的环境,也称预处理符号。

这一节我们就讲解预处理,


预定义符号



  • FILE 进行编译的源文件
  • LINE 文件当前的行号
  • DATE 文件被编译的日期
  • TIME 文件被编译的时间
  • STDC 如果编译器遵循ANSI C,其值为1,否则未定义
  • FUNCTION 获取函数名


这些预定义符号都是语言内置的。

那该如何使用呢?

  • 代码实例


#include<stdio.h>
#include<stdlib.h>
int main()
{
  int a[] = { 1,2,3,4,5, };
  int sz = sizeof(a) / sizeof(a[0]);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
   printf("%d , %s , %s , %s , lene:%d\n", a[i], __FILE__, __TIME__, __DATE__, __LINE__);
  }
  system("pause");
  return 0;
}


最终于输出结果:

1 , D:\C.Code\2023test.c\test_4_2\test_4_2\test.c , 00:26:17 , Apr 4 2023 , lene:90

2 , D:\C.Code\2023test.c\test_4_2\test_4_2\test.c , 00:26:17 , Apr 4 2023 , lene:90

3 , D:\C.Code\2023test.c\test_4_2\test_4_2\test.c , 00:26:17 , Apr 4 2023 , lene:90

4 , D:\C.Code\2023test.c\test_4_2\test_4_2\test.c , 00:26:17 , Apr 4 2023 , lene:90

5 , D:\C.Code\2023test.c\test_4_2\test_4_2\test.c , 00:26:17 , Apr 4 2023 , lene:90


知识点扩展:

补充: 其实编译器在代码编译的时候,会对函数和变量名重命名的//在C语言中重命名的规则基本就是: 加_


#define



#define 定义标识符


#define MAX 100
int main()
{
  printf("%d", MAX);
  int a[MAX] = { 0 };
  return 0;
}


我们用VS code gcc编译打开预处理看看

输入gcc test.c -E -o test.i 打开预处理指令

#define MAX 定义的标识符都给替换成100了


55fbeff5d69944d1bf5552ca06634eb7.png


当然用#define重命名也是可以的

  • 比如


#define reg register          //为 register这个关键字,创建一个简短的名字


此时打开预处理指令显示


5be8d75ab95847f38421715b6c1faf37.png


如果定义的 某个值过长,可以分成几行写,除了最后一行外,每行的后面都加一个 \ 反斜杠(续行符)

  • 比如


#define DEBUG_PRINT printf("file:%s\tline:%d\t \ date:%s\ttime:%s\n" ,\
                       __FILE__,__LINE__ ,  \
                      __DATE__,__TIME__ )  


如果一直按回车跳到下一行,语法会出现问题,所以要使用 \ 反斜杠(续行符)

注意:续行符后面不能添加其他东西


注意事项:

在define定义标识符的时候,不要加上 ; 分号


  • 那为什么呢?我们用gcc编译器打开预处理看看


535465bee1634f449a2fe753a2adb5ad.png


如果#define加了 ; 在其他语句也会默认替换成; 所以一定要牢记 #define不要加分号


#define 定义宏


#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义

宏(define macro)。


下面是宏的申明方式:


#define ADD(x) x + x


注意:

参数列表的左括号必须与ADD紧邻。

如果两者之间有任何空白存在,参数列表就会被解释为x+x的一部分。


在置于程序中,预处理器会将传进来的参数原封不动的替换宏定义的表达式、

-比如

f2073634371a41c1afbaabc95d667697.png


警告:

宏存在一定的问题,我们要尽量避开:

观察下面的代码段:


#define SQUABE(x) x * x
int main()
{
  printf("%d", SUL(1 + 7));
  return 0;
}


下面输出结果是15,为什么呢?就是因为我们刚刚所说的,传进来的参数原封不动的替换宏定义的表达式.替换文本时,参数x被替换成1 + 7,所以这条语句实际上变成了:

printf (“%d\n”,1+7 * 1+7 );

这样就比较清晰了,由替换产生的表达式并没有按照预想的次序进行求值。


44443cbf4ff847aca5197a1719b4fa7a.png


那该怎么处理?

在宏定义上加上两个括号,这个问题便轻松的解决了:>


#define SQUABE(x) (x) * (x)


这样预处理之后就产生了预期的效果:


printf ("%d\n",(1+7) * (1+7) );


warning:

这里还有一个宏定义:

比如

#define DOUBLE(x) (x) + (x)
int main()
{
  printf("%d", SUL(1 + 7));
  return 0;
}


  • 定义中我们使用了括号,想避免之前的问题,但是这个宏可能会出现新的错误。

看上去,好像打印100,但事实上打印的是55.

我们发现打开预处理替换之后:


printf ("%d\n",10 * (5) + (5));


乘法运算先于宏定义的加法,所以出现了

55 .


这个问题的解决办法是在宏定义表达式两边加上一对括号就可以了。


#define DOUBLE( x)   ( ( x ) + ( x ) )


提醒:

所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中

的操作符或邻近操作符之间不可预料的相互作用。


#define 替换规则


在程序中扩展#define定义符号和宏时,需要涉及几个步骤。


1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。

2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。

3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。


注意:

  1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递

归。

  1. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。


#和##


#.

我们直接看代码:

#include<stdio.h>
 int main()
 {
     int a = 10;
     printf("the value of a is %d\n", a);
     int b = 20;
     printf("the value of b is %d\n", b);
     float f = 3.14f;
     printf("the value of f is %f\n", f);
     return 0;
 }


这里写是不是太麻烦了,每次都要打印。那分装一个函数能不能解决,答案是不行的,因为不能每次都不能把the value of a,b,f这些字符串跟着改变,那该怎么办?


可以用宏来实现!

当然我们这里先要做下知识铺垫


int main()
 {
     printf("hello world\n");
     printf("hello " "world\n");
     return 0;
 }


最终输出结果:

hello world

hello world

这样写的格式也会自然合成一个字符串


  • 宏的方式


 #define print_format(num, format) \
             printf("the value of num is "format, num)
 int main()
 {
     int a = 10;
     print_format(a, "%d\n");
     //printf("the value of a is %d\n", a);
     int b = 20;
     print_format(b, "%d\n");
     //printf("the value of b is %d\n", b);
     float f = 3.14f;
     print_format(f, "%f\n");
     //printf("the value of f is %f\n", f);
     system("pause");
     return 0;
 }


最终输出结果:

the value of num is 10

the value of num is 20

the value of num is 3.140000


我们发现这样写还是不能实现该功能,num那里应该是a,b,f才对阿,那该怎么办?

这样我们就要用到我们的 #和C语言自带的天然字符串格式,我们只需要把 the value of和is变成字符串就可以了,任何num用一个#来修饰。


  • 下面来看代码吧

#num 会让这个参数不是替换进来,而是对应他的名字所对应的字符串。


 #define print_format(num, format) \
             printf("the value of "#num" is "format, num)
 int main()
 {
     int a = 10;
     print_format(a, "%d\n");
     //printf("the value of a is %d\n", a);
     int b = 20;
     print_format(b, "%d\n");
     //printf("the value of b is %d\n", b);
     float f = 3.14f;
     print_format(f, "%f\n");
     //printf("the value of f is %f\n", f);
     system("pause");
     return 0;
 }


最终输出结果:

the value of a is 10

the value of b is 20

the value of f is 3.140000


使用 # ,把一个宏参数变成对应的字符串。


##.

##的作用

##可以把位于它两边的符号合成一个符号。

它允许宏定义从分离的文本片段创建标识符。


  • 代码实例

##把Class和110合并成Class110


 int Class110 = 2023;
 #define CAT(x,y) x##y
 //Class110
 int main()
 {
     printf("%d\n", CAT(Class, 110));
     system("pause");
     return 0;
 }


输出结果:

2023


注:

这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。比如上面没有int Class110 = 2023; 产生的结果是未定义的


##的使用场景很少


目录
相关文章
|
1月前
|
存储 自然语言处理 程序员
【C语言】文件的编译链接和预处理
【C语言】文件的编译链接和预处理
|
25天前
|
程序员 编译器 C语言
C语言中的预处理指令及其实际应用
C语言中的预处理指令及其实际应用
52 0
|
3月前
|
C语言 编译器 开发者
【C语言基础】:预处理详解(二)
【C语言基础】:预处理详解(二)
|
3月前
|
编译器 C语言 C++
【C语言基础】:预处理详解(一)
【C语言基础】:预处理详解(一)
|
3月前
|
编译器 C语言
C语言中的#include指令
C语言中的#include指令
|
3月前
|
编译器 C语言
【C语言】:预处理详解
【C语言】:预处理详解
27 0
|
4月前
|
安全 编译器 程序员
C语言(16)----预处理中的宏以及预处理指令
C语言(16)----预处理中的宏以及预处理指令
40 2
|
3月前
|
存储 自然语言处理 编译器
C语言——环境与预处理
C语言——环境与预处理
19 0
|
3月前
|
编译器 C语言
C语言收尾 预处理相关知识
C语言收尾 预处理相关知识
27 0
|
4月前
|
编译器 C语言 开发者
C语言的预处理
C语言的预处理
30 0