預處理命令在我之前看過的C語言基礎教程中好像并沒有詳細說到,在現在的一些項目中預處理命令的出現頻率卻越來越多。事物的存在必有其存在的理由,于是就花時間去琢磨了一下,以及查閱相關資料,發現使用預處理命令去優化代碼可以達到很好的效果。預處理命令在某資料中是這樣描述的"C程序的源代碼中可包括各種編譯指令,這些指令稱為預處理命令。雖然它們實際上不是C語言的一部分,但卻擴展了C程序設計的環境"。預處理命令實質上是運行在編譯器編譯過程的指令。所以說在嵌入式程序設計中使用預處理命令不會占用最終目标運行系統的存儲空間。
預處理命令有哪些在ANSI标準定義的C語言預處理程序中包括下列命令:
#include,#define,#if,#else,#elif,#endif,#ifdef,#ifndef,#error,#undef,#line,#pragma等。從以上可以看出預處理命令的共同特點就是以"#"開頭。下面就分别介紹幾個在項目中使用比較多的預處理命令。
這個預處理命令算是使用的最多而又最重要的一個預處理命令了。它的作用你是否還記得?include就是"包含"的意思,在程序編譯的時候預處理器看到#include就會把<>尖括号或者" "中的那個文件找到,然後用該文件的内容替換掉#include <>這一行。
使用方法或者格式:
#include <xxx.h>
#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
#ifndef是"if not defined"的簡寫,是宏定義的一種,它是可以根據是否已經定義了一個變量來進行分支選擇,一般用于調試等。實際上确切地說這應該是預處理功能中三種(宏定義,文件包含和條件編譯)中的第三種——條件編譯。
條件指示符#ifndef的最主要目的是防止頭文件的重複包含和編譯。條件編譯當然也可以用條件語句來實現, 但是用條件語句将會對整個源程序進行編譯,生成的目标代碼程序很長,而采用條件編譯,則根據條件隻編譯其中的程序段1或程序段2,生成的目标程序較短。如果條件選擇的程序段很長,采用條件編譯的方法是十分必要的。
假如你有一個C源文件,它包含了多個頭文件,比如頭文件A和頭文件B,而頭文件B又包含了頭文件A,則最終的效果是,該源文件包含了兩次頭文件A。如果你在頭文件A裡定義了結構體或者類類型(這是最常見的情況),那麼問題來了,編譯時會報大量的重複定義錯誤。
使用方法或者格式:
//經常在H文件(頭文件)看到是格式:
#ifndef 标識符
#define 标識符
/*************
程序段
***************/
#endif
其他格式:
#ifndef表達式
/***********
程序段 1
***********/
#else
/***********
程序段 2
***********/
#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中的功能配置
總結。預處理命令主要是使用在程序的優化上。可以使程序設計更靈活更好看等等。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!