【C++】一篇文章搞懂為什麼CPP支援函數過載而C不支援?

2020-10-04 11:00:21

Windows作業系統下VS編譯C檔案

#include <stdio.h>
#include <Windows.h>

double add(double a, double b);
int main()
{
    add(1.0, 2.0);
    
    system("pause");
    return 0;
}

這段程式碼編譯可以通過,但是連結通過不了

報錯:

錯誤 2 error LNK2019: 無法解析的外部符號 _add,該符號在函數 _main 中被參照

#include <stdio.h>
#include <Windows.h>

int add(int a, int b);

int main()
{
    add(1, 2);
    system("pause");
    return 0;
}

這段程式碼可以通過編譯階段,但是無法通過連結階段

報錯:

錯誤 2 error LNK2019: 無法解析的外部符號 _add,該符號在函數 _main 中被參照

由上述兩個相同功能資料型別不同的add函數的報錯可以看出:add函數在編譯之後存在符號表中的名字都是_add,而main函數名字是_main

所以,

VS編譯器編譯C檔案時對於相同名字的函數並不做區分

,都是以

_函數名

來命名

Windows作業系統下VS編譯CPP檔案

#include <iostream>
using namespace std;

int add(int a, int b);
double add(double a, double b);
int main()
{
    add(1.0, 2.0);
    add(1, 2);
    system("pause");
    return 0;
}

這段程式碼可以通過編譯,但是不能通過連結

報錯:

error LNK2019: 無法解析的外部符號 「int __cdecl add(int,int)」 (?add@@YAHHH@Z),該符號在函數 _main 中被參照

error LNK2019: 無法解析的外部符號 「double __cdecl add(double,double)」 (?add@@YANNN@Z),該符號在函數 _main 中被參照

由報錯可以看出:自定義函數的函數名經過編譯之後,變成了另外一種表現形式,對於同名函數有區分度

命名規則:【?函數名@@YA返回值資料型別對應字母+形引資料型別對應字母@Z】

對應關係:

  • int -> H
  • double-> N
  • float -> M
  • char -> D
  • void -> X
  • bool -> _N

例如:

int add(int ,int ) -> ?add@@YAHHH@Z

double func(double,double) -> ?func@@YANNN@Z

Linux作業系統下gcc編譯C檔案

image.png

由此可以看出gcc編譯c之後,自定義函數的函數名不變,對於同名函數沒有區分度

Linux作業系統下g++編譯CPP檔案

image.png

由此可以看出g++編譯CPP檔案之後,自定義函數的函數名改變了,可以對同名函數進行區分

命名規則:【_Z + 函數名字長度 + 函數名 + 形引資料型別首字母】

例如:

int add(int ,int ) -> _Z 3 add i i

int func(double ,double ,double) -> _Z 4 func d d d

總結:無論是Windows作業系統還是Linux作業系統,基於這些作業系統的主流編譯器都有一下特點:

  • 編譯C檔案: 編譯之後,對於 相同函數名的自定義函數 無區分度
  • 編譯CPP檔案:編譯之後,對於 相同函數名的自定義函數 有區分度

結:無論是Windows作業系統還是Linux作業系統,基於這些作業系統的主流編譯器都有一下特點:

  • 編譯C檔案: 編譯之後,對於 相同函數名的自定義函數 無區分度
  • 編譯CPP檔案:編譯之後,對於 相同函數名的自定義函數 有區分度

因此,至於是 命名規則 導致 C語言不支援函數過載而CPP支援函數過載 ,還是 C語言不支援函數過載而CPP支援函數過載 導致 命名規則如此,其因果關係就仁者見仁智者見智啦