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


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


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


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


{
ptr();
}
//主函數實現
int main()


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

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