欧美激情久久久久久,人妻久久精品天天中文字幕,国产精品无码色一区二区三区按摩 ,日韩中文无码有码免费视频

C語(yǔ)言預處理命令

預處理(或稱(chēng)預編譯)是指在進(jìn)行編譯的第一遍掃描(詞法掃描和語(yǔ)法分析)之前所作的工作。預處理指令指示在程序正式編譯前就由編譯器進(jìn)行的操作,可放在程序中任何位置。


預處理是C語(yǔ)言的一個(gè)重要功能,它由預處理程序負責完成。當對一個(gè)源文件進(jìn)行編譯時(shí),系統將自動(dòng)引用預處理程序對源程序中的預處理部分作處理,處理完畢自動(dòng)進(jìn)入對源程序的編譯。


C語(yǔ)言提供多種預處理功能,主要處理#開(kāi)始的預編譯指令,如宏定義(#define)、文件包含(#include)、條件編譯(#ifdef)等。合理使用預處理功能編寫(xiě)的程序便于閱讀、修改、移植和調試,也有利于模塊化程序設計。


二  宏定義


C語(yǔ)言源程序中允許用一個(gè)標識符來(lái)表示一個(gè)字符串,稱(chēng)為“宏”。被定義為宏的標識符稱(chēng)為“宏名”。在編譯預處理時(shí),對程序中所有出現的宏名,都用宏定義中的字符串去代換,這稱(chēng)為宏替換或宏展開(kāi)。


宏定義是由源程序中的宏定義命令完成的。宏替換是由預處理程序自動(dòng)完成的。


在C語(yǔ)言中,宏定義分為有參數和無(wú)參數兩種。下面分別討論這兩種宏的定義和調用。


2.1 無(wú)參宏定義


無(wú)參宏的宏名后不帶參數。其定義的一般形式為:


#define  標識符  字符串


其中,“#”表示這是一條預處理命令(以#開(kāi)頭的均為預處理命令)?!癲efine”為宏定義命令?!皹俗R符”為符號常量,即宏名?!白址笨梢允浅?、表達式、格式串等。


宏定義用宏名來(lái)表示一個(gè)字符串,在宏展開(kāi)時(shí)又以該字符串取代宏名。這只是一種簡(jiǎn)單的文本替換,預處理程序對它不作任何檢查。如有錯誤,只能在編譯已被宏展開(kāi)后的源程序時(shí)發(fā)現。


注意理解宏替換中“換”的概念,即在對相關(guān)命令或語(yǔ)句的含義和功能作具體分析之前就要進(jìn)行文本替換。


【例1】定義常量:


#define MAX_TIME 1000


若在程序里面寫(xiě)if(time < MAX_TIME){.........},則編譯器在處理該代碼前會(huì )將MAX_TIME替換為1000。


注意,這種情況下使用const定義常量可能更好,如const int MAX_TIME = 1000;。因為const常量有數據類(lèi)型,而宏常量沒(méi)有數據類(lèi)型。編譯器可以對前者進(jìn)行類(lèi)型安全檢查,而對后者只進(jìn)行簡(jiǎn)單的字符文本替換,沒(méi)有類(lèi)型安全檢查,并且在字符替換時(shí)可能會(huì )產(chǎn)生意料不到的錯誤。


【例2】反例:


#define pint (int*)

pint pa, pb;

     

本意是定義pa和pb均為int型指針,但實(shí)際上變成int* pa,pb;。pa是int型指針,而pb是int型變量。本例中可用typedef來(lái)代替define,這樣pa和pb就都是int型指針了。


因為宏定義只是簡(jiǎn)單的字符串代換,在預處理階段完成,而typedef是在編譯時(shí)處理的,它不是作簡(jiǎn)單的代換,而是對類(lèi)型說(shuō)明符重新命名,被命名的標識符具有類(lèi)型定義說(shuō)明的功能。


typedef的具體說(shuō)明見(jiàn)附錄6.4。


無(wú)參宏注意事項:


宏名一般用大寫(xiě)字母表示,以便于與變量區別。宏定義末尾不必加分號,否則連分號一并替換。宏定義可以嵌套。


可用#undef命令終止宏定義的作用域。


使用宏可提高程序通用性和易讀性,減少不一致性,減少輸入錯誤和便于修改。如數組大小常用宏定義。預處理是在編譯之前的處理,而編譯工作的任務(wù)之一就是語(yǔ)法檢查,預處理不做語(yǔ)法檢查。宏定義寫(xiě)在函數的花括號外邊,作用域為其后的程序,通常在文件的最開(kāi)頭。字符串" "中永遠不包含宏,否則該宏名當字符串處理。

宏定義不分配內存,變量定義分配內存。


2.2 帶參宏定義


C語(yǔ)言允許宏帶有參數。在宏定義中的參數稱(chēng)為形式參數,在宏調用中的參數稱(chēng)為實(shí)際參數。


對帶參數的宏,在調用中,不僅要宏展開(kāi),而且要用實(shí)參去代換形參。


帶參宏定義的一般形式為:

#define  宏名(形參表)  字符串


在字符串中含有各個(gè)形參。


帶參宏調用的一般形式為:

宏名(實(shí)參表);


在宏定義中的形參是標識符,而宏調用中的實(shí)參可以是表達式。


在帶參宏定義中,形參不分配內存單元,因此不必作類(lèi)型定義。而宏調用中的實(shí)參有具體的值,要用它們去代換形參,因此必須作類(lèi)型說(shuō)明,這點(diǎn)與函數不同。函數中形參和實(shí)參是兩個(gè)不同的量,各有自己的作用域,調用時(shí)要把實(shí)參值賦予形參,進(jìn)行“值傳遞”。而在帶參宏中只是符號代換,不存在值傳遞問(wèn)題。


【例3】


 #define INC(x) x+1  //宏定義

 y = INC(5);         //宏調用


在宏調用時(shí),用實(shí)參5去代替形參x,經(jīng)預處理宏展開(kāi)后的語(yǔ)句為y=5+1。


【例4】反例:


#define SQ(r)    r*r

     

上述這種實(shí)參為表達式的宏定義,在一般使用時(shí)沒(méi)有問(wèn)題;但遇到如area=SQ(a+b);時(shí)就會(huì )出現問(wèn)題,宏展開(kāi)后變?yōu)閍rea=a+b*a+b;,顯然違背本意。


相比之下,函數調用時(shí)會(huì )先把實(shí)參表達式的值(a+b)求出來(lái)再賦予形參r;而宏替換對實(shí)參表達式不作計算直接地照原樣代換。因此在宏定義中,字符串內的形參通常要用括號括起來(lái)以避免出錯。


