C++之每日知識樹-09-從static到extern 「C「再到C無法過載原因

2020-08-10 11:41:28

之前我們寫過C++之每日知識樹-08-從static到單例模式再到多執行緒安全
今天我們再寫一個由static引出的一個面試點。

關於static關鍵字的五處用處的記憶體位置、作用域、初始化等,大家可以看上一篇文章,今天就從作用域展開。

大家都知道static宣告定義的函數或者變數的作用域就是在本檔案中,普通全域性變數是在整個工程檔案中都可以使用,然而由C語言演化而來的C++語言可以使用另外一個C檔案嗎??這就是需要用到extern關鍵字!

一、extern 「C"

extern可以置於變數或者函數前,以標示變數或者函數的定義在別的檔案中,提示編譯器遇到此變數和函數時在其他模組中尋找其定義。此外extern也可用來進行鏈接指定。

1、extern關鍵字
在一個專案中必須保證函數、變數、列舉等在所有的原始檔中保持一致,除非你指定定義爲區域性的。首先來一個例子:

//file1.c:
    int x=1;
    int f(){do something here}
//file2.c:
    extern int x;
    int f();
    void g(){x=f();}

在file2.c中g()使用的x和f()是定義在file1.c中的。extern關鍵字表明file2.c中x,僅僅是一個變數的宣告,其並不是在定義變數x,並未爲x分配記憶體空間。變數x在所有模組中作爲一種全域性變數只能被定義一次,否則會出現連線錯誤。但是可以宣告多次,且宣告必須保證型別一致,如:

//file1.c:
    int x=1;
    int b=1;
    extern c;
//file2.c:
    int x;// x equals to default of int type 0
    int f();
    extern double b;
    extern int c;

在這段程式碼中存在着這樣的三個錯誤:

x被定義了兩次
b兩次被宣告爲不同的型別
c被宣告瞭兩次,但卻沒有定義

extern是C/C++語言中表明函數和全域性變數作用範圍(可見性)的關鍵字,該關鍵字告訴編譯器,其宣告的函數和變數可以在本模組或其它模組中使用。

2、」C"
典型的,一個C++程式包含其它語言編寫的部分程式碼。類似的,C++編寫的程式碼片段可能被使用在其它語言編寫的程式碼中。不同語言編寫的程式碼互相呼叫是困難的,甚至是同一種編寫的程式碼但不同的編譯器編譯的程式碼。例如,不同語言和同種語言的不同實現可能會在註冊變數保持參數和參數在棧上的佈局,這個方面不一樣。
extern "C"指令非常有用,因爲C和C++的近親關係。

注意:extern "C"指令中的C,表示的一種編譯和連線規約,而不是一種語言。C表示符合C語言的編譯和連線規約的任何語言,如Fortran、assembler等。

3、C與C++的相互混用

3、1、C++中混用C程式碼
extern有兩個作用

1、 當它與"C"一起連用時,如: extern 「C」 void fun(int a, int b);則告訴編譯器在編譯fun這個函數名時按着C的規則去翻譯相應的函數名而不是C++的,C++的規則在翻譯這個函數名時會把fun這個名字變得面目全非(下面 下麪會介紹).
2、當extern不與"C"在一起修飾變數或函數時,如在標頭檔案中: extern int g_Int;它的作用就是宣告函數或全域性變數的作用範圍的關鍵字,其宣告的函數和變數可以在本模組和其他模組中使用

如:
cHeader.h

#ifndef C_HEADER
#define C_HEADER
 
extern void print(int i);
#endif C_HEADER

cHeader.c

#include <stdio.h>
#include "cHeader.h"
void print(int i)
{
    printf("cHeader %d\n",i);
}

main.cpp

extern "C"{
#include "cHeader.h"
}
 
int main(int argc,char** argv)
{
    print(3);
    return 0;
}

在这里插入图片描述
3、2、C中混用C++程式碼
cppHeader.h

#ifndef CPP_HEADER
#define CPP_HEADER
 
extern "C" void print(int i);
#endif CPP_HEADER

cppHeader.cpp

#include "cppHeader.h"
 
#include <iostream>
using namespace std;
void print(int i)
{
    cout<<"cppHeader "<<i<<endl;
}

main.c

extern void print(int i);
int main(int argc,char** argv)
{
    print(3);
    return 0;
}

二、C語言無法過載
我們知道過載是C++實現其三大特性之一的多型的重要步驟,然而對於程序導向的C語言來說是不能過載的,這就涉及到兩者的編譯過程不同。、

C++編譯過程
void print(int i,char j , float k , double l);
編譯後函數名變爲:

_print_int_char_float_double

C編譯過程
void print(int i,char j , float k , double l);
編譯後函數名變爲:

_print

可以看出,C++編譯後函數名由以前做的函數名,參數數量和參數型別等組合生成,故可以實現過載,而C語言就不行了。