函數(shù)是C++語(yǔ)言的基本功能單位,一般來(lái)說(shuō),函數(shù)的功能單一,函數(shù)代碼不可過(guò)長(zhǎng)。函數(shù)接口設(shè)計(jì)清晰明了,返回值和錯(cuò)誤代碼的返回分開(kāi)。除了普通的函數(shù)之外,還有內(nèi)聯(lián)函數(shù)、回調(diào)函數(shù)等。在不同語(yǔ)言之間的調(diào)用函數(shù),提供了不同的調(diào)用規(guī)范。所謂預(yù)處理是指在進(jìn)行編譯的第一遍掃描(詞法掃描和語(yǔ)法分析)之前所做的工作。預(yù)處理是C++語(yǔ)言的一個(gè)重要功能,它由預(yù)處理程序負(fù)責(zé)完成。當(dāng)對(duì)一個(gè)源文件進(jìn)行編譯時(shí),系統(tǒng)將自動(dòng)引用預(yù)處理程序?qū)υ闯绦蛑械念A(yù)處理部分進(jìn)行處理,處理完畢自動(dòng)進(jìn)入對(duì)源程序的編譯。C++語(yǔ)言提供了多種預(yù)處理功能,如宏定義、文件包含、條件編譯等。合理地使用預(yù)處理功能編寫(xiě)的程序便于閱讀、修改、移植和調(diào)試,也有利于模塊化程序設(shè)計(jì)。
1、 在C++中調(diào)用C編譯器的函數(shù)
1.1 C++中調(diào)用C編譯器的函數(shù)的原理
C++語(yǔ)言,曾經(jīng)有一叫法是帶類的C語(yǔ)言,那也就是說(shuō)它的前身是C語(yǔ)言,所以說(shuō),用C寫(xiě)的函數(shù),基本上是可以在C++中調(diào)用的。比如說(shuō),C語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的函數(shù),在C++中是可以調(diào)用的。但是 C++畢竟跟C不同,那怎樣才能順利地把C函數(shù)應(yīng)用到C++中呢?下面來(lái)討論這個(gè)問(wèn)題。
C++語(yǔ)言支持函數(shù)重載,C語(yǔ)言不支持函數(shù)重載。函數(shù)被C++編譯后在庫(kù)中的名字與C語(yǔ)言的不同。
1.2 實(shí)例代碼
假設(shè)某個(gè)C函數(shù)的聲明如下:
void foo(int x, int y);
該函數(shù)被C編譯器編譯后在庫(kù)中的名字為_foo,而C++編譯器則會(huì)產(chǎn)生像_foo_int_int之類的名字用來(lái)支持函數(shù)重載和類型安全連接。由于編譯后的名字不同,C++程序不能直接調(diào)用C函數(shù)。C++提供了一個(gè)C連接交換指定符號(hào)extern”C”來(lái)解決這個(gè)問(wèn)題。例如
extern “C”


{
void foo(int x, int y);
//其他函數(shù)
}
或者寫(xiě)成
extern “C”