進(jìn)一步地,考慮到運算符優(yōu)先級和結合性,遇到area=10/SQ(a+b);時(shí)即使形參加括號仍會(huì )出錯。因此,還應在宏定義中的整個(gè)字符串外加括號,


綜上,正確的宏定義是#define SQ(r) ((r)*(r)),即宏定義時(shí)建議所有的層次都要加括號。


【例5】帶參函數和帶參宏的區別:


 #define SQUARE(x) ((x)*(x))


 int Square(int x){


   return (x * x); //未考慮溢出保護


}


int main(void){


    int i = 1;


   while(i <= 5)


       printf("i = %d, Square = %d\n", i, Square(i++));


   int j = 1;


   while(j <= 5)


      printf("j = %d, SQUARE = %d\n", j, SQUARE(j++));

  return 0;


 }


執行后輸出如下:


i = 2, Square = 1


i = 3, Square = 4


i = 4, Square = 9


i = 5, Square = 16


i = 6, Square = 25


j = 3, SQUARE = 1


j = 5, SQUARE = 9


j = 7, SQUARE = 25


本例意在說(shuō)明,把同一表達式用函數處理與用宏處理兩者的結果有可能是不同的。


調用Square函數時(shí),把實(shí)參i值傳給形參x后自增1,再輸出函數值。因此循環(huán)5次,輸出1~5的平方值。調用SQUARE宏時(shí),SQUARE(j++)被代換為((j++)*(j++))。在第一次循環(huán)時(shí),表達式中j初值為1,兩者相乘的結果為1。相乘后j自增兩次變?yōu)?,因此表達式中第二次相乘時(shí)結果為3*3=9。同理,第三次相乘時(shí)結果為5*5=25,并在此次循環(huán)后j值變?yōu)?,不再滿(mǎn)足循環(huán)條件,停止循環(huán)。


從以上分析可以看出函數調用和宏調用二者在形式上相似,在本質(zhì)上是完全不同的。


帶參宏注意事項:


宏名和形參表的括號間不能有空格。

宏替換只作替換,不做計算,不做表達式求解。

函數調用在編譯后程序運行時(shí)進(jìn)行,并且分配內存。宏替換在編譯前進(jìn)行,不分配內存。

函數只有一個(gè)返回值,利用宏則可以設法得到多個(gè)值。

宏展開(kāi)使源程序變長(cháng),函數調用不會(huì )。

宏展開(kāi)不占用運行時(shí)間,只占編譯時(shí)間,函數調用占運行時(shí)間(分配內存、保留現場(chǎng)、值傳遞、返回值)。

為防止無(wú)限制遞歸展開(kāi),當宏調用自身時(shí),不再繼續展開(kāi)。

如:#define TEST(x)  (x + TEST(x))被展開(kāi)為1 + TEST(1)。


2.3 實(shí)踐用例


包括基本用法(及技巧)和特殊用法(#和##等)。


#define可以定義多條語(yǔ)句,以替代多行的代碼,但應注意替換后的形式,避免出錯。宏定義在換行時(shí)要加上一個(gè)反斜杠”\”,而且反斜杠后面直接回車(chē),不能有空格。


2.3.1 基本用法

1. 定義常量:


#define PI   3.1415926

將程序中出現的PI全部換成3.1415926。


2. 定義表達式:

 #define M   (y*y+3*y)


編碼時(shí)所有的表達式(y*y+3*y)都可由M代替,而編譯時(shí)先由預處理程序進(jìn)行宏替換,即用(y*y+3*y)表達式去置換所有的宏名M,然后再進(jìn)行編譯。


注意,在宏定義中表達式(y*y+3*y)兩邊的括號不能少,否則可能會(huì )發(fā)生錯誤。如s=3*M+4*M在預處理時(shí)經(jīng)宏展開(kāi)變?yōu)閟=3*(y*y+3*y)+4*(y*y+3*y),如果宏定義時(shí)不加括號就展開(kāi)為s=3*y*y+3*y+4*y*y+3*y,顯然不符合原意。因此在作宏定義時(shí)必須十分注意。應保證在宏替換之后不發(fā)生錯誤。


3. 得到指定地址上的一個(gè)字節或字:


#define MEM_B(x)     (*((char *)(x)))

 #define MEM_W(x)     (*((short *)(x)))


4. 求最大值和最小值:


#define MAX(x, y)     (((x) > (y)) ? (x) : (y))

#define MIN(x, y)     (((x) < (y)) ? (x) : (y))


以后使用MAX (x,y)或MIN (x,y),就可分別得到x和y中較大或較小的數。


但這種方法存在弊病,例如執行MAX(x++, y)時(shí),x++被執行多少次取決于x和y的大??;當宏參數為函數也會(huì )存在類(lèi)似的風(fēng)險。所以建議用內聯(lián)函數而不是這種方法提高速度。不過(guò),雖然存在這樣的弊病,但宏定義非常靈活,因為x和y可以是各種數據類(lèi)型。


以下給出MAX宏的兩個(gè)安全版本(源自linux/kernel.h):


 #define MAX_S(x, y) ({ \


    const typeof(x) _x = (x);  \


    const typeof(y) _y = (y);  \


    (void)(&_x == &_y);       \


     _x > _y ? _x : _y; })



 #define TMAX_S(type, x, y) ({ \


     type _x = (x);  \


     type _y = (y);  \


    _x > _y ? _x: _y; })


Gcc編譯器將包含在圓括號和大括號雙層括號內的復合語(yǔ)句看作是一個(gè)表達式,它可出現在任何允許表達式的地方;復合語(yǔ)句中可聲明局部變量,判斷循環(huán)條件等復雜處理。而表達式的最后一條語(yǔ)句必須是一個(gè)表達式,它的計算結果作為返回值。MAX_S和TMAX_S宏內就定義局部變量以消除參數副作用。


MAX_S宏內(void)(&_x == &_y)語(yǔ)句用于檢查參數類(lèi)型一致性。當參數x和y類(lèi)型不同時(shí),會(huì )產(chǎn)生” comparison of distinct pointer types lacks a cast”的編譯警告。


注意,MAX_S和TMAX_S宏雖可避免參數副作用,但會(huì )增加內存開(kāi)銷(xiāo)并降低執行效率。若使用者能保證宏參數不存在副作用,則可選用普通定義(即MAX宏)。 


