1. 程式人生 > 實用技巧 >第九章 編譯預處理和位運算

第九章 編譯預處理和位運算

預處理命令是指C語言設定的像#include這樣的在源程式編譯之前必須進行處理的命令。C語言的預處理命令均以 # 打頭,末尾不加分號,以區別於C語句。

9.1 巨集定義

  巨集定義就是用一串字元代替名字,這串字元既可以是常數也可以是任何字串,甚至是可以帶引數的巨集。例如:

  #define PI 3.14159   PI稱作巨集名,3.14159稱作巨集體。

同一原始檔,巨集名不可以相同。多個原始檔巨集名可以重名。巨集的作用域就是所在的原始檔。

9.1.1 符號常量巨集定義

  符號常量巨集定義,其巨集體為常量表達式。預處理時,將把程式中該巨集體定義之後的所有巨集名用巨集體替換。這是一種簡單的字元替換,不進行任何計算。

  使用巨集替換的好處:

  1)提高了程式的可讀性。

  2)易修改性。

9.1.2 帶引數的巨集定義

  #define 巨集名(形參表) 字串

  對於帶引數的巨集定義,在編譯預處理時,不僅要進行字串替換而且還要進行引數替換。巨集體不僅可以是字串常數,也可以是表示式或語句組成的字串。

注意:

  1)巨集名與巨集體之間應以空格間隔,所以巨集名中不能含有空格。

  2)巨集名不能用引號括起來。否則將不進行巨集替換。

  3)較長的定義在一行中寫不下時,可以在本行末尾使用反斜槓 \ 表示要續行。

  4)對帶引數巨集定義,巨集體及其各個形參應該用圓括號括起來。

  5)巨集定義可以寫在源程式中的任何地方,但一定要寫在程式中引用該巨集之前。通常寫在一個檔案之首。

巨集與函式的區別:

  1)時空效率不同。巨集替換時要巨集體去替換巨集名,往往使程式體積膨脹,加大系統的儲存開銷。但是它不像函式呼叫要進行引數傳遞、儲存現場、返回等操作,所以時間效率比函式高。所以,通常對簡短的表示式,以及呼叫頻繁、要求快速響應的場合,採用巨集比採用函式合適。

  2)巨集雖可以帶引數,但巨集替換過程中不像函式那樣要進行引數值的計算、傳遞及結果返回等操作;巨集替換隻是簡單的字元替換不進行計算。因此,一些過程中是不能用巨集代替函式的,如遞迴呼叫等。

9.1.3 巨集定義解除

  巨集定義具有全域性作用域。為了限制巨集的使用範圍,可以使用編譯預處理命令 #undef來解除已有的巨集定義,其一般格式:

  #undef 識別符號

其中,識別符號是在此之前使用#define定義過的符號常量或帶引數的巨集,在此命令之後,該巨集定義被解除。

9.2檔案包含

  檔案包含是通過命令#include 把已經進入系統的另一個檔案的整個內容嵌入進來。實際是巨集替換的延伸。

  檔案包含有兩種格式:

  格式1:#include "檔案標識"

  檔案標識中包含有檔案路徑。按這種格式定義時,預處理程式首先在原來的原始檔目錄中檢索該指定的檔案;如果沒有找到,則按系統指定的標準方式檢索其它檔案目錄,直到找到為止。

  格式2:#include<檔名>

  按這種格式定義時,預處理程式只按系統規定的標準方式檢索檔案目錄

對自己定義的非標準檔案使用格式1,而對系統提供的標準檔案常使用格式2。

9.3 條件編譯

  條件編譯是在編譯檔案之前,根據給定的條件決定編譯的範圍。滿足一定條件時,編譯其中的一部分語句,在不滿足條件時編譯另一部分語句,這就是所謂的 “條件編譯”。

9.3.1 #if…#else…#endif指令

  # 表示式

    程式段 1

  #else

    程式段 2

  #endif

  它的功能是當指定的表示式值為真時,編譯程式段1,否則編譯程式段2,其中的程式段可以是C語言中合法的語句或命令列。#else部分可以省略,但#if與#endif一定要配對使用。

9.3.2 #ifdef…#else…#endif指令

  #ifdef 識別符號

    程式段1

  #else

    程式段2

  #endif

  其作用為:若識別符號已被定義(一般指用#define 定義)則編譯程式段1,否則編譯程式段2。

9.3.3 #ifndef…#else…#endif指令

  #ifndef 識別符號

    程式段1

  #else

    程式段2

  #endif

  其作用為:若識別符號未被定義,則編譯程式段1,否則編譯程式段2,與前一種命令形式恰好相反。

  C語言規定,條件編譯中#if後面的條件必須是常量表達式,即表示式中參與運算的量必須是常量,在大多數情況下使用由#define定義的符號常量。

9.3.4 條件編譯的作用

  1.便於程式的除錯

  2.增強程式的可移植性

  3.提高程式的效率

9.5 位運算

  所謂位運算是指,按二進位制位進行的運算。

9.5.1 按位運算子

  1.按位與 --- &

    參與運算的兩個資料,按二進位制位進行“與”運算。如果兩個相應的二進位制位都為1,則該位的結果值為1,否則為0。

  2.按位或 --- |

    兩個對應的二進位制位中要有一個為1,該位的結果值為1。對應位均為0時才為0,否則為1。

  3.按位異或 --- ^

    兩個對應的二進位制位相同時,則結果為0(假),不同時為1(真)。

異或的意思是判斷兩個對應的位值是否為“異”,為“異”(值不同)就取真(1),否則為假(0)

交換兩個值不用臨時變數:

    a = a ^ b;

    b = b ^ a;

    a = a ^ b; 不會越界

  4.按位取反 --- ~

  ~是一個單目運算子,用來對一個二進位制位各位翻轉。即按位取反,原來為1的位變變成0,原來為0的位變成1。

9.5.2 移位運算子

  1.按位左移 --- <<

  用來將一個數的各二進位制位全部左移若干位,低位補0,高位左移後溢位,捨棄不起作用。左移,右邊都是填充0。左移一位相當於該數乘以2。但此結論只適用於該數左移時被溢位捨棄的高位中不包含1的情況。

  2.按位右移 --- >>

  右移,如果是無符號資料,左邊高位填充0。如果有符號資料,正數,按照符號位0左邊高位填充0.負數按照符號位1左邊高位填充1。

右移一位相當於該數除以2,右移n位相當於除以2n。

從鍵盤上輸入1個正整數賦值給int變數num,按二進位制位輸出該數。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void main()
{
    int num, mask, i;
    scanf("%d", &num);
    mask = 1 << 15; //構造一個最高位為1,其餘各位為0的遮蔽字
    for (i = 1; i <= 16; i++)
    {
        putchar(num&mask ? '1' : '0');   //輸出最高位的值1或0
        num <<= 1;                        //將次高位移到最高位
        if (i % 4 == 0) putchar(' ');  //四位一組用空格分開
    }
    system("pause");
}