您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

C语言编程中的预处理命令你会用吗?

时间:2021-03-24 12:56:30  来源:  作者:

什么是预处理命令

预处理命令在我之前看过的C语言基础教程中好像并没有详细说到,在现在的一些项目中预处理命令的出现频率却越来越多。事物的存在必有其存在的理由,于是就花时间去琢磨了一下,以及查阅相关资料,发现使用预处理命令去优化代码可以达到很好的效果。预处理命令在某资料中是这样描述的"C程序的源代码中可包括各种编译指令,这些指令称为预处理命令。虽然它们实际上不是C语言的一部分,但却扩展了C程序设计的环境"。预处理命令实质上是运行在编译器编译过程的指令。所以说在嵌入式程序设计中使用预处理命令不会占用最终目标运行系统的存储空间。

预处理命令有哪些

在ANSI标准定义的C语言预处理程序中包括下列命令:

#include,#define,#if,#else,#elif,#endif,#ifdef,#ifndef,#error,#undef,#line,#pragma等。从以上可以看出预处理命令的共同特点就是以"#"开头。下面就分别介绍几个在项目中使用比较多的预处理命令。

  1. #include

这个预处理命令算是使用的最多而又最重要的一个预处理命令了。它的作用你是否还记得?include就是"包含"的意思,在程序编译的时候预处理器看到#include就会把<>尖括号或者" "中的那个文件找到,然后用该文件的内容替换掉#include <>这一行。

使用方法或者格式:

#include <xxx.h>
  1. #define

#define叫做宏定义,标识符为所定义的宏名,简称宏。标识符的命名规则与前面讲的变量的命名规则是一样的。#define 的功能是将标识符定义为其后的常量。一经定义,程序中就可以直接用标识符来表示这个常量。是不是与定义变量类似?但是要区分开!变量名表示的是一个变量,但宏名表示的是一个常量。可以给变量赋值,但绝不能给常量赋值。宏所表示的常量可以是数字、字符、字符串、表达式。使用宏定义可以用宏代替一个在程序中经常使用的常量。注意,是"经常"使用的。这样,当需要改变这个常量的值时,就不需要对整个程序一个一个进行修改,只需修改宏定义中的常量即可。且当常量比较长时,使用宏就可以用较短的有意义的标识符来代替它,这样编程的时候就会更方便,不容易出错。因此,宏定义的优点就是方便和易于维护。

需要注意的是,预处理指令不是语句,所以后面不能加分号。

使用方法或者格式:

#define xxx 1 //定义常量
#define xxx 'a' //定义字符
#define xxx "abcb" //定义字符串
#define xxx abc() //定义函数

还可以这样:

#define XX_ABS(x) ((x)>=0? (x):-(x)) //返回绝对值
#define XX_SIGN(x) ((x<0)?-1:((x>0)?1:0)) //正数返回 1,负数返回 -1 零返回 0
  1. #ifndef

#ifndef是"if not defined"的简写,是宏定义的一种,它是可以根据是否已经定义了一个变量来进行分支选择,一般用于调试等。实际上确切地说这应该是预处理功能中三种(宏定义,文件包含和条件编译)中的第三种——条件编译。

条件指示符#ifndef的最主要目的是防止头文件的重复包含和编译。条件编译当然也可以用条件语句来实现, 但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。如果条件选择的程序段很长,采用条件编译的方法是十分必要的。

假如你有一个C源文件,它包含了多个头文件,比如头文件A和头文件B,而头文件B又包含了头文件A,则最终的效果是,该源文件包含了两次头文件A。如果你在头文件A里定义了结构体或者类类型(这是最常见的情况),那么问题来了,编译时会报大量的重复定义错误。

使用方法或者格式:

//经常在H文件(头文件)看到是格式:
#ifndef 标识符
#define 标识符
/*************
程序段
***************/
#endif

其他格式:

#ifndef表达式
/***********
程序段 1
***********/
#else
/***********
程序段 2
***********/
#endif
  1. #if,#else,#elif,#endif

#if,#else,#elif#endif通常是组合使用,#if 叫条件预处理命令主要作用跟if判断语句相似,格式上的区别在在于使用#if需要使用#endif以表示结束。否则编译器会报错。主要作用就是通过判断条件是否满足去选择编译程序段。

使用方法或者格式:

格式1

#if XXX
/*****
程序段
*****/
#endif

格式2

#if XXX
/*****
程序段1
*****/
#else
/*****
程序段2
*****/
#endif

格式3

#ifndef XXX   0 //
#if (XXX==1)
/*****
程序段1
*****/
#elif (XXX==2)
/*****
程序段2
*****/
#else
/*****
程序段3
*****/
#endif

在以上的格式基础上如果再加上#define宏定义就更好地去控制了。比如下面的这个例子:

在一个嵌入式的小系统中有两种调试方式,一种是串口调试,一种是USB转串口调试。串口调试一般使用在系统程序开发阶段调试。而USB转串口调试一般使用在系统开发完成后的功能调试,因为USB还可以实现文件系统的挂载,在后期使用USB进行固件升级是非常方便的。现在的问题就是用什么方法可以更简单地实现串口调试与USB转串口调试的快速转换。

其中的方法之一就是使用条件预处理命令实现切换,

有四个函数:

USB_Init();
Usb_printf(char *p); //向usb转串口打印调试信息
Usart_Init();
Usart_printf(char *p); //向串口打印调试信息

实例过程如下:

#define PRMOS 0 //0 使用串口打印 1 使用usb打印信息

#if PRMOS
		#define DEBUG(s) Usb_printf(s)
#else
		#define DEBUG(s) Usart_printf(s)
#endif

int main(void)
{
    #if PRMOS
    USB_Init();
    #else
    Usart_Init();
    #endif
      While(1)
      {
          DEBUG("Hello, World! n");
          Delay_ms(1000);
      }
}

大致的思路就是使用#define定义一个常量和使用同一个标识符分别定义串口打印函数和usb打印函数,然后使用条件判断预处理#if通过判断PRMOS标识符选择编译哪一部分,在程序设计的时候也需要使用#if把USB和串口相关的代码区分开。这样在编译的时候始终都是编译一部分,所以整体的编译速度或者占用空间都是比较少的。在功能切换时只需要更改PRMOS的值就完成了全部代码的修改,这样是不是很方便。

这种程序设计方法在一些实时操作系统的配置中使用的比较多,例如FreeRTOS中的功能配置

C语言编程中的预处理命令你会用吗?

 


C语言编程中的预处理命令你会用吗?

 

总结。预处理命令主要是使用在程序的优化上。可以使程序设计更灵活更好看等等。



Tags:预处理命令   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
什么是预处理命令预处理命令在我之前看过的C语言基础教程中好像并没有详细说到,在现在的一些项目中预处理命令的出现频率却越来越多。事物的存在必有其存在的理由,于是就花时...【详细内容】
2021-03-24  Tags: 预处理命令  点击:(521)  评论:(0)  加入收藏
▌简易百科推荐
在go语言中使用viper之类的库很方便的处理yaml配置文件,但是在c语言中就比较麻烦,经过一番思索和借助强大的github,发现了一个libyaml c库,但是网上的例子都比较麻烦,而且比较繁...【详细内容】
2022-07-07  全栈搬砖家    Tags:yaml   点击:(19)  评论:(0)  加入收藏
目前数据的种类非常多,每种数据都支持sql语句,但是大家发现没有每种数据的SQL都有自己的语法特性,都是SQL语句都没有一个特定的语法标准,导致开发人员在开发的过程中无法任意选...【详细内容】
2022-07-05  海椰人    Tags:SQL语句   点击:(21)  评论:(0)  加入收藏
今天给大家分享一篇C语言编码规范,可以从源头上规避掉很多bug,希望能对大家有所帮助。 01 最重要的规则编写代码时最重要的一条规则是:检查周围的代码并尝试模仿它。 作为维护...【详细内容】
2022-07-04  嵌入式Linux    Tags:C语言   点击:(22)  评论:(0)  加入收藏
什么编程语言最好?这是个能让各路程序员吵翻天的话题,每种语言都有自己的拥趸,全球现在找出三五十种编程语言都不是问题,热门的至少也有20种,先不说这些语言好用与否,它们的性能也...【详细内容】
2022-07-01  HAIFEIX    Tags:编程语言   点击:(29)  评论:(0)  加入收藏
描述使用C#.Net开发的Windows远程控制管理系统SiMay,底层基于IOCP的异步通信模型,实时监控海量客户端,目前功能已实现:逐行扫描远程桌面协助、经典的文件管理、实时远程语音、实...【详细内容】
2022-06-30  IT微部落    Tags:C#   点击:(36)  评论:(0)  加入收藏
1.如何远程调试服务器的windows 服务-远程调试 远程服务调试前提:要确保你的代码和服务在同一个域下才可以 1)首先要在你vs的根目录下找到 Remote Debugger 文件夹,复制到带有...【详细内容】
2022-06-16  中年农码工    Tags:C#   点击:(57)  评论:(0)  加入收藏
描述一款高性能敏感词(非法词/脏字)检测过滤组件,附带繁体简体互换,支持全角半角互换,汉字转拼音,模糊搜索等功能。同时支持多种语言,C#、golang、java、javascript、python参数...【详细内容】
2022-06-14  IT微部落    Tags:C#   点击:(51)  评论:(0)  加入收藏
讲故事前几天看公司一个新项目使用了FluentValidation,大家都知道FluentValidation是一个非常强大的用于构建强类型验证规则的 .NET 框架,帮程序员解决了繁琐的校验问题,用起来...【详细内容】
2022-06-08  黑哥聊dotNet    Tags:FluentValidation   点击:(53)  评论:(0)  加入收藏
C语言中没有参数默认值的概念,但其实可以利用宏来模拟参数的默认值;我们之前介绍过可变参数宏与逗号运算符,将两者相结合的话可以定义出以下形式的宏:#define func(...) __fun...【详细内容】
2022-06-04  嵌入式ICU    Tags:C语言   点击:(66)  评论:(0)  加入收藏
请大家看下面的题目,应该选择哪个答案呢?选择题 1首先,此类题目每年的考试都会出现,也就是说,标识符这个知识点非常重要。此类题目一般都是送分题 ,只要掌握了标识符的命名规则,这...【详细内容】
2022-05-20  正直的苏离    Tags:标识符   点击:(74)  评论:(0)  加入收藏
相关文章
    无相关信息
站内最新
站内热门
站内头条