5. 得到一個(gè)成員在結構體中的偏移量(lint 545告警表示"&用法值得懷疑",此處抑制該警告):


 #define FPOS(type, field) \

 /*lint -e545 */ ((int)&((type *)0)-> field) /*lint +e545 */


6. 得到一個(gè)結構體中某成員所占用的字節數:


#define FSIZ(type, field)    sizeof(((type *)0)->field)


7. 按照LSB格式把兩個(gè)字節轉化為一個(gè)字(word):


#define FLIPW(arr)          ((((short)(arr)[0]) * 256) + (arr)[1])


8. 按照LSB格式把一個(gè)字(word)轉化為兩個(gè)字節:


#define FLOPW(arr, val) \

    (arr)[0] = ((val) / 256); \

    (arr)[1] = ((val) & 0xFF)


9. 得到一個(gè)變量的地址:


#define B_PTR(var)       ((char *)(void *)&(var))

#define W_PTR(var)       ((short *)(void *)&(var))


10. 得到一個(gè)字(word)的高位和低位字節:


#define WORD_LO(x)       ((char)((short)(x)&0xFF))

#define WORD_HI(x)       ((char)((short)(x)>>0x8))


11. 返回一個(gè)比X大的最接近的8的倍數:


#define RND8(x)           ((((x) + 7) / 8) * 8)


12. 將一個(gè)字母轉換為大寫(xiě)或小寫(xiě):


#define UPCASE(c)         (((c) >= 'a' && (c) <= 'z') ? ((c) + 'A' - 'a') : (c))

#define LOCASE(c)         (((c) >= 'A' && (c) <= 'Z') ? ((c) + 'a' - 'A') : (c))


注意,UPCASE和LOCASE宏僅適用于A(yíng)SCII編碼(依賴(lài)于碼字順序和連續性),而不適用于EBCDIC編碼。


13. 判斷字符是不是10進(jìn)值的數字:


 #define ISDEC(c)          ((c) >= '0' && (c) <= '9')


14. 判斷字符是不是16進(jìn)值的數字:


  #define ISHEX(c)          (((c) >= '0' && (c) <= '9') ||\


     ((c) >= 'A' && (c) <= 'F') ||\


     ((c) >= 'a' && (c) <= 'f'))



15. 防止溢出的一個(gè)方法:


 #define INC_SAT(val)      (val = ((val)+1 > (val)) ? (val)+1 : (val))

16. 返回數組元素的個(gè)數:


#define ARR_SIZE(arr)     (sizeof((arr)) / sizeof((arr[0])))


17. 對于IO空間映射在存儲空間的結構,輸入輸出處理:


 #define INP(port)           (*((volatile char *)(port)))


 #define INPW(port)          (*((volatile short *)(port)))


 #define INPDW(port)         (*((volatile int *)(port)))


 #define OUTP(port, val)     (*((volatile char *)(port)) = ((char)(val)))


 #define OUTPW(port, val)    (*((volatile short *)(port)) = ((short)(val)))


 #define OUTPDW(port, val)   (*((volatile int *)(port)) = ((int)(val)))


18. 使用一些宏跟蹤調試:


ANSI標準說(shuō)明了五個(gè)預定義的宏名(注意雙下劃線(xiàn)),即:__LINE__、__FILE __、__DATE__、__TIME__、__STDC __。


若編譯器未遵循ANSI標準,則可能僅支持以上宏名中的幾個(gè),或根本不支持。此外,編譯程序可能還提供其它預定義的宏名(如__FUCTION__)。


__DATE__宏指令含有形式為月/日/年的串,表示源文件被翻譯到代碼時(shí)的日期;源代碼翻譯到目標代碼的時(shí)間作為串包含在__TIME__中。串形式為時(shí):分:秒。


如果實(shí)現是標準的,則宏__STDC__含有十進(jìn)制常量1。如果它含有任何其它數,則實(shí)現是非標準的。


可以借助上面的宏來(lái)定義調試宏,輸出數據信息和所在文件所在行。如下所示:


 #define MSG(msg, date)      printf(msg);printf(“[%d][%d][%s]”,date,__LINE__,__FILE__)

     

19. 用do{…}while(0)語(yǔ)句包含多語(yǔ)句防止錯誤:


#define DO(a, b) do{\

    a+b;\

    a++;\

 }while(0)


20. 實(shí)現類(lèi)似“重載”功能


C語(yǔ)言中沒(méi)有swap函數,而且不支持重載,也沒(méi)有模板概念,所以對于每種數據類(lèi)型都要寫(xiě)出相應的swap函數,如:


IntSwap(int *,  int *);  


LongSwap(long *,  long *);  


StringSwap(char *,  char *); 


可采用宏定義TSWAP (t,x,y)或SWAP(x, y)交換兩個(gè)整型或浮點(diǎn)參數:


 

#define TSWAP(type, x, y) do{ \


      type _y = y; \


      y = x;       \


      x = _y;      \


  }while(0)


  #define SWAP(x, y) do{ \


      x = x + y;   \


      y = x - y;   \


      x = x - y;   \


 }while(0)



 int main(void){


     int a = 10, b = 5;


     TSWAP(int, a, b);


     printf(“a=%d, b=%d\n”, a, b);


     return 0;


}


21. 1年中有多少秒(忽略閏年問(wèn)題) :


 #define SECONDS_PER_YEAR    (60UL * 60 * 24 * 365)


該表達式將使一個(gè)16位機的整型數溢出,因此用長(cháng)整型符號L告訴編譯器該常數為長(cháng)整型數。


注意,不可定義為#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL,否則將產(chǎn)生(31536000)UL而非31536000UL,這會(huì )導致編譯報錯。


以下幾種寫(xiě)法也正確:


 #define SECONDS_PER_YEAR    60 * 60 * 24 * 365UL


 #define SECONDS_PER_YEAR    (60UL * 60UL * 24UL * 365UL)


 #define SECONDS_PER_YEAR    ((unsigned long)(60 * 60 * 24 * 365))

}


22. 取消宏定義:


#define [MacroName] [MacroValue]       //定義宏


#undef [MacroName]                     //取消宏



宏定義必須寫(xiě)在函數外,其作用域為宏定義起到源程序結束。如要終止其作用域可使用#undef命令:


 #define PI   3.14159


 int main(void){

     //……

 }


 #undef PI

 int func(void){

     //……

 }


表示PI只在main函數中有效,在func1中無(wú)效。


2.3.2 特殊用法


主要涉及C語(yǔ)言宏里#和##的用法,以及可變參數宏。


2.3.2.1 字符串化操作符#