{
#include “myheader.h”
//其他C頭文件
}
這就告訴C++編譯器,函數(shù)foo是個(gè)C連接,應(yīng)該到庫(kù)中找名字_foo而不是找_foo_int_int。C++編譯器開(kāi)發(fā)商已經(jīng)對(duì)C標(biāo)準(zhǔn)庫(kù)的頭文件做了extern”C”處理,所以可以用#include直接引用這些頭文件。
如果調(diào)用自己寫(xiě)的C函數(shù),可以把C函數(shù)集合成一個(gè)庫(kù),然后把其相應(yīng)的頭文件用extern”C”處理,用#include直接引用這些頭文件即可。
2、 內(nèi)聯(lián)函數(shù)和宏的比較
2.1 內(nèi)聯(lián)函數(shù)的概念
在C++中,可以將函數(shù)指定為內(nèi)聯(lián)(inline)的,這樣,編譯器會(huì)在調(diào)用該函數(shù)的每個(gè)地方產(chǎn)生該函數(shù)的一個(gè)副本。使用內(nèi)聯(lián)函數(shù)可以減少函數(shù)調(diào)用帶來(lái)的開(kāi)銷(xiāo),不過(guò)應(yīng)該在函數(shù)很簡(jiǎn)單而且在調(diào)用次數(shù)相對(duì)較少的情況下,才將其指定為內(nèi)聯(lián)的。
內(nèi)聯(lián)函數(shù)的基本思想在于將每個(gè)函數(shù)調(diào)用以它的代碼體來(lái)替換。用不著統(tǒng)計(jì)專家出面就可以看出,這種做法很可能會(huì)增加整個(gè)目標(biāo)代碼的體積。在一臺(tái)內(nèi)存有限的計(jì)算機(jī)里,過(guò)分地使用內(nèi)聯(lián)所產(chǎn)生的程序會(huì)因?yàn)橛刑蟮捏w積而導(dǎo)致可用空間不夠。即使可以使用虛擬內(nèi)存,內(nèi)聯(lián)造成的代碼膨脹也可能會(huì)導(dǎo)致不合理的頁(yè)面調(diào)度行為(系統(tǒng)顛簸),這將使你的程序運(yùn)行慢得象在爬。(當(dāng)然,它也為磁盤(pán)控制器提供了一個(gè)極好的鍛煉方式:))
過(guò)多的內(nèi)聯(lián)還會(huì)降低指令高速緩存的命中率,從而使取指令的速度降低,因?yàn)閺闹鞔嫒≈噶町?dāng)然比從緩存要慢。另一方面,如果內(nèi)聯(lián)函數(shù)體非常短,編譯器為這個(gè)函數(shù)體生成的代碼就會(huì)真的比為函數(shù)調(diào)用生成的代碼要小許多。如果是這種情況,內(nèi)聯(lián)這個(gè)函數(shù)將會(huì)確實(shí)帶來(lái)更小的目標(biāo)代碼和更高的緩存命中率!要牢記在心的一條是,inline指令就象register,它只是對(duì)編譯器的一種提示,而不是命令。也就是說(shuō),只要編譯器愿意,它就可以隨意地忽略掉你的指令,事實(shí)上編譯器常常會(huì)這么做。例如,大多數(shù)編譯器拒絕內(nèi)聯(lián)"復(fù)雜"的函數(shù)。
摘自Effective C++2e,建議看一下《深度探索C++對(duì)象模型〉
2.2 內(nèi)聯(lián)函數(shù)和宏的比較
兩者相同之處是,在其出現(xiàn)的地方將代碼替換。但是區(qū)別很大。
對(duì)于宏來(lái)說(shuō),C++中不贊成使用,除非程序中一定要用宏時(shí)。宏只是在編譯前(編譯處理階段)將程序中有關(guān)字符串替換成宏體,也不進(jìn)行參數(shù)類型等的檢查,容易出錯(cuò)
對(duì)于內(nèi)聯(lián)函數(shù)來(lái)說(shuō),其不是通過(guò)函數(shù)調(diào)用實(shí)現(xiàn)的,是在調(diào)用該函數(shù)的程序處將它展開(kāi),這是在程序的編譯期間完成的,期間進(jìn)行諸如類型檢測(cè)等過(guò)程,減少了錯(cuò)誤的發(fā)生
2.3 應(yīng)用規(guī)則
·宏盡量少用,或不用。
·內(nèi)聯(lián)函數(shù)不宜過(guò)大,過(guò)復(fù)雜
·內(nèi)聯(lián)函數(shù)是建議性而非指令性的,過(guò)大或過(guò)復(fù)雜,會(huì)被系統(tǒng)忽略,而當(dāng)成一般函數(shù)用了。
·內(nèi)聯(lián)函數(shù)一般不要超過(guò)5行,而且經(jīng)常要用時(shí)才聲明稱內(nèi)聯(lián)函數(shù)
3、 #include<filename.h>和#include “filename.h”的區(qū)別
3.1 包含文件
#inculde預(yù)處理指令的作用是在指令處展開(kāi)被包含的文件,包含可以是多重的,也就是說(shuō),一個(gè)被包含的文件中海可以包含其他的文件。
3.2 包含頭文件有兩種格式
一種就是#include<文件名>,另一種是#include ”文件名”。
用尖括號(hào)包括頭文件,這是C++的標(biāo)準(zhǔn)方式,一般這些頭文件都存放于C++系統(tǒng)目錄中的include子目錄下,C++預(yù)處理碰到這種情況后,就到include目錄下搜索給出的文件。
用引號(hào)包括頭文件,適用于用戶自己寫(xiě)的頭文件中,預(yù)處理碰到這種格式,就先在當(dāng)前目錄下進(jìn)行搜索,如果找不到,再到系統(tǒng)目錄下進(jìn)行搜索。
4、 回調(diào)函數(shù)的概念與操作技巧
4.1 回調(diào)函數(shù)的概念
回調(diào)函數(shù)就是在調(diào)用某個(gè)函數(shù)(通常是API函數(shù))時(shí),將用戶自己的一個(gè)函數(shù)(這個(gè)函數(shù)為回調(diào)函數(shù))的地址作為參數(shù)傳遞給那個(gè)函數(shù)。而那個(gè)函數(shù)在需要的時(shí)候,利用傳遞的地址調(diào)用用戶自己定義的函數(shù),這時(shí)在回調(diào)函數(shù)中處理消息或完成一定得操作。
4.2 回調(diào)函數(shù)的實(shí)現(xiàn)
回調(diào)函數(shù)調(diào)用是利用其函數(shù)地址調(diào)用函數(shù)的,實(shí)現(xiàn)函數(shù)的功能。要實(shí)現(xiàn)一個(gè)回調(diào)函數(shù),首先要實(shí)現(xiàn)一個(gè)函數(shù)指針,讓這個(gè)指針指向功能函數(shù),然后利用函數(shù)指針,隱式調(diào)用函數(shù)。
#include<iostream.h>
//聲明功能函數(shù):
void fun()


