[C++]函式與編譯預處理(二)
函式與編譯預處理
引數個數可變的函式
到目前為止,在定義函式時,都明確規定了函式的引數個數及型別。在呼叫函式時,實參的個數必須與形參相同。在呼叫具有預設引數值的函式時,本質上,實參的個數與形參的個數仍是相同的,由於引數具有預設值,因此,在呼叫時可省略。在某些應用中,在定義函式時,並不能確定函式的引數個數,引數的個數在調時才能確定。在C++中允許定義引數個數可變的函式。
首先,必須包含標頭檔案“stdarg.h”,因為要用到裡面的三個庫函式 va_start( )、va_arg( )和va_end( )。
其次,要說明一個va_list型別的變數。va_list與int,float類同,它是C++系統預定義的一個資料型別(非float),只有通過這種型別的變數才能從實際參數列中取出可變有引數。如:va_list ap;
va_start(ap,b):初始化
va_arg(ap,int):依次取引數
va_end(ap):正確結束
va_start():有兩個引數,va_start(ap,b); b即為可變引數前的最後一個確定的引數。
va_arg():有兩個引數,va_arg(ap,int) int即為可變引數的資料型別名。
int temp;
temp=va_arg(ap,int);
va_end():完成收尾工作。va_end(ap);
在呼叫引數個數可變的函式時,必定有一個引數指明可變引數的個數或總的實參個數。如第一個引數值為總的實際引數的個數。
使用引數數目可變的函式時要注意以下幾點:
1、在定義函式時,固定引數部分必須放在參數列的前面,可變引數在後面,並用省略號“...”表示可變引數。在函式呼叫時,可以沒有可變的引數。
2、必須使用函式va_start()來初始化可變引數,為取第一個可變的引數作好準備工作;使用函式va_arg()依次取各個可變的引數值;最後用函式va_end()做好結束工作,以便能正確地返回。
3、在呼叫引數個數可變的函式時,必定有一個引數指明可變引數的個數或總的實參個數。
函式的過載
所謂函式的過載是指完成不同功能的函式可以具有相同的函式名。
C++的編譯器是根據函式的實參來確定應該呼叫哪一個函式的。
1、定義的過載函式必須具有不同的引數個數,或不同的引數型別。只有這樣編譯系統才有可能根據不同的引數去呼叫不同的過載函式。
2、僅返回值不同時,不能定義為過載函式。即僅函式的型別不同,不能定義為過載函式
編譯預處理
高階語言編譯過程
C語言提供的編譯預處理的功能有以下三種:
巨集定義 檔案包含 條件編譯
巨集定義
不帶引數的巨集定義
用一個指定的識別符號(即名字)來代表一個字串,以後凡在程式中碰到這個識別符號的地方都用字串來代替。
這個識別符號稱為巨集名,編譯前的替代過程稱為“巨集展開”。
# define 識別符號 字串
#define PRICE 30
void main(void)
{ int num, total; /*定義變數*/
num=10; /*變數賦值*/
total=num*PRICE;
cout<<"total=“<<total<<endl;
}
編譯程式將巨集定義的內容認為是字串,沒有任何實際的物理意義。
注意:
1、巨集展開只是一個簡單的“物理”替換,不做語法檢查,不是一個語句,其後不加分號“;”
2、#define命令出現在函式的外面,其有效範圍為定義處至本原始檔結束。可以用# undef命令終止巨集定義的作用域。
#define G 9.8
void main(void )
{.....}
# undef G
int max(int a,int b)
{...... }
3、對程式中用雙引號括起來的字串內容,即使與巨集名相同,也不進行置換。
4、在進行巨集定義中,可以用已定義的巨集名,進行層層置換。
帶引數的巨集定義
按#define命令列中指定的字串從左至右進行置換巨集名,字串中的形參以相應的實參代替,字串中的非形參字元保持不變。
定義巨集時在巨集名與帶引數的括弧間不能有空格。
#define S_ (r) P*r*r
帶引數的巨集與函式呼叫的區別
相同:有實參、形參,代入呼叫。
不同之處
1、函式呼叫先求表示式的值,然後代入形參,而巨集只是機械替換。
2、函式呼叫時形參、實參進行型別定義,而巨集不需要,只是作為字串替代。
3、函式呼叫是在執行程式時進行的,其目的碼短,但程式執行時間長。而巨集呼叫是在編譯之前完成的,執行時已將程式碼替換程式序中,目的碼長,執行時間稍快。
一般用巨集表示實時的、短小的表示式。
檔案包含
一個原始檔可以將另外一個原始檔的全部內容包含進來,即將另外的檔案包含到本檔案之中。
# include “檔名”
注意:
1、檔名是C的原始檔名,是文字檔案,字尾名可以任選。*.cpp *.h
2、一個#include語句只能指定一個被包含檔案。
3、檔名用雙引號或尖括號括起來。
4、包含後所有原始檔編譯為一個可執行檔案。
條件編譯
C語言允許有選擇地對程式的某一部分進行編譯。也就是對一部分源程式指定編譯條件。
條件編譯有以下幾種形式:
1、 # ifdef 識別符號
程式段1
# else
程式段2
# end if
當識別符號已被定義過(用#define定義),則對程式段1進行編譯,否則編譯程式段2.
2、 # ifndef 識別符號
程式段1
# else
程式段2
# endif
與形式1相反,當識別符號沒有被定義過(用#define定義),則對程式段1進行編譯,否則編譯程式段2。
3、 # if 表示式
程式段1
# else
程式段2
# endif
當表示式為真(非零),編譯程式段1,表示式為零,編譯程式段2。
程式的多檔案組織
而在設計一個功能複雜的大程式時,為了便於程式的設計和除錯,通常將程式分成若干個模組,把實現一個模組的程式或資料放在一個檔案中。當一個完整的程式被存放在多於一個檔案中時,稱為程式的多檔案組織。
內部函式和外部函式
內部函式:函式只限於在本檔案中呼叫,其它檔案不能呼叫,用static 定義該函式。
static float fac( int n)
{ ...... }
外部函式:函式的預設形式,可以被其它檔案呼叫,用extern 定義該函式。呼叫時,在檔案中用extern 說明。
void main(void)
{ extern enter_string( );
char str[80];
enter_string(str);
..........
}
相關文章
- en_concat函式編譯失敗處理函式編譯
- GCC編譯過程(預處理->編譯->彙編->連結)GC編譯
- [譯] 如何使用純函式式 JavaScript 處理髒副作用函式JavaScript
- doxygen 宏定義/宏編譯/條件編譯/預處理/預編譯 不處理、忽略條件、分析所有條件、滿足所有條件的方法編譯
- C++ 字元處理函式(cctype標頭檔案)C++字元函式
- C++行內函數、函式過載與函式預設引數C++函數函式
- 模板函式編譯原理函式編譯原理
- Flink處理函式實戰之二:ProcessFunction類函式Function
- 編譯warp,d語言寫的c/c++前處理器.編譯C++
- 陣列處理函式陣列函式
- 大話css預編譯處理(三):基礎語法篇CSS編譯
- C++ 字串 cctype 標頭檔案標準庫處理函式C++字串函式
- C++中函式指標與函式物件C++函式指標物件
- C++中的字串編碼處理C++字串編碼
- 使用自定義函式實現資料編解碼、格式處理與業務告警函式
- 用預編譯去理解函式宣告提升和變數宣告提升編譯函式變數
- .Net7 CLR的呼叫函式和編譯函式函式編譯
- fill函式與memset函式的區別(c++)函式C++
- C++學習筆記(二)——函式C++筆記函式
- 如何在ES5與ES6環境下處理函式預設引數函式
- (特徵工程實戰)ML最實用的資料預處理與特徵工程常用函式!特徵工程函式
- Flink處理函式實戰之四:視窗處理函式
- [譯] 編寫函式式的 JavaScript 實用指南函式JavaScript
- Sanic 處理函式修飾器函式
- mongoDB中聚合函式java處理MongoDB函式Java
- JavaScript 註冊事件處理函式JavaScript事件函式
- echarts 繫結事件處理函式Echarts事件函式
- C語言之字串處理函式C語言字串函式
- C++ 的函式分檔案編寫C++函式
- C++ lambda 表示式與「函式物件」(functor)C++函式物件
- 【C/C++】訊號處理之sigaction函式的健壯性測試C++函式
- split用法與影像預處理
- Flink處理函式實戰之五:CoProcessFunction(雙流處理)函式Function
- AndroidKiller反編譯失敗的處理方法Android編譯
- C++建構函式和解構函式呼叫虛擬函式時使用靜態聯編C++函式
- 從原始檔到可執行檔案:原始檔的預處理、編譯、彙編、連結編譯
- JavaScript 批量註冊事件處理函式JavaScript事件函式
- JavaScript 非同步函式的 Promisification 處理JavaScript非同步函式
- 六、函式、包和錯誤處理函式