在C語(yǔ)言的宏中,#的功能是將其后面的宏參數進(jìn)行字符串化操作(Stringfication),簡(jiǎn)單說(shuō)就是將宏定義中的傳入參數名轉換成用一對雙引號括起來(lái)參數名字符串。#只能用于有傳入參數的宏定義中,且必須置于宏定義體中的參數名前。例如:


 #define EXAMPLE(instr)      printf("The input string is:\t%s\n", #instr)

 #define EXAMPLE1(instr)     #instr


當使用該宏定義時(shí),example(abc)在編譯時(shí)將會(huì )展開(kāi)成printf("the input string is:\t%s\n","abc");string str=example1(abc)將會(huì )展成string str="abc"。


 又如下面代碼中的宏:


  define WARN_IF(exp) do{ \


     if(exp) \


         fprintf(stderr, "Warning: " #exp"\n"); \


 } while(0)


則代碼WARN_IF (divider == 0)會(huì )被替換為:


do{

     if(divider == 0)

     

       fprintf(stderr, "Warning" "divider == 0" "\n");

 } while(0)


這樣,每次divider(除數)為0時(shí)便會(huì )在標準錯誤流上輸出一個(gè)提示信息。


注意#宏對空格的處理:


忽略傳入參數名前面和后面的空格。如str= example1(   abc )會(huì )被擴展成 str="abc"。

當傳入參數名間存在空格時(shí),編譯器會(huì )自動(dòng)連接各個(gè)子字符串,每個(gè)子字符串間只以一個(gè)空格連接。如str= example1( abc    def)會(huì )被擴展成 str="abc def"。


2.3.2.2 符號連接操作符##


 ##稱(chēng)為連接符(concatenator或token-pasting),用來(lái)將兩個(gè)Token連接為一個(gè)Token。注意這里連接的對象是Token就行,而不一定是宏的變量。例如:

 #define PASTER(n)     printf( "token" #n " = %d", token##n)


 int token9 = 9;


則運行PASTER(9)后輸出結果為token9 = 9。


又如要做一個(gè)菜單項命令名和函數指針組成的結構體數組,并希望在函數名和菜單項命令名之間有直觀(guān)的、名字上的關(guān)系。那么下面的代碼就非常實(shí)用:


struct command{


     char * name;


     void (*function)(void);


 };


#define COMMAND(NAME)   {NAME, NAME##_command}


然后,就可用一些預先定義好的命令來(lái)方便地初始化一個(gè)command結構的數組:


 struct command commands[] = {


     COMMAND(quit),


     COMMAND(help),


     //...


 }


COMMAND宏在此充當一個(gè)代碼生成器的作用,這樣可在一定程度上減少代碼密度,間接地也可減少不留心所造成的錯誤。


還可以用n個(gè)##符號連接n+1個(gè)Token,這個(gè)特性是#符號所不具備的。如:


 #define  LINK_MULTIPLE(a, b, c, d)      a##_##b##_##c##_##d


 typedef struct record_type LINK_MULTIPLE(name, company, position, salary);


這里這個(gè)語(yǔ)句將展開(kāi)為typedef struct record_type name_company_position_salary。


注意:


當用##連接形參時(shí),##前后的空格可有可無(wú)。

連接后的實(shí)際參數名,必須為實(shí)際存在的參數名或是編譯器已知的宏定義。

凡是宏定義里有用'#'或'##'的地方,宏參數是不會(huì )再展開(kāi)。如:


 #define STR(s)       #s


 #define CONS(a,b)    int(a##e##b)


則printf("int max: %s\n", STR(INT_MAX))會(huì )被展開(kāi)為printf("int max: %s\n", "INT_MAX")。其中,變量INT_MAX為int型的最大值,其值定義在<climits.h>中。printf("%s\n", CONS(A, A))會(huì )被展開(kāi)為printf("%s\n", int(AeA)),從而編譯報錯。


INT_MAX和A都不會(huì )再被展開(kāi),多加一層中間轉換宏即可解決這個(gè)問(wèn)題。加這層宏是為了把所有宏的參數在這層里全部展開(kāi),那么在轉換宏里的那一個(gè)宏(如_STR)就能得到正確的宏參數。


#define _STR(s)         #s 


#define STR(s)          _STR(s)       // 轉換宏


#define _CONS(a,b)      int(a##e##b)


#define CONS(a,b)       _CONS(a,b)    // 轉換宏


則printf("int max: %s\n", STR(INT_MAX))輸出為int max: 0x7fffffff;而printf("%d\n", CONS(A, A))輸出為200。


這種分層展開(kāi)的技術(shù)稱(chēng)為宏的Argument Prescan,參見(jiàn)附錄6.1。




2.3.2.3 字符化操作符@#


@#稱(chēng)為字符化操作符(charizing),只能用于有傳入參數的宏定義中,且必須置于宏定義體的參數名前。作用是將傳入的單字符參數名轉換成字符,以一對單引號括起來(lái)。


 #define makechar(x)    #@x

 a = makechar(b);


展開(kāi)后變成a= 'b'。 


2.3.2.4 可變參數宏


在C語(yǔ)言宏中稱(chēng)為Variadic Macro,即變參宏。C99編譯器標準允許定義可變參數宏(Macros with a Variable Number of Arguments),這樣就可以使用擁有可變參數表的宏。


可變參數宏的一般形式為:


#define  DBGMSG(format, ...)  fprintf (stderr, format, __VA_ARGS__)


省略號代表一個(gè)可以變化的參數表,變參必須作為參數表的最右一項出現。使用保留名__VA_ARGS__ 把參數傳遞給宏。在調用宏時(shí),省略號被表示成零個(gè)或多個(gè)符號(包括里面的逗號),一直到到右括號結束為止。當被調用時(shí),在宏體(macro body)中,那些符號序列集合將代替里面的__VA_ARGS__標識符。當宏的調用展開(kāi)時(shí),實(shí)際的參數就傳遞給fprintf ()。


注意:可變參數宏不被ANSI/ISO C++所正式支持。因此,應當檢查編譯器是否支持這項技術(shù)。 


在標準C里,不能省略可變參數,但卻可以給它傳遞一個(gè)空的參數,這會(huì )導致編譯出錯。因為宏展開(kāi)后,里面的字符串后面會(huì )有個(gè)多余的逗號。為解決這個(gè)問(wèn)題,GNU CPP中做了如下擴展定義:


#define  DBGMSG(format, ...)  fprintf (stderr, format, ##__VA_ARGS__)


若可變參數被忽略或為空,##操作將使編譯器刪除它前面多余的逗號(否則會(huì )編譯出錯)。若宏調用時(shí)提供了可變參數,編譯器會(huì )把這些可變參數放到逗號的后面。


同時(shí),GCC還支持顯式地命名變參為args,如同其它參數一樣。如下格式的宏擴展:


#define  DBGMSG(format, args...)  fprintf (stderr, format, ##args)


這樣寫(xiě)可讀性更強,并且更容易進(jìn)行描述。


用GCC和C99的可變參數宏, 可以更方便地打印調試信息,如:


 #ifdef DEBUG


     #define DBGPRINT(format, args...) \


         fprintf(stderr, format, ##args)


 #else


     #define DBGPRINT(format, args...)


 #endif


這樣定義之后,代碼中就可以用dbgprint了,例如dbgprint ("aaa [%s]", __FILE__)。


結合第4節的“條件編譯”功能,可以構造出如下調試打印宏:

 

 #ifdef LOG_TEST_DEBUG


      /* OMCI調試日志宏 */


    //以10進(jìn)制格式日志整型變量


      #define PRINT_DEC(x)          printf(#x" = %d\n", x)


      #define PRINT_DEC2(x,y)       printf(#x" = %d\n", y)


     //以16進(jìn)制格式日志整型變量


      #define PRINT_HEX(x)          printf(#x" = 0x%-X\n", x)


      #define PRINT_HEX2(x,y)       printf(#x" = 0x%-X\n", y)


      //以字符串格式日志字符串變量


     #define PRINT_STR(x)          printf(#x" = %s\n", x)


     #define PRINT_STR2(x,y)       printf(#x" = %s\n", y)



     //日志提示信息


     #define PROMPT(info)          printf("%s\n", info)


     //調試定位信息打印宏


     #define  TP                   printf("%-4u - [%s<%s>]\n", __LINE__, __FILE__, __FUNCTION__);


     //調試跟蹤宏,在待日志信息前附加日志文件名、行數、函數名等信息


     #define TRACE(fmt, args...)\


     do{\


        printf("[%s(%d)<%s>]", __FILE__, __LINE__, __FUNCTION__);\


        printf((fmt), ##args);\


     }while(0)


 #else


     #define PRINT_DEC(x)


     #define PRINT_DEC2(x,y)


     #define PRINT_HEX(x)


     #define PRINT_HEX2(x,y)


     #define PRINT_STR(x)


     #define PRINT_STR2(x,y)


     #define PROMPT(info)


     #define  TP


     #define TRACE(fmt, args...)


 #endif

 


三  文件包含


文件包含命令行的一般形式為:


#include "文件名"


通常,該文件是后綴名為"h"或"hpp"的頭文件。文件包含命令把指定頭文件插入該命令行位置取代該命令行,從而把指定的文件和當前的源程序文件連成一個(gè)源文件。


在程序設計中,文件包含是很有用的。一個(gè)大程序可以分為多個(gè)模塊,由多個(gè)程序員分別編程。有些公用的符號常量或宏定義等可單獨組成一個(gè)文件,在其它文件的開(kāi)頭用包含命令包含該文件即可使用。這樣,可避免在每個(gè)文件開(kāi)頭都去書(shū)寫(xiě)那些公用量,從而節省時(shí)間,并減少出錯。


對文件包含命令要說(shuō)明以下幾點(diǎn):


包含命令中的文件名可用雙引號括起來(lái),也可用尖括號括起來(lái),如#include "common.h"和#include<math.h>。但這兩種形式是有區別的:使用尖括號表示在包含文件目錄中去查找(包含目錄是由用戶(hù)在設置環(huán)境時(shí)設置的include目錄),而不在當前源文件目錄去查找;


使用雙引號則表示首先在當前源文件目錄中查找,若未找到才到包含目錄中去查找。用戶(hù)編程時(shí)可根據自己文件所在的目錄來(lái)選擇某一種命令形式。


一個(gè)include命令只能指定一個(gè)被包含文件,若有多個(gè)文件要包含,則需用多個(gè)include命令。文件包含允許嵌套,即在一個(gè)被包含的文件中又可以包含另一個(gè)文件。

 


四  條件編譯


一般情況下,源程序中所有的行都參加編譯。但有時(shí)希望對其中一部分內容只在滿(mǎn)足一定條件才進(jìn)行編譯,也就是對一部分內容指定編譯的條件,這就是“條件編譯”。有時(shí),希望當滿(mǎn)足某條件時(shí)對一組語(yǔ)句進(jìn)行編譯,而當條件不滿(mǎn)足時(shí)則編譯另一組語(yǔ)句。


條件編譯功能可按不同的條件去編譯不同的程序部分,從而產(chǎn)生不同的目標代碼文件。這對于程序的移植和調試是很有用的。


條件編譯有三種形式,下面分別介紹。


4.1 #ifdef形式


#ifdef  標識符  (或#if defined標識符)


    程序段1


#else


    程序段2


#endif



如果標識符已被#define命令定義過(guò),則對程序段1進(jìn)行編譯;否則對程序段2進(jìn)行編譯。如果沒(méi)有程序段2(它為空),#else可以沒(méi)有,即可以寫(xiě)為:


#ifdef  標識符  (或#if defined標識符)


    程序段


#endif


這里的“程序段”可以是語(yǔ)句組,也可以是命令行。這種條件編譯可以提高C源程序的通用性。


【例6】


#define NUM OK


 int main(void){


     struct stu{


          int num;


          char *name;


          char sex;


          float score;


     }*ps;


     ps=(struct stu*)malloc(sizeof(struct stu));


     ps->num = 102;


     ps->name = "Zhang ping";


     ps->sex = 'M';


     ps->score = 62.5;


 #ifdef NUM


     printf("Number=%d\nScore=%f\n", ps->num, ps->score); /*--Execute--*/


 #else


     printf("Name=%s\nSex=%c\n", ps->name, ps->sex);


 #endif


     free(ps);


     return 0;


 }

     

由于在程序中插入了條件編譯預處理命令,因此要根據NUM是否被定義過(guò)來(lái)決定編譯哪個(gè)printf語(yǔ)句。而程序首行已對NUM作過(guò)宏定義,因此應對第一個(gè)printf語(yǔ)句作編譯,故運行結果是輸出了學(xué)號和成績(jì)。


程序首行定義NUM為字符串“OK”,其實(shí)可為任何字符串,甚至不給出任何字符串,即#define NUM也具有同樣的意義。只有取消程序首行宏定義才會(huì )去編譯第二個(gè)printf語(yǔ)句。


4.2 #ifndef 形式


#ifndef  標識符


    程序段1


#else


    程序段2


#endif



如果標識符未被#define命令定義過(guò),則對程序段1進(jìn)行編譯,否則對程序段2進(jìn)行編譯。這與#ifdef形式的功能正相反。


“#ifndef  標識符”也可寫(xiě)為“#if  !(defined 標識符)”。


4.3 #if形式


#if 常量表達式


    程序段1


#else


    程序段2


#endif


如果常量表達式的值為真(非0),則對程序段1 進(jìn)行編譯,否則對程序段2進(jìn)行編譯。因此可使程序在不同條件下,完成不同的功能。


【例7】輸入一行字母字符,根據需要設置條件編譯,使之能將字母全改為大寫(xiě)或小寫(xiě)字母輸出。


#define CAPITAL_LETTER   1


  int main(void){


      char szOrig[] = "C Language", cChar;


      int dwIdx = 0;


      while((cChar = szOrig[dwIdx++]) != '\0')


      {


  #if CAPITAL_LETTER


         if((cChar >= 'a') && (cChar <= 'z')) cChar = cChar - 0x20;


  #else


         if((cChar >= 'A') && (cChar <= 'Z')) cChar = cChar + 0x20;


 #endif


         printf("%c", cChar);


    }

     return 0;

 }


在程序第一行定義宏CAPITAL_LETTER為1,因此在條件編譯時(shí)常量表達式CAPITAL_LETTER的值為真(非零),故運行后使小寫(xiě)字母變成大寫(xiě)(C LANGUAGE)。


本例的條件編譯當然也可以用if條件語(yǔ)句來(lái)實(shí)現。但是用條件語(yǔ)句將會(huì )對整個(gè)源程序進(jìn)行編譯,生成的目標代碼程序很長(cháng);而采用條件編譯,則根據條件只編譯其中的程序段1或程序段2,生成的目標程序較短。如果條件編譯的程序段很長(cháng),采用條件編譯的方法是十分必要的。


4.4 實(shí)踐用例


1. 屏蔽跨平臺差異


在大規模開(kāi)發(fā)過(guò)程中,特別是跨平臺和系統的軟件里,可以在編譯時(shí)通過(guò)條件編譯設置編譯環(huán)境。


例如,有一個(gè)數據類(lèi)型,在Windows平臺中應使用long類(lèi)型表示,而在其他平臺應使用float表示。這樣往往需要對源程序作必要的修改,這就降低了程序的通用性??梢杂靡韵碌臈l件編譯:


 #ifdef WINDOWS


     #define MYTYPE long


 #else


     #define MYTYPE float


 #endif


如果在Windows上編譯程序,則可以在程序的開(kāi)始加上#define WINDOWS,這樣就編譯命令行    #define MYTYPE long;


如果在這組條件編譯命令前曾出現命令行#define WINDOWS 0,則預編譯后程序中的MYTYPE都用float代替。這樣,源程序可以不必作任何修改就可以用于不同類(lèi)型的計算機系統。


2. 包含程序功能模塊


例如,在程序首部定義#ifdef FLV:


 #ifdef FLV


    include"fastleave.c"


 #endif


如果不許向別的用戶(hù)提供該功能,則在編譯之前將首部的FLV加一下劃線(xiàn)即可。


3. 開(kāi)關(guān)調試信息


調試程序時(shí),常常希望輸出一些所需的信息以便追蹤程序的運行。而在調試完成后不再輸出這些信息??梢栽谠闯绦蛑胁迦胍韵碌臈l件編譯段:


 #ifdef DEBUG


     printf("device_open(%p)\n", file);


 #endif


 如果在它的前面有以下命令行#define DEBUG,則在程序運行時(shí)輸出file指針的值,以便調試分析。調試完成后只需將這個(gè)define命令行刪除即可,這時(shí)所有使用DEBUG作標識符的條件編譯段中的printf語(yǔ)句不起作用,即起到“開(kāi)關(guān)”一樣統一控制的作用。 


4. 避開(kāi)硬件的限制。


有時(shí)一些具體應用環(huán)境的硬件不同,但限于條件本地缺乏這種設備,可繞過(guò)硬件直接寫(xiě)出預期結果:


#ifndef TEST


     i = dial();  //程序調試運行時(shí)繞過(guò)此語(yǔ)句


 #else


     i = 0;


 #endif


調試通過(guò)后,再屏蔽TEST的定義并重新編譯即可。   


5. 防止頭文件重復包含


頭文件(.h)可以被頭文件或C文件包含。由于頭文件包含可以嵌套,C文件就有可能多次包含同一個(gè)頭文件;或者不同的C文件都包含同一個(gè)頭文件,編譯時(shí)就可能出現重復包含(重復定義)的問(wèn)題。


在頭文件中為了避免重復調用(如兩個(gè)頭文件互相包含對方),常采用這樣的結構:


 #ifndef  <標識符>


     #define  <標識符>


     //真正的內容,如函數聲明之類(lèi)


 #endif


<標識符>可以自由命名,但一般形如__HEADER_H,且每個(gè)頭文件標識都應該是唯一的。


事實(shí)上,不管頭文件會(huì )不會(huì )被多個(gè)文件引用,都要加上條件編譯開(kāi)關(guān)來(lái)避免重復包含。 


6. 在#ifndef中定義變量出現的問(wèn)題(一般不定義在#ifndef中)。


 

#ifndef PRECMPL


     #define PRECMPL


    int var;

    

 #endif


其中有個(gè)變量定義,在VC中鏈接時(shí)會(huì )出現變量var重復定義的錯誤,而在C中成功編譯。


(1) 當第一個(gè)使用這個(gè)頭文件的.cpp文件生成.obj時(shí),var在里面定義;當另一個(gè)使用該頭文件的.cpp文件再次(單獨)生成.obj時(shí),var又被定義;然后兩個(gè)obj被第三個(gè)包含該頭文件.cpp連接在一起,會(huì )出現重復定義。


(2) 把源程序文件擴展名改成.c后,VC按照C語(yǔ)言語(yǔ)法對源程序進(jìn)行編譯。在C語(yǔ)言中,遇到多個(gè)int var則自動(dòng)認為其中一個(gè)是定義,其他的是聲明。


(3) C語(yǔ)言和C++語(yǔ)言連接結果不同,可能是在進(jìn)行編譯時(shí),C++語(yǔ)言將全局變量默認為強符號,所以連接出錯。C語(yǔ)言則依照是否初始化進(jìn)行強弱的判斷的(僅供參考)。


解決方法:


(1) 把源程序文件擴展名改成.c。


 (2) .h中只聲明 extern int var;,在.cpp中定義(推薦)


//<x.h>


 #ifndef  __X_H


     #define  __X_H


     extern int var;


 #endif


 //<x.c>


 int var = 0;


綜上,變量一般不要定義在.h文件中。



五  小結


預處理功能是C語(yǔ)言特有的功能,它是在對源程序正式編譯前由預處理程序完成的。程序員在程序中用預處理命令來(lái)調用這些功能。


宏定義是用一個(gè)標識符來(lái)表示一個(gè)字符串,這個(gè)字符串可以是常量、變量或表達式。在宏調用中將用該字符串代換宏名。


宏定義可以帶有參數,宏調用時(shí)是以實(shí)參代換形參。而不是“值傳遞”。

為了避免宏替換時(shí)發(fā)生錯誤,宏定義中的字符串應加括號,字符串中出現的形式參數兩邊也應加括號。


文件包含是預處理的一個(gè)重要功能,它可用來(lái)把多個(gè)源文件連接成一個(gè)源文件進(jìn)行編譯,結果將生成一個(gè)目標文件。


條件編譯允許只編譯源程序中滿(mǎn)足條件的程序段,使生成的目標程序較短,從而減少了內存的開(kāi)銷(xiāo)并提高了程序的效率。


使用預處理功能便于程序的修改、閱讀、移植和調試,也便于實(shí)現模塊化程序設計。

 


六 附錄

6.1 Argument Prescan

(摘自http://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html)


Macro arguments are completely macro-expanded before they are substituted into a macro body, unless they are stringified or pasted with other tokens. After substitution, the entire macro body, including the substituted arguments, is scanned again for macros to be expanded. The result is that the arguments are scanned twice to expand macro calls in them.


宏參數被完全展開(kāi)后再替換入宏體,但當宏參數被字符串化(#)或與其它子串連接(##)時(shí)不予展開(kāi)。在替換之后,再次掃描整個(gè)宏體(包括已替換宏參數)以進(jìn)一步展開(kāi)宏。結果是宏參數被掃描兩次以展開(kāi)參數所(嵌套)調用的宏。


若帶參數宏定義中的參數稱(chēng)為形參,調用宏時(shí)的實(shí)際參數稱(chēng)為實(shí)參,則宏的展開(kāi)可用以下三步來(lái)簡(jiǎn)單描述(該步驟與gcc摘錄稍有不同,但更易操作):


1) 用實(shí)參替換形參,將實(shí)參代入宏文本中;


2) 若實(shí)參也是宏,則展開(kāi)實(shí)參;


3) 繼續處理宏替換后的宏文本,若宏文本也包含宏則繼續展開(kāi),否則完成展開(kāi)。


其中第一步將實(shí)參代入宏文本后,若實(shí)參前遇到字符“#”或“##”,即使實(shí)參是宏也不再展開(kāi)實(shí)參,而當作文本處理。


上述展開(kāi)步驟示例如下:


#define TO_STRING(x)    _TO_STRING(x)


#define _TO_STRING(x)   #x


#define FOO             4


則_TO_STRING(FOO)展開(kāi)為”FOO”;TO_STRING(FOO)展開(kāi)為_(kāi)TO_STRING(4),進(jìn)而展開(kāi)為”4”。相當于借助_TO_STRING這樣的中間宏,先展開(kāi)宏參數,延遲其字符化。


6.2 宏的其他注意事項


1. 避免在無(wú)作用域限定(未用{}括起)的宏內定義數組、結構、字符串等變量,否則函數中對宏的多次引用會(huì )導致實(shí)際局部變量空間成倍放大。


 2. 按照宏的功能、模塊進(jìn)行集中定義。即在一處將常量數值定義為宏,其他地方通過(guò)引用該宏,生成自己模塊的宏。嚴禁相同含義的常量數值,在不同地方定義為不同的宏,即使數值相同也不允許(維護修改后極易遺漏,造成代碼隱患)。


3. 用只讀變量適當替代(類(lèi)似功能的)宏,例如將#define PIE 3.14改為const float PIE = 3.14。


這樣做的好處如下:


1) 預編譯時(shí)用宏定義值替換宏名,編譯時(shí)報錯不易理解;


2) 跟蹤調試時(shí)顯示宏值,而不是宏名;