{
cout<<"this is a callback test"<<endl;
}
//聲明指針函數(shù):
void (*p) (); //指針p指向一個(gè)參數(shù)為空,返回值為void的功能函數(shù)
//聲明調(diào)用函數(shù)caller,函數(shù)的參數(shù)是返回值為void,參數(shù)為空的函數(shù)指針:
void caller(void (*ptr)())


{
ptr();
}
//主函數(shù)實(shí)現(xiàn)
int main()


{
p=fun;
caller(p);
return 0;
}
4.3 回調(diào)函數(shù)的應(yīng)用
回調(diào)函數(shù)在Windows系統(tǒng)編程中用的較多,比如MousePro、GetMsgProc及EnumWindows、DrawState等
例如API函數(shù)
EnumFonts(HDC hdc,
LPCTSTR lpFaceName,
FONTENUMPROC,
PARAM lParam
);
在使用時(shí)需要定義一個(gè)回調(diào)函數(shù):
Int CALLBACK EnumFontsProc(
CONST LOCFONT *lplf,
CONST TEXTMETRIC *lptm,
DWORD dwTtype,
LPARAM lpDate
);
然后寫(xiě)EnumFonts(hdc,null,EnumFontsProc)即可。在使用計(jì)時(shí)器時(shí),把計(jì)時(shí)器消息發(fā)送給程序的另一個(gè)函數(shù),接收這些計(jì)時(shí)器消息的函數(shù)被稱為“回調(diào)”函數(shù),這是一個(gè)在程序中,但是由Windows調(diào)用的函數(shù)。程序的窗口過(guò)程實(shí)際上也是一類回調(diào)函數(shù),當(dāng)注冊(cè)窗口類時(shí),要將函數(shù)的地址告訴Windows,當(dāng)發(fā)送消息給程序時(shí),Windows調(diào)用此函數(shù)。
5、 函數(shù)的調(diào)用規(guī)范
當(dāng)程序被執(zhí)行時(shí),CPU無(wú)法知道函數(shù)的調(diào)用需要多少個(gè)、什么樣的參數(shù),那么也就是說(shuō)計(jì)算機(jī)無(wú)法知道如何給這個(gè)函數(shù)傳遞參數(shù)。函數(shù)的調(diào)用時(shí)由調(diào)用者和被調(diào)用者協(xié)作完成的。計(jì)算機(jī)為此專門(mén)提供了棧的數(shù)據(jù)結(jié)構(gòu),支持參數(shù)的傳遞。函數(shù)調(diào)用時(shí),參數(shù)執(zhí)行壓棧操作,然后開(kāi)始調(diào)用函數(shù),函數(shù)被調(diào)用以后,參數(shù)值出棧,參與函數(shù)功能實(shí)現(xiàn)后,進(jìn)行棧的清理工作。在參數(shù)傳遞中,當(dāng)參數(shù)多于一個(gè)時(shí),按照什么順序壓棧,函數(shù)調(diào)用后,由誰(shuí)把堆?;貜?fù)。
在高級(jí)語(yǔ)言中,通過(guò)函數(shù)調(diào)用規(guī)范(Calling Conventions)來(lái)說(shuō)明這三個(gè)問(wèn)題。常見(jiàn)的調(diào)用規(guī)范有:stdcall、cdecl。
1. stdcall調(diào)用規(guī)范
這是Win API函數(shù)使用的調(diào)用規(guī)范。參數(shù)從右向左一次傳遞并壓入堆棧,由被調(diào)用函數(shù)負(fù)責(zé)堆棧的清退。該規(guī)范生產(chǎn)代碼比_cdecl更小,但當(dāng)函數(shù)有可變個(gè)數(shù)的參數(shù)時(shí)會(huì)轉(zhuǎn)為_cdecl規(guī)范。在Windows中,宏WINAPI、CALLBACK都定義為_stdcall。
stdcall調(diào)用規(guī)范聲明的語(yǔ)法為:
int _stdcall function(int a, int b)
利用stdcall調(diào)用,參數(shù)從右向左壓入堆棧,函數(shù)自身修改堆棧,調(diào)用時(shí)函數(shù)名自動(dòng)加前導(dǎo)的下劃線,后面緊跟一個(gè)@符號(hào),其后緊跟著參數(shù)的尺寸。以上述這個(gè)函數(shù)為例,參數(shù)b首先被壓棧,然后是參數(shù)a,函數(shù)調(diào)用function(1,2),而在編譯時(shí),這個(gè)函數(shù)的名字被翻譯成_function@8。
2. cdecl調(diào)用規(guī)范
cdecl調(diào)用約定又被稱為C調(diào)用約定,是C語(yǔ)言默認(rèn)的調(diào)用約定,它的定義語(yǔ)法是:
int function(int a, int b) //不加修飾就是C調(diào)用約定

