預處理命令與位運算

  所謂預處理是指在進行編譯的第一遍(詞法掃描和語法分析)之前所做的工作。預處理是C語言的一個重要功能,它由預處理程序負責完成。當對一個原文件進行編譯時,系統將自動引用預處理程序對源程序中的預處理部分進行處理,處理完畢自動進入對源程序的編譯。

  第一部分 宏定義

  在C語言源程序中允許用一個標識符來表示一個字符串,成爲“宏”。被定義爲“宏”的標識符成爲“宏名”。在編譯處理命令時,對程序中所出現的“宏名”,都是宏定義中的字符串去代替,這稱爲“宏代換”或“宏展開”。

  宏定義是由源程序中的宏定義命令完成的。宏代換是由預處理程序自動完成的。

  在C語言中,“宏”分爲有參數和無參數兩種。

  1.1  無參宏定義

   無參宏的宏名不帶參數。其定義的一般形式爲:

   #define 標識符 字符串

  其中“#”表示這是一條預處理命令。凡是以“#”開頭的均爲預處理命令。“define“爲宏定義命令。“標識符”爲所定義的宏名。“字符串”可以是常量、表達式或格式串等。

  在前面介紹過的符號常量的定義就是一種無參宏定義。此外,對程序中反覆使用的表達式也可進行宏定義。如:#define M (y*y+3*y)

它的作用是指定標識符M來代替表達式(y*y+3*y)。在編寫源程序時,所有的(y*y+3*y)都可由M代替,而對源程序進行編譯時,將先由預處理程序進行宏代換,即用(y*y+3*y)表達式去置換所有的宏名M,然後再進行編譯。

例1:紅的定義和替換

#include<stdio.h>
#define M (y*y+3*y)
main(){
 int s,y;
 printf("input a number:/n");
 scanf("%d",&y);
 s=3*M+4*M+5*M;
 printf("s=%d/n",s);
}

  首先定義了宏,定義M來替換表達式(y*y+3*y),在s=3*M+4*M+5*M;中進行宏調用。在預處理時經宏展開後該語句變爲:s=3*
(y*y+3*y)+4*(y*y+3*y)+5*(y*y+3*y);但是要注意在宏定義中表達式(y*y+3*y)兩邊的括號不能少。否則會產生錯誤。

  對於宏定義有幾點說明:

(1)宏定義是用宏名來表示一個字符串,在宏展開時又以該字符串取代宏名,這只是一種簡單的代換,字符串中可以包含任何字符字符,可以是常量,也可以是表達式,預處理程序對他不做任何檢查。如有錯誤,只能在編譯已被展開後的源程序時發現。

(2)宏定義不是類型聲明或語句,在行末不必加分號,如加分號則連分號也一起置換。

(3)宏定義必須寫在函數之外,其作用於從宏定義命令起到源程序結束。如要終止起作用域也是用#undef命令。

(4)宏名在源程序中若用引號括起來,則預處理程序不對其進行宏代換。

(5)宏定義允許嵌套,在宏定義的字符串中可以使用已經定義的宏名。在宏展開時由預處理程序層層代換。

(6)習慣上宏名用大寫字符表示,以便與變量區分。但也允許用小寫字母。

(7)可用宏定義表示數據類型,使書寫方便。

如:

   #define STU struct stu

 在程序中可用STU做變量說明:STU body[5],*p;

  #define INTEGER int

 在程序中就可用INTEGER進行整形變量說明:INTEGER a,b;

 應注意用宏定義表示數據類型和typedef定義數據說明符的區別。宏定義只是簡單的字符串替換,是在預處理階段完成的,而typedef是在編譯階段進行處理的,他不是做簡單的代換,而是對類型說明符重新命名。被命名的標識符具有類型定義說明的功能。

  請看下面的例子:

   #define PIN1 int *

   typedef (int *)PIN2;

 從形式上看這兩者相似,但在實際使用中卻不相同。下面用PIN1,PIN2聲明變量就可以看出他們的區別:

 PIN1 a,b;在宏替換後變成: int * a,b;表示a是指向整形的指針變量,而b是整型變量。然而:PIN2 a,b;表示a,b都是指向整形的指針變量。因爲PIN2是一個類型說明符。由此例可知,宏定義雖然也可表示數據類型,但畢竟是做字符替換。在使用時要格外小心,避免出錯。

例3:對“輸入格式”做宏定義

#include<stdio.h>
#define P printf
#define D "%d,"
#define F "%f/n"
main(){
 int a=5,c=8,e=11;
 float b=3.8,d=8.7,f=21.08;
 P(D F,a,b);
 P(D F,c,d);
 P(D F,e,f);
}

1.2 帶參宏定義
 C語言允許宏帶有參數。在宏定義中的參數成爲形式參數,在宏調用中的參數成爲實際參數。對於帶參數的宏,在調用中,不僅要進行宏展開,而且要用實參去替換形參。

 帶參宏定義的一般形式爲:#define 宏名(形參表) 字符串

 在字符串中含有各個形參。帶參宏調用的一般格式爲:宏名(實參表);

如:#define M(y) y*y+3*y ... k=M(5); ...

例4:帶參數的宏

#include<stdio.h>
#define MAX(a,b) (a>b)?a:b

main(){
 int x,y,max;
 printf("input two numbers:/n");
 scanf("%d%d",&x,&y);
 max=MAX(x,y);
 printf("max=%d/n",max);
}
 對帶參的宏定義有以下幾點說明:

(1)帶參宏定義中,宏名和形參表之間不能有空格。

(2)在帶參宏定義中,形式參數不分配內存空間,因此不必做類型定義。而宏調用中的實參有具體的值。要用它們去替換形參,因此必須做類型說明。這是與函數中的情況不同的。在函數中,形參和實參是兩個不同的量,各有自己的作用域,調用時要把實參值賦值予形參,進行“值傳遞”。而在帶參宏中,只進行符號替換,不存在值傳遞的問題。

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

(4)宏定義中,字符串內的形參通常把實參表達式的值用括號括起來以避免出錯。

(5)帶參的宏和帶參的函數很相似,但本質上是不同的,除上面已談到的幾點外,把同一表達式用函數處理與用宏處理後,兩者的結果可能是不同的。

例5:帶參的函數

#include<stdio.h>
main(){
 int i=1;
 while(i<5)
  printf("%d  ",SQ(i++));
}
SQ(int y){
 return (y*y);
}

例6:帶參的宏

#include<stdio.h>
#define SQ(y) (y*y)
main(){
 int i=1;
 while(i<=5)
  printf("%d  ",SQ(i++));
}

發佈了48 篇原創文章 · 獲贊 11 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章