【C语言】----预处理宏

宏是学习任何语言所不可缺少的,优秀的宏定义可以使得代码变得很简洁且高效,有效地提高编程效率。

宏是一种预处理指令,它提供了一种机制,可以用来替换源代码中的字符串,解释器或编译器在遇到宏时会自动进行这一模式替换

C语言有简单的宏系统,由编译器或汇编器的预处理器实现。C的宏预处理器的工作只是简单的文本搜索和替换,

C语言的宏非常简单,我们只需要做好宏定义,其余交给编译器预处理即可

C语言的宏(预处理功能)有以下几类:

1. 宏定义
2. 包含头文件的宏
3. 条件编译宏
4. 预定义宏

宏定义

c程序提供的预处理功能之一。包括带参数的宏定义和不带参数的宏定义。具体是指用一个指定的标志符来进行简单的字符串替换或者进行阐述替换。形式为:

#define  标志符[(参数表)] 字符串

 
 
   
   
   
   
  • 1

其中“#”表示这是一条预处理命令(在 C 语言中凡是以“#”开头的均为预处理命令)“define”为宏定义命令,“标识符”为所定义的宏名, “字符串”可以是常数、表达式、格式串等。符号常量的定义就是一种无参宏定义

常见使用

//宏定义
#define SIZE 1000

//取消宏

#undef SIZE

//普通宏

#define PI 3.1415926

//带参数的宏

#define max(a,b) ((a)>(b)? (a),(b))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

其实宏定义就是一个简单的字符串替换,将前面的字符串替换成后面的字符串。

当然要注意,#define宏定义的作用仅仅是替换,它只是一个简单的替换,
比如下方代码:

#define   m(a,b)  a*3+b*4

c = m(5+6,2+4)
//C的结果等于41

  • 1
  • 2
  • 3
  • 4

这里的宏定义仅仅是将a换为5+6 而不是11 将b换位2+4 而不是6
因此顺序应该是 5+63+2+44 = 41

如果想要实际的预期结果 则需要

#define   m(a,b)  a*3+b*4

c = m((5+6),(2+4)) //应该使用括号将其包围
//C的结果等于57

  • 1
  • 2
  • 3
  • 4

再比如

#define point (int*);

point a,b;

  • 1
  • 2
  • 3

本意是a和b都是int型指针,但是实际上变成int* a,b;

a是int型指针,而b是int型变量。

这点要尤为注意

宏连接:

C语言中,使用两个#即可将两个字符连接成为一个

#include 
#define A1 printf("print A1\r\n")
#define A2 printf("print A2\r\n")
#define A(NAME) A##NAME
int main()
{
    A(1);
    return 0;
}

 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

会打印:

print A1

 
 
   
   
   
   
  • 1

在该例子中,调用宏A(1)时,NAME为1。A##NAME这个符号连接,即将A和1连接成了一个符号A1,然后执行宏A1的内容。最终打印出来了print A1

还有就是

无法通过宏展开的方式创建预处理器命令。即使宏的展开结果会生成形式上有效的命令,但预处理器不会执行它

你所要知道的小知识:

  1. 定义宏不占用内存,只有在使用时才分配内存
  2. 宏定义不存在类型问题,它的参数也是无类型的
  3. 字符串" "中不能使用宏定义
  4. 预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查
  5. 宏替换在预处理阶段进行,函数调用在编译后程序运行时进行,并且分配内存。预处理在编译之前,因此宏替换不分配内存
  6. 宏定义前面的替换必须是C语言合法的用户标识符 如 #define 0a 25 就是错误的
  7. 重复定义的宏只有最后一个是有效的
  8. 宏定义不是C语句,不必在行未加“;”号,如果加了连“;”号一起进行置换;

包含头文件的宏

用一个include表示,引入的头文件有两种区别

一种是C语言自带的头文件,用 <>来表示

#include 

 
 
   
   
   
   
  • 1

编译的时候会在编译器的bin目录下面查找

另一种是用户自定义头文件,用""来表示

#include "LED.h"

 
 
   
   
   
   
  • 1

编译时从对应自定义文件路径查找

当然 ,C语言自带头文件也可以使用 "" 这个我们讲解下:

  • “”先在自定义路径查找有无该头文件,有则包含该目录下的头文件,没有则到系统指定的目录下找该头文件
    -<>直接到系统指定的目录下查找该文件

也就是说使用双引号比使用尖括号多了一个查找路径

#include 引入头文件的本质就是源码替换,预处理的时候将那些.h文件中的代码替换到头文件引入的位置进行编译 比如printf就会从stdio.h中寻找对应源码,之后进行替换,再替换以后的结果再交给编译器处理,进行编译。

条件编译宏

c语言中条件编译相关的预编译指令

#define            定义一个预处理宏
#undef            取消宏的定义

#if 编译预处理中的条件命令,相当于C语法中的if语句
#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 与#ifdef相反,判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
defined  与#if, #elif配合使用,判断某个宏是否被定义

#error 编译程序时如果遇到#error就会生成一个编译错误提示信息并停止编译

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

举个简单的例子:

#define  ZZX

#ifdef ZZX
printf(“定义了Z小旋”);
#else
printf(“没有定义Z小旋”);
#endif

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上面的代码就会输出定义Z小旋 而不满足条件的代码就不会编译

我们经常使用条件编译来定义头文件

#ifndef _DataCollect_H_
#define _DataCollect_H_

#代码块

#endif

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

条件编译再我们正常程序的编写中是非常见的,你可以使用条件编译完成模块不同功能的设置,比如你想要用一套代码编译一个产品的两个功能,使用条件编译,一个#ifndef 即可完成两个不同代码块的切换,决定编译的时候生成的是哪个功能的代码。

总览图

在这里插入图片描述

预定义宏

宏名称 功能
_LINE_ 正在编译的文件的行号
_FILE_ 正在编译的文件的名字
_DATE_ 编译时刻的日期字符串
_TIME_ 编译时刻的时间字符串
_STDC_ 判断该程序是否为标准的c程序

标准的预定义宏都是用一两个下划线字符开头和结尾,这类宏不能被#undef所取消也不能被编程人员重新定义和修改 ,常用来说明文件信息等

#include 
int main() {
printf("Date : %s\n", __DATE__);
printf("Time : %s\n", __TIME__);
printf("File : %s\n", __FILE__);
printf("Line : %d\n", __LINE__);
system("pause");
return 0;
}

 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出如下:

Date : Sep 28 2019
Time : 21:46:50
File : C:\Users\48013\Desktop\小学弟的渣渣c\预定义宏.c
Line : 6

 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4

剩余部分宏:

宏名称 功能
_STDC_VERSION_ 表示ISO C的版本
_STDC_HOSTED_ 如果值为1的话,表示目标环境有完成的标准C库
_BYTE_ORDER_ 表示当前环境的字节序
_LP64_ 表示当前环境是不是64位,如果该值为1,则环境为64位环境

你可能感兴趣的:(编程基础)