int _cdecl function(int a, int b) //明確指出C調(diào)用約定
cdecl調(diào)用約定的參數(shù)壓棧順序是和stdcall一樣的,參數(shù)首先由右向左壓入堆棧。所不同的是,函數(shù)本身不清理堆棧,調(diào)用者負(fù)責(zé)清理堆棧。由于這種變化,C調(diào)用約定允許函數(shù)的參數(shù)個(gè)數(shù)是不固定的,這也是C語(yǔ)言的一大特色。對(duì)于前面的function函數(shù),該修飾自動(dòng)在函數(shù)名前加前導(dǎo)的下劃線,因此函數(shù)名在符號(hào)表中被記錄為_function
3. 函數(shù)調(diào)用約定導(dǎo)致的常見(jiàn)問(wèn)題
如果定義的約定和使用的約定不一致,將導(dǎo)致堆棧被破壞,導(dǎo)致嚴(yán)重問(wèn)題,下面是兩種常見(jiàn)的問(wèn)題。
·函數(shù)原型聲明和函數(shù)體定義不一致
·DLL導(dǎo)入函數(shù)時(shí)聲明了不同的函數(shù)約定
如果還想獲得更多關(guān)于《Visual C++代碼參考與技巧大全》的內(nèi)容,可點(diǎn)擊下面網(wǎng)址,
http://www.shnenglu.com/kangnixi/archive/2010/01/13/105591.html