3) 宏沒(méi)有類(lèi)型,不能做類(lèi)型檢查,不安全;


4) 宏自身沒(méi)有作用域;


5) 只讀變量和宏的效率同樣高。


注意,C語(yǔ)言中只讀變量不可用于數組大小、變量(包括數組元素)初始化值以及case表達式。


4. 用inline函數代替(類(lèi)似功能的)宏函數。好處如下:


1) 宏函數在預編譯時(shí)處理,編譯出錯信息不易理解;


2) 宏函數本身無(wú)法單步跟蹤調試,因此也不要在宏內調用函數。但某些編譯器(為了調試需要)可將inline函數轉成普通函數;


3) 宏函數的入參沒(méi)有類(lèi)型,不安全;


5) inline函數會(huì )在目標代碼中展開(kāi),和宏的效率一樣高;


注意,某些宏函數用法獨特,不能用inline函數取代。當不想或不能指明參數類(lèi)型時(shí),宏函數更合適。


5. 不帶參數的宏函數也要定義成函數形式,如#define HELLO( )  printf(“Hello.”)。


括號會(huì )暗示閱讀代碼者該宏是一個(gè)函數。


6. 帶參宏內定義變量時(shí),應注意避免內外部變量重名的問(wèn)題:


 typedef struct{


     int d;


  }T_TEST;


  T_TEST gtTest = {0};


 #define ASSIGN1(_d) do{ \


     T_TEST t = {0}; \


      t.d = _d; \


      gtTest = t; \


  }while(0)


 #define ASSIGN2(_p) do{ \


     int _d; \


     _d = 5; \


     (_p) = _d; \


 }while(0)


