目錄
一、函數的缺省參數
(一)全缺省參數
(二)半缺省參數
二、函數重載
(一)參數類型不同
(二)參數個數不同
(三)參數類型順序不同
三、引用相關問題
(一)引用的基本概念與初始化
(二)引用在函數中的應用 - 以Swap函數為例
1.?普通變量交換(值傳遞方式存在問題)
2.?使用指針引用實現交換
3.?使用普通引用實現交換(更簡潔常用)
四、C++編譯流程
(一)預處理
(二)編譯
(三)匯編
(四)鏈接
在深入學習C++編程的過程中,函數特性、引用機制以及編譯流程都是極為關鍵的知識點。通過學習,我對這些內容有了更為透徹的理解,在此進行詳細梳理與記錄。
?
一、函數的缺省參數
(一)全缺省參數
全缺省參數意味著函數的所有參數都具備默認值。以下是示例代碼:
cppvoid Func(int a = 10, int b = 20, int c = 30){cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;}
當調用此函數時,若不傳入參數,函數會自動采用默認值。比如執行?Func()? ,將輸出?a = 10? 、?b = 20? 、?c = 30? 。這一特性在很多場景下能為函數調用提供便利,減少重復的參數輸入。
(二)半缺省參數
半缺省參數是指函數部分參數擁有默認值。代碼示例如下:
cppvoid Func(int a, int b = 10, int c = 20){cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;}
需要特別注意的是,半缺省參數必須遵循從右至左的順序依次給出,不能出現間隔的情況。并且,缺省參數不能在函數聲明和定義中同時存在。這是C++語法的嚴格規定,違背此規則會導致編譯錯誤。
二、函數重載
函數重載允許在同一作用域內,存在多個同名但參數列表(參數類型、個數、順序)不同的函數。以下是具體示例:
(一)參數類型不同
cppint Add(int left, int right){cout << "int Add(int left, int right)" << endl;return left + right;}double Add(double left, double right){cout << "double Add(double left, double right)" << endl;return left + right;}
上述代碼中,兩個?Add?函數雖然名字相同,但一個處理?int?類型參數,一個處理?double?類型參數,構成了函數重載。
(二)參數個數不同
?
cppvoid f(){cout << "f()" << endl;}void f(int a){cout << "f(int a)" << endl;}
這里兩個?f?函數,一個無參數,一個帶有?int?類型參數,體現了因參數個數不同而形成的函數重載。
(三)參數類型順序不同
?
cppvoid f(int a, char b){cout << "f(int a,char b)" << endl;}void f(char b, int a){cout << "f(char b,int a)" << endl;}
這兩個?f?函數參數類型相同,但順序不同,同樣構成函數重載。
然而,函數重載也可能引發調用歧義問題。例如:
?
cppvoid f(){cout << "f()" << endl;}void f(int a = 0){cout << "f(int a)" << endl;}int main(){f(); // 此處調用會出現歧義,編譯器無法確定調用哪個函數return 0;}
在此情形下,編譯器難以抉擇要調用哪個?f?函數,從而報錯。這就要求我們在使用函數重載時,要謹慎考慮,避免出現這種不明確的調用情況。
三、引用相關問題
(一)引用的基本概念與初始化
在C++中,引用實際上是給變量取的一個別名。示例如下:
cppint a = 0;int& b = a;int& c = b;
這里?b?和?c?均為?a?的引用。需要著重強調的是,引用在定義時必須進行初始化。像?int& d;?這種未初始化的引用是不符合語法規則的,編譯器會報錯,提示如?C2530 “d”: 必須初始化引用? 。這是因為引用本質上是變量的別名,必須在定義時明確其關聯的變量。
(二)引用在函數中的應用 - 以Swap函數為例
1.?普通變量交換(值傳遞方式存在問題)
最初嘗試實現交換兩個整數變量值的函數時,若采用值傳遞方式,代碼如下:
cppvoid Swap(int a, int b){int tmp = a;a = b;b = tmp;}int main(){int x = 0, y = 1;Swap(x, y);cout << x << " " << y << endl; // 輸出結果仍為0 1,未實現交換return 0;}
這種方式無法真正實現變量值的交換,原因在于函數內交換的只是形參?a?和?b?的值,而實參?x?和?y?并未受到影響,因為形參是實參的副本,函數結束后副本的改變不會反饋到實參上。
2.?使用指針引用實現交換
為解決上述問題,可使用指針引用的方式,代碼如下:
?
cppvoid Swap(int*& a, int*& b){int* tmp = a;a = b;b = tmp;}int main(){int x = 0, y = 1;int* px = &x, * py = &y;cout << px << " " << py << endl;Swap(px, py);cout << px << " " << py << endl;return 0;}
這里函數參數是指針的引用,通過這種方式可以直接操作指針本身,實現指針所指向變量地址的交換。
3.?使用普通引用實現交換(更簡潔常用)
更為簡潔常用的方式是使用普通引用,代碼如下:
?
cppvoid Swap(int& a, int& b){int tmp = a;a = b;b = tmp;}int main(){int x = 0, y = 1;Swap(x, y);cout << x << " " << y << endl; // 輸出1 0,成功實現交換return 0;}
在這個版本中,?Swap?函數接收的是變量的引用,函數內部對形參的操作等同于對實參的操作,從而順利實現了兩個整數變量值的交換。
四、C++編譯流程
(一)預處理
預處理階段承擔著多項重要任務,包括頭文件展開、宏替換、條件編譯以及去掉注釋等操作。假設有?Stack.h? 、?Stack.cpp? 、?Test.cpp?等文件,經過預處理后,會生成?Stack.i?和?Test.i?。這一階段為后續的編譯工作做了前期的準備,將代碼整理成更便于編譯器處理的形式。
(二)編譯
編譯階段主要是對代碼進行語法檢查,并生成匯編代碼。在此過程中,會生成?Stack.s?和?Test.s?文件。如果代碼中存在語法錯誤,就會在這個階段被編譯器檢測出來并報錯。語法檢查涵蓋了對變量聲明、語句結構、函數調用等多方面的規則校驗,只有通過語法檢查的代碼才能順利進入后續階段。
(三)匯編
匯編階段的任務是將匯編代碼轉換成二進制機器碼,生成?Stack.o?和?Test.o?目標文件。這是將高級語言逐步轉化為機器能夠直接執行的指令的關鍵步驟,二進制機器碼是計算機硬件能夠直接理解和執行的指令形式。
(四)鏈接
鏈接階段會把各個目標文件以及所依賴的庫文件等進行鏈接,最終生成可執行程序(如?xxx.exe?或?a.out?)。在鏈接過程中,會處理函數的聲明和定義之間的關聯,找到函數的具體實現位置。例如,在多個源文件中調用的函數,鏈接器會確保函數的聲明和定義能夠正確匹配,使得程序在運行時能夠準確找到并執行相應的函數代碼。
理解C++編程中的這些關鍵知識點,無論是函數特性、引用機制還是編譯流程,對于編寫高質量、無錯誤的代碼至關重要。在實際編程過程中,不僅要牢記這些理論知識,更要通過大量的實踐來加深理解和掌握,在遇到編譯錯誤和運行時問題時,能夠依據這些知識快速定位和解決問題,不斷提升自己的編程能力。