(六)學習筆記 c語言基礎 宏定義和條件編譯

1.什麼是宏定義?

引用自百度百科

宏(Macro),是一種批量處理的稱謂。計算機科學裏的宏是一種抽象(Abstraction),它根據一系列預定義的規則替換一定的文本模式。解釋器或編譯器在遇到宏時會自動進行這一模式替換。對於編譯語言,宏展開在編譯時發生,進行宏展開的工具常被稱爲宏展開器。宏這一術語也常常被用於許多類似的環境中,它們是源自宏展開的概念,這包括鍵盤宏和宏語言。絕大多數情況下,“宏”這個詞的使用暗示着將小命令或動作轉化爲一系列指令。

宏定義:是指用一個宏名(名字)來代表一個字符串。宏定義的功能是在編譯預處理時,對程序中所有出現的“宏名”都用宏定義中的字符串去代換,這稱爲“宏代換”或“宏展開”。

2.宏應用

1).宏定義常量

#define PI 3.1415926
#define PATH "D:\project\src"

2).宏定義表達式

#define MAX(a,b) ((a)<(b)?(b):(a))

注意:宏定義僅是做簡單的文本替換,如果是表達式需要用括號括起來,否則可能會出現邏輯上的“錯誤”

如下會得到意想不到的結果:

#include<stdio.h>
#define SUM(x, y) x + y //正確寫法是:#define SUM(x, y) ((x) + (y))

int main()
{
    int a = 1, b = 2;
    int sub = SUM(a, b) * SUM(a, b);
    printf("sub = %d\n", sub);
    return 0;
}
宏定義換行表達式(交換兩個整數值)
#define SWAP(x, y) \
{                  \
    int temp = x;  \
    x = y;         \
    y = temp;      \
}
注意:宏定義行連接符是’\’

3.宏定義運用的好處與缺點

宏定義的好處:

1).提高代碼的可讀性,方便進行修改;
2).宏定義是簡單的文本替換,使用帶參的宏定義完成函數調用,可以提高程序的運行效率,減少系統開銷。
分析:宏是在預處理階段進行宏替換的,執行時不需要轉換,佔用編譯時間。並且宏定義不進行內存分配,由變量定義分配內存。(宏定義不存在類型問題,參數也是無類型的)
函數調用是在編譯後,程序運行時進行的,需要分配內存,值傳遞,返回值等一系列操作,佔用運行時間。
3).宏是由預處理器處理的,通過字符串操作可以完成很多編譯器無法實現的功能。比如##連接符。

宏定義缺點:

1).對帶參的宏而言,由於是直接替換,並不會檢查參數是否合法,存在安全隱患。
2).不方便調試問題。

4.什麼是條件編譯?

引用自百度百科

—般情況下,C語言源程序中的每一行代碼.都要參加編譯。但有時候出於對程序代碼優化的考慮.希望只對其中一部分內容進行編譯.此時就需要在程序中加上條件,讓編譯器只對滿足條件的代碼進行編譯,將不滿足條件的代碼捨棄,這就是條件編譯(conditional compile)。

5.條件編譯應用

三種語法格式:

1).if格式
#if 表達式
     條件語句1
#else
     條件語句2
#endif

當表達式的值爲真時,編譯條件語句1,否則編譯條件語句2。分支#else和條件語句2可以沒有。

2).ifdef格式
#ifdef 標識符
     條件語句1
#else
     條件語句2
#endif

當標識符已被定義時(用#define定義),編譯條件語句1,否則編譯條件語句2。分支#else和條件語句2可以沒有。

3).ifndef格式
#ifndef 標識符
     條件語句1
#else
     條件語句2
#endif

和ifdef格式相反(if not def)

6.#if(表達式)、#if defined(標識符) 和 #ifdef (標識符) 的區別

1).#if(表達式)後面跟的是表達式,條件爲真時,把條件語句編譯進去。
2).#if defined(標識符)後面跟的是宏定義,不管宏定義的標識符是真還是假,只要存在就把條件語句編譯進去。
3).#ifdef(標識符)和第二個用法一樣,但是隻能用於單個標識符,作爲判斷依據。
注意:如果是多個宏定義標識符作爲判斷依據,只能用defined()。

如:

#if (deined(A) || defined(B))
    條件語句1
#endif

7.綜合引用

1).防止一個頭文件被重複包含
#ifndef _HELLO_H
#define _HELLO_H 

#include<a.h>
#include<b.h>

int main();
#endif

當頭文件第一次被包含時,正常處理,符號_HELLO_H被定義爲1。如果頭文件被再次包含,通過條件編譯,它的內容被忽略。符號_HELLO_H按照被包含頭文件的文件名進行取名,以避免由於其他頭文件使用相同的符號而引起的衝突。

注意:預處理器仍將整個頭文件讀入,即使這個頭文件所有內容將被忽略。這種處理將託慢編譯速度,如果可能,應該避免出現多重包含。

#ifndef的是方式是受C/C++語言標準支持。#ifndef方式依賴於宏名稱要唯一。
優點:可以保證同一個文件不會被包含多次,也能保證內容完全相同的兩個文件不會被同時包含。
缺點:如果不同頭文件中的宏名不小心”碰撞”,可能就會導致你看到頭文件明明存在,編譯器卻硬說找不到聲明的狀況。由於編譯器每次都需要打開頭文件才能判定是否有重複定義,因此在編譯大型項目時,#ifndef會使得編譯時間相對較長。

另外一種處理方式#pragma once
#pragma once
#include<a.h>
#include<b.h>

int main();

#pragma once由編譯器提供保證:
同一個文件名不會被包含多次。無法對一個頭文件中的一段代碼作#pragma once聲明,只能針對文件。
優點:不會出現宏名一樣引發的奇怪問題,大型項目的編譯速度也因此得以提高。
缺點:如果某個頭文件有多份拷貝,此方法不能保證它們不被重複包含。在C/C++中,#pragma once是一個非標準但是被廣泛支持的方式。

注意:兼容性不好,較老編譯器不支持這種方式,如GCC 3.4版本之前不支持#pragma once
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章