若宏參數名或宏內變量名不加前綴下劃線(xiàn),則ASSIGN1(c)將會(huì )導致編譯報錯(t.d被替換為t.c),ASSIGN2(d)會(huì )因宏內作用域而導致外部的變量d值保持不變(而非改為5)。


7. 不要用宏改寫(xiě)語(yǔ)言。例如:


#define FOREVER   for ( ; ; )


 #define BEGIN     {


 #define END       }


C語(yǔ)言有完善且眾所周知的語(yǔ)法。試圖將其改變成類(lèi)似于其他語(yǔ)言的形式,會(huì )使讀者混淆,難于理解。


6.3 do{…}while(0)妙用


1. 函數中使用do{…}while(0)可替代goto語(yǔ)句。例如:


goto寫(xiě)法


替代寫(xiě)法


bOk = func1();


if(!bOk) goto errorhandle; 


bOk = func2();


if(!bOk) goto errorhandle; 


bOk = func3();


if(!bOk) goto errorhandle;


 


//… …


//執行成功,釋放資源并返回


delete p;   


p = NULL;


return true;


 


errorhandle:


delete p;   


p = NULL;


return false;


do{


      //執行并進(jìn)行錯誤處理


      bOk = func1();


      if(!bOk) break; 


      bOk = func2();


      if(!bOk) break; 


      bOk = func3();


      if(!bOk) break;


 


      // ..........


   }while(0);


 


    //釋放資源


    delete p;   


    p = NULL;


    return bOk;



