C語(yǔ)言中的內(nèi)聯(lián)函數(shù)(inline)與宏定義(#define)詳細(xì)解析
更新時(shí)間:2013年09月22日 09:18:57 作者:
內(nèi)聯(lián)函數(shù)與宏本質(zhì)上是兩個(gè)不同的概念如果程序編寫者對(duì)于既要求快速,又要求可讀的情況下,則應(yīng)該將函數(shù)冠以inline
先簡(jiǎn)明扼要,說(shuō)下關(guān)鍵:
1、內(nèi)聯(lián)函數(shù)在可讀性方面與函數(shù)是相同的,而在編譯時(shí)是將函數(shù)直接嵌入調(diào)用程序的主體,省去了調(diào)用/返回指令,這樣在運(yùn)行時(shí)速度更快。
2、內(nèi)聯(lián)函數(shù)可以調(diào)試,而宏定義是不可以調(diào)試的。
內(nèi)聯(lián)函數(shù)與宏本質(zhì)上是兩個(gè)不同的概念如果程序編寫者對(duì)于既要求快速,又要求可讀的情況下,則應(yīng)該將函數(shù)冠以inline。下面詳細(xì)介紹一下探討一下內(nèi)聯(lián)函數(shù)與宏定義。
一、內(nèi)聯(lián)函數(shù)是什么?
內(nèi)聯(lián)函數(shù)是代碼被插入到調(diào)用者代碼處的函數(shù)。如同 #define 宏(但并不等同,原因見(jiàn)下文),內(nèi)聯(lián)函數(shù)通過(guò)避免被調(diào)用的開(kāi)銷來(lái)提高執(zhí)行效率,尤其是它能夠通過(guò)調(diào)用(“過(guò)程化集成”)被編譯器優(yōu)化。
二、 內(nèi)聯(lián)函數(shù)是如何在安全和速度上取得折衷?
在 C 中,你可以通過(guò)在結(jié)構(gòu)中設(shè)置一個(gè) void* 來(lái)得到“封裝的結(jié)構(gòu)”,在這種情況下,指向?qū)嶋H數(shù)據(jù)的 void* 指針對(duì)于結(jié)構(gòu)的用戶來(lái)說(shuō)是未知的。因此結(jié)構(gòu)的用戶不知道如何解釋void*指針?biāo)竷?nèi)容,但是存取函數(shù)可以將 void* 轉(zhuǎn)換成適當(dāng)?shù)碾[含類型。這樣給出了封裝的一種形式。
不幸的是這樣做喪失了類型安全,并且也將繁瑣的對(duì)結(jié)構(gòu)中的每個(gè)域的訪問(wèn)強(qiáng)加于函數(shù)調(diào)用。(如果你允許直接存取結(jié)構(gòu)的域,那么對(duì)任何能直接存取的人來(lái)說(shuō),了解如何解釋 void* 指針?biāo)竷?nèi)容就是必要的了;這樣將使改變底層數(shù)據(jù)結(jié)構(gòu)變的困難)。
雖然函數(shù)調(diào)用開(kāi)銷是很小的,但它會(huì)被累積。C++類允許函數(shù)調(diào)用以內(nèi)聯(lián)展開(kāi)。這樣讓你在得到封裝的安全性時(shí),同時(shí)得到直接存取的速度。此外,內(nèi)聯(lián)函數(shù)的參數(shù)類型由編譯器檢查,這是對(duì) C 的 #define 宏的一個(gè)改進(jìn)。
三、為什么我應(yīng)該用內(nèi)聯(lián)函數(shù)?而不是原來(lái)清晰的 #define 宏?
因?yàn)?define宏定義函數(shù)是在四處是有害的:
和 #define 宏不同的是,內(nèi)聯(lián)函數(shù)總是對(duì)參數(shù)只精確地進(jìn)行一次求值,從而避免了那聲名狼藉的宏錯(cuò)誤。換句話說(shuō),調(diào)用內(nèi)聯(lián)函數(shù)和調(diào)用正規(guī)函數(shù)是等價(jià)的,差別僅僅是更快:
// 返回 i 的絕對(duì)值的宏
#define unsafe(i) \
( (i) >= 0 ? (i) : -(i) )
// 返回 i 的絕對(duì)值的內(nèi)聯(lián)函數(shù)
inline
int safe(int i)
{
return i >= 0 ? i : -i;
}
int f();
void userCode(int x)
{
int ans;
ans = unsafe(x++); // 錯(cuò)誤!x 被增加兩次
ans = unsafe(f()); // 危險(xiǎn)!f()被調(diào)用兩次
ans = safe(x++); // 正確! x 被增加一次
ans = safe(f()); // 正確! f() 被調(diào)用一次
}
和宏不同的,還有內(nèi)聯(lián)函數(shù)的參數(shù)類型被檢查,并且被正確地進(jìn)行必要的轉(zhuǎn)換。宏定義復(fù)雜函數(shù)是有害的;非萬(wàn)不得已不要用。
四、如何告訴編譯器使非成員函數(shù)成為內(nèi)聯(lián)函數(shù)?
聲明內(nèi)聯(lián)函數(shù)看上去和普通函數(shù)非常相似:
void f(int i, char c);
當(dāng)你定義一個(gè)內(nèi)聯(lián)函數(shù)時(shí),在函數(shù)定義前加上 inline 關(guān)鍵字,并且將定義放入頭文件:inlinevoid f(int i, char c){ // ...}
注意:將函數(shù)的定義({...}之間的部分)放在頭文件中是強(qiáng)制的,除非該函數(shù)僅僅被單個(gè) .cpp 文件使用。尤其是,如果你將內(nèi)聯(lián)函數(shù)的定義放在 .cpp 文件中并且在其他 .cpp文件中調(diào)用它,連接器將給出 “unresolved external” 錯(cuò)誤。
五、如何告訴編譯器使一個(gè)成員函數(shù)成為內(nèi)聯(lián)函數(shù)?
聲明內(nèi)聯(lián)成員函數(shù)看上去和普通函數(shù)非常類似:
class Fred {public:
void f(int i, char c);};
但是當(dāng)你定義內(nèi)聯(lián)成員函數(shù)時(shí),在成員函數(shù)定義前加上 inline 關(guān)鍵字,并且將定義放入頭文件中:inlinevoid Fred::f(int i, char c){ // ...}通常將函數(shù)的定義({...}之間的部分)放在頭文件中是強(qiáng)制的。如果你將內(nèi)聯(lián)函數(shù)的定義放在 .cpp 文件中并且在其他 .cpp 文件中調(diào)用它,連接器將給出“unresolved external”錯(cuò)誤。
六、 有其它方法告訴編譯器使成員函數(shù)成為內(nèi)聯(lián)嗎?
有:在類體內(nèi)定義成員函數(shù):class Fred {public: void f(int i, char c) { // ... }};盡管這對(duì)于寫類的人來(lái)說(shuō)很容易,但由于它將類是“什么”(what)和類“如何”(how)工作混在一起.小結(jié)總之,在嵌入式C(或C++)編程里面,懂得使用內(nèi)聯(lián)函數(shù)(inline)與宏定義(#define),并使用好它們,對(duì)我們是大有裨益的。(注:本文部分內(nèi)容來(lái)源于網(wǎng)絡(luò)整理,上述探討屬于個(gè)人意見(jiàn),僅供參考。錯(cuò)誤之處也是難免?。?
1、內(nèi)聯(lián)函數(shù)在可讀性方面與函數(shù)是相同的,而在編譯時(shí)是將函數(shù)直接嵌入調(diào)用程序的主體,省去了調(diào)用/返回指令,這樣在運(yùn)行時(shí)速度更快。
2、內(nèi)聯(lián)函數(shù)可以調(diào)試,而宏定義是不可以調(diào)試的。
內(nèi)聯(lián)函數(shù)與宏本質(zhì)上是兩個(gè)不同的概念如果程序編寫者對(duì)于既要求快速,又要求可讀的情況下,則應(yīng)該將函數(shù)冠以inline。下面詳細(xì)介紹一下探討一下內(nèi)聯(lián)函數(shù)與宏定義。
一、內(nèi)聯(lián)函數(shù)是什么?
內(nèi)聯(lián)函數(shù)是代碼被插入到調(diào)用者代碼處的函數(shù)。如同 #define 宏(但并不等同,原因見(jiàn)下文),內(nèi)聯(lián)函數(shù)通過(guò)避免被調(diào)用的開(kāi)銷來(lái)提高執(zhí)行效率,尤其是它能夠通過(guò)調(diào)用(“過(guò)程化集成”)被編譯器優(yōu)化。
二、 內(nèi)聯(lián)函數(shù)是如何在安全和速度上取得折衷?
在 C 中,你可以通過(guò)在結(jié)構(gòu)中設(shè)置一個(gè) void* 來(lái)得到“封裝的結(jié)構(gòu)”,在這種情況下,指向?qū)嶋H數(shù)據(jù)的 void* 指針對(duì)于結(jié)構(gòu)的用戶來(lái)說(shuō)是未知的。因此結(jié)構(gòu)的用戶不知道如何解釋void*指針?biāo)竷?nèi)容,但是存取函數(shù)可以將 void* 轉(zhuǎn)換成適當(dāng)?shù)碾[含類型。這樣給出了封裝的一種形式。
不幸的是這樣做喪失了類型安全,并且也將繁瑣的對(duì)結(jié)構(gòu)中的每個(gè)域的訪問(wèn)強(qiáng)加于函數(shù)調(diào)用。(如果你允許直接存取結(jié)構(gòu)的域,那么對(duì)任何能直接存取的人來(lái)說(shuō),了解如何解釋 void* 指針?biāo)竷?nèi)容就是必要的了;這樣將使改變底層數(shù)據(jù)結(jié)構(gòu)變的困難)。
雖然函數(shù)調(diào)用開(kāi)銷是很小的,但它會(huì)被累積。C++類允許函數(shù)調(diào)用以內(nèi)聯(lián)展開(kāi)。這樣讓你在得到封裝的安全性時(shí),同時(shí)得到直接存取的速度。此外,內(nèi)聯(lián)函數(shù)的參數(shù)類型由編譯器檢查,這是對(duì) C 的 #define 宏的一個(gè)改進(jìn)。
三、為什么我應(yīng)該用內(nèi)聯(lián)函數(shù)?而不是原來(lái)清晰的 #define 宏?
因?yàn)?define宏定義函數(shù)是在四處是有害的:
和 #define 宏不同的是,內(nèi)聯(lián)函數(shù)總是對(duì)參數(shù)只精確地進(jìn)行一次求值,從而避免了那聲名狼藉的宏錯(cuò)誤。換句話說(shuō),調(diào)用內(nèi)聯(lián)函數(shù)和調(diào)用正規(guī)函數(shù)是等價(jià)的,差別僅僅是更快:
復(fù)制代碼 代碼如下:
// 返回 i 的絕對(duì)值的宏
#define unsafe(i) \
( (i) >= 0 ? (i) : -(i) )
// 返回 i 的絕對(duì)值的內(nèi)聯(lián)函數(shù)
inline
int safe(int i)
{
return i >= 0 ? i : -i;
}
int f();
void userCode(int x)
{
int ans;
ans = unsafe(x++); // 錯(cuò)誤!x 被增加兩次
ans = unsafe(f()); // 危險(xiǎn)!f()被調(diào)用兩次
ans = safe(x++); // 正確! x 被增加一次
ans = safe(f()); // 正確! f() 被調(diào)用一次
}
和宏不同的,還有內(nèi)聯(lián)函數(shù)的參數(shù)類型被檢查,并且被正確地進(jìn)行必要的轉(zhuǎn)換。宏定義復(fù)雜函數(shù)是有害的;非萬(wàn)不得已不要用。
四、如何告訴編譯器使非成員函數(shù)成為內(nèi)聯(lián)函數(shù)?
聲明內(nèi)聯(lián)函數(shù)看上去和普通函數(shù)非常相似:
void f(int i, char c);
當(dāng)你定義一個(gè)內(nèi)聯(lián)函數(shù)時(shí),在函數(shù)定義前加上 inline 關(guān)鍵字,并且將定義放入頭文件:inlinevoid f(int i, char c){ // ...}
注意:將函數(shù)的定義({...}之間的部分)放在頭文件中是強(qiáng)制的,除非該函數(shù)僅僅被單個(gè) .cpp 文件使用。尤其是,如果你將內(nèi)聯(lián)函數(shù)的定義放在 .cpp 文件中并且在其他 .cpp文件中調(diào)用它,連接器將給出 “unresolved external” 錯(cuò)誤。
五、如何告訴編譯器使一個(gè)成員函數(shù)成為內(nèi)聯(lián)函數(shù)?
聲明內(nèi)聯(lián)成員函數(shù)看上去和普通函數(shù)非常類似:
class Fred {public:
void f(int i, char c);};
但是當(dāng)你定義內(nèi)聯(lián)成員函數(shù)時(shí),在成員函數(shù)定義前加上 inline 關(guān)鍵字,并且將定義放入頭文件中:inlinevoid Fred::f(int i, char c){ // ...}通常將函數(shù)的定義({...}之間的部分)放在頭文件中是強(qiáng)制的。如果你將內(nèi)聯(lián)函數(shù)的定義放在 .cpp 文件中并且在其他 .cpp 文件中調(diào)用它,連接器將給出“unresolved external”錯(cuò)誤。
六、 有其它方法告訴編譯器使成員函數(shù)成為內(nèi)聯(lián)嗎?
有:在類體內(nèi)定義成員函數(shù):class Fred {public: void f(int i, char c) { // ... }};盡管這對(duì)于寫類的人來(lái)說(shuō)很容易,但由于它將類是“什么”(what)和類“如何”(how)工作混在一起.小結(jié)總之,在嵌入式C(或C++)編程里面,懂得使用內(nèi)聯(lián)函數(shù)(inline)與宏定義(#define),并使用好它們,對(duì)我們是大有裨益的。(注:本文部分內(nèi)容來(lái)源于網(wǎng)絡(luò)整理,上述探討屬于個(gè)人意見(jiàn),僅供參考。錯(cuò)誤之處也是難免?。?
相關(guān)文章
C++實(shí)現(xiàn)當(dāng)前時(shí)間動(dòng)態(tài)顯示的方法
這篇文章主要介紹了C++實(shí)現(xiàn)當(dāng)前時(shí)間動(dòng)態(tài)顯示的方法,涉及C++時(shí)間操作及Sleep方法的使用,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07C++ 學(xué)習(xí)之旅 Windows程序內(nèi)部運(yùn)行原理
學(xué)習(xí)C++與.net不同的是,一定要搞清楚Windows程序內(nèi)部運(yùn)行原理,因?yàn)樗婕按蠖鄶?shù)是操作系統(tǒng)的調(diào)用,而.net畢竟是在.netFrameWork上唱戲2012-11-11Qt 使用 canon edsdk 實(shí)現(xiàn)實(shí)時(shí)預(yù)覽的示例代碼
這篇文章主要介紹了Qt 使用 canon edsdk 實(shí)現(xiàn)實(shí)時(shí)預(yù)覽的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11深入淺出理解C語(yǔ)言初識(shí)結(jié)構(gòu)體
C?數(shù)組允許定義可存儲(chǔ)相同類型數(shù)據(jù)項(xiàng)的變量,結(jié)構(gòu)是?C?編程中另一種用戶自定義的可用的數(shù)據(jù)類型,它允許你存儲(chǔ)不同類型的數(shù)據(jù)項(xiàng),本篇讓我們來(lái)了解C?的結(jié)構(gòu)體2022-02-02C語(yǔ)言中組成不重復(fù)的三位數(shù)問(wèn)題
這篇文章主要介紹了C語(yǔ)言中組成不重復(fù)的三位數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11C++中對(duì)象的常引用、動(dòng)態(tài)建立和釋放相關(guān)知識(shí)講解
這篇文章主要介紹了C++中對(duì)象的常引用、動(dòng)態(tài)建立和釋放相關(guān)知識(shí)講解,是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09C語(yǔ)言基于EasyX庫(kù)實(shí)現(xiàn)有圖形界面時(shí)鐘
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言基于EasyX庫(kù)實(shí)現(xiàn)有圖形界面時(shí)鐘,獲得本地時(shí)間,輸出文字,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03