C語言學習篇(32)——為什麼C語言不能函式過載

前言

在日常的開發中,我們有時會遇到根據不同情景,想透過傳入不同型別的引數,而呼叫統一的函式介面,即函式過載。 在C++中原生支援了函式過載, 而在C語言中並不支援,只能透過一些技巧來變相解決, 如定義flag形參, 根據flag值不同,進行不同的處理。例如:

void test(void *a, void *b, int flag){ if(flag == 1) { int *m_a = (int *)a; int *m_b = (int *)b; } else if(flag == 2) { char *m_a = (char *)a; char *m_b = (char *)b; } else if(flag == 3) { int *m_a = (int *)a; char *m_b = (char *)b; }}

而在C++中,直接函式過載:

//===============函式過載===================void test(int *a, int *b){ //函式一}void test(int *a, char *b){ //函式二}void test(char *a, double *b){ //函式三}//===============測試======================int main(void){ //呼叫函式一 int a, b; test(&a, &b); //呼叫函式二 int c; char d; test(&c, &d); //呼叫函式三 char e; double f; test(&e, &f); return 0;}

注意:

函式返回值不是函式過載的判斷標準!

使用objdump工具反彙編

大家都知道了在C語言中不能函式過載, 究其原因是否思考過呢?接下來我們以下c和c++程式碼為例子,分別用gcc和 g++編譯, 然後再用objdump工具反彙編看看得到的彙編程式碼有什麼區別。

C語言程式碼:

C語言學習篇(32)——為什麼C語言不能函式過載

使用編譯無警告無錯誤, 使用以下命令:

objdump -d c_test > c_res

C語言學習篇(32)——為什麼C語言不能函式過載

使用vi(vim)開啟反彙編結果c_res檔案:

C語言學習篇(32)——為什麼C語言不能函式過載

從上圖可知,在C語言中,函式經過C編譯器編譯後,最終得到的符號表(對應函式地址)只包含函式名,跟函式返回型別, 引數型別都無關係。

接下來,我們在看看C++程式碼:

C語言學習篇(32)——為什麼C語言不能函式過載

使用g++(c++編譯器)編譯後,同樣再用objdump工具反彙編:

C語言學習篇(32)——為什麼C語言不能函式過載

得到反彙編結果檔案cpp_res,vi開啟:

C語言學習篇(32)——為什麼C語言不能函式過載

從上圖我們可以看到,函式和編譯後符號對應關係:

函式一:void test(int *a, int *b) —— _Z4testPiS_

函式二:char test(char *a, int *b) —— _Z4testPcPi

函式三: int test(char *a, double *b) —— _Z4testPcPd

分析:因為函式一, 二,三,3個函式返回值型別都不同,而最終得到的符號字首都是“_Z4”,因此驗證了函式返回值型別不是函式過載的判斷依據。中間的“test”是函式名,這個容易理解,而剩餘的3個字尾:

PiS_ 對應 函式一的引數型別 (int , int) 的首字母i, 這裡因為2個型別一致,編譯處理下

PcPi 對應 函式二的引數型別(char, int)的首字母ci

PcPd 對應 函式三的引數型別(char , double)的首字母cd

因此

函式引數型別是函式過載的判斷依據!

小結

根據上面的objdump工具反彙編檢視彙編程式碼中符號,我們知道了在C語言中,C編譯器在函式符號處理時,只以函式名稱為符號,因此無法進行函式過載,同時我們也明白了C++中能夠使用函式過載,只是編譯器進行了處理,最終實際呼叫的還是不同的函式,但統一了介面。

最後再次強調下, 要想學好程式語言,尤其是編譯語言,要站在編譯器的角度思考問題,我們可以透過像objdump等工具來輔助我們反推理解。

C語言學習篇(32)——為什麼C語言不能函式過載