2. 宏定義中使用do{…}while(0)的原因及好處:


1) 避免空的宏定義產(chǎn)生warning,如 #define DUMMY( ) do{}while(0)。


2) 存在一個(gè)獨立的代碼塊,可進(jìn)行變量定義,實(shí)現比較復雜的邏輯處理。


注意,該代碼塊內(即{…}內)定義的變量其作用域僅限于該塊。此外,為避免宏的實(shí)參與其內部定義的變量同名而造成覆蓋,最好在變量名前加上_(基于如下編程慣例:除非是庫,否則不應定義以_開(kāi)始的變量)。


3) 若宏出現在判斷語(yǔ)句之后,可保證作為一個(gè)整體來(lái)實(shí)現。


如#define SAFE_DELETE(p)  delete p; p = NULL;,則以下代碼


 if(NULL != p)


     SAFE_DELETE(p)


 else


     DUMMY( );


就有兩個(gè)問(wèn)題:


a) 因為if分支后有兩條語(yǔ)句,else分支沒(méi)有對應的if,編譯失??;


b) 假設沒(méi)有else,則SAFE_DELETE中第二條語(yǔ)句無(wú)論if判斷是否成立均會(huì )執行,這顯然違背程序設計的原始目的。


那么,為了避免這兩個(gè)問(wèn)題,將宏直接用{}括起來(lái)是否可以?如:


#define SAFE_DELETE(p)  {delete p; p = NULL;}


的確,上述問(wèn)題不復存在。但C/C++編程中,在每條語(yǔ)句后加分號是約定俗成的習慣,此時(shí)以下代碼


 if(NULL != p)


     SAFE_DELETE(p);


 else


     DUMMY( );


其else分支就無(wú)法通過(guò)編譯(多出一個(gè)分號),而采用do{…}while(0)則毫無(wú)問(wèn)題。


使用do{...} while(0)將宏包裹起來(lái),成為一個(gè)獨立的語(yǔ)法單元,從而不會(huì )與上下文發(fā)生混淆。同時(shí)因為絕大多數編譯器都能夠識別do{...}while(0)這種無(wú)用的循環(huán)并優(yōu)化,所以該法不會(huì )導致程序的性能降低。


6.4 類(lèi)型定義符typedef


C語(yǔ)言不僅提供了豐富的數據類(lèi)型,而且還允許由用戶(hù)自己定義類(lèi)型說(shuō)明符,也就是說(shuō)允許由用戶(hù)為數據類(lèi)型取“別名”。類(lèi)型定義符typedef即可用來(lái)完成此功能。


typedef定義的一般形式為:


           typedef 原類(lèi)型名  新類(lèi)型名


其中原類(lèi)型名中含有定義部分,新類(lèi)型名一般用大寫(xiě)表示,以便于區別。 


例如,有整型量int a,b。其中int是整型變量的類(lèi)型說(shuō)明符。int的完整寫(xiě)法為integer,為增加程序的可讀性,可把整型說(shuō)明符用typedef定義為typedef  int  INTEGER。此后就可用INTEGER來(lái)代替int作整型變量的類(lèi)型說(shuō)明,如INTEGER a,b等效于int a,b。


用typedef定義數組、指針、結構等類(lèi)型將帶來(lái)很大的方便,不僅使程序書(shū)寫(xiě)簡(jiǎn)單而且意義更為明確,因而增強了可讀性。


例如,typedef char NAME[20]表示NAME是字符數組類(lèi)型,數組長(cháng)度為20。然后可用NAME 說(shuō)明變量,如NAME a1,a2,s1,s2完全等效于:char a1[20],a2[20],s1[20],s2[20]。


又如:


 typedef struct{


     char name[20];


     int  age;


     char sex;


 }STU;


然后可用STU來(lái)定義結構變量:STU body1,body2;


有時(shí)也可用宏定義來(lái)代替typedef的功能,但是宏定義是由預處理完成的,而typedef則是在編譯時(shí)完成的,后者更為靈活方便。


此外,采用typedef重新定義一些類(lèi)型,可防止因平臺和編譯器不同而產(chǎn)生的類(lèi)型字節數差異,方便移植。如:



  typedef unsigned char boolean;       /* Boolean value type. */


  typedef unsigned long int uint32;    /* Unsigned 32 bit value */


  typedef unsigned short uint16;       /* Unsigned 16 bit value */


  typedef unsigned char uint8;         /* Unsigned 8 bit value */


  typedef signed long int int32;       /* Signed 32 bit value */


  typedef signed short int16;          /* Signed 16 bit value */


  typedef signed char int8;            /* Signed 8 bit value */




  //下面的不建議使用


 typedef unsigned char byte;          /* Unsigned 8 bit value type. */


 typedef unsigned short word;         /* Unsinged 16 bit value type. */


 typedef unsigned long dword;         /* Unsigned 32 bit value type. */


 typedef unsigned char uint1;         /* Unsigned 8 bit value type. */


 typedef unsigned short uint2;        /* Unsigned 16 bit value type. */


 typedef unsigned long uint4;         /* Unsigned 32 bit value type. */


 typedef signed char int1;            /* Signed 8 bit value type. */


 typedef signed short int2;           /* Signed 16 bit value type. */


 typedef long int int4;               /* Signed 32 bit value type. */


 typedef signed long sint31;          /* Signed 32 bit value */


 typedef signed short sint15;         /* Signed 16 bit value */


 typedef signed char sint7;           /* Signed 8 bit value */


圖片加載中...

在線(xiàn)留言

◎歡迎您的留言,您也可以通過(guò)以下方式聯(lián)系我們:

◎客戶(hù)服務(wù)熱線(xiàn):021-51095123

◎郵箱:xin021@126.com

展開(kāi)