在 C# CLR 中學習 C++ 之瞭解 extern

2022-09-02 12:01:29

一:背景

在 CLR 原始碼中有很多的 externextern "C" 這樣的關鍵詞,比如下面這些程式碼:


extern size_t gc_global_mechanisms[MAX_GLOBAL_GC_MECHANISMS_COUNT];

extern DWORD g_dwHandles;

// The single GC heap instance, shared with the VM.
extern IGCHeapInternal* g_theGCHeap;

extern PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntimeFunction, /* out */ SIZE_T * pSize);

extern "C" uint32_t* g_gc_card_table;
extern "C" uint8_t* g_gc_lowest_address;

extern "C"
{
    uint8_t *g_gc_sw_ww_table = nullptr;
    bool g_gc_sw_ww_enabled_for_gc_heap = false;
}

那這些都是什麼意思呢? 為了更好的學習 CLR,這些還是要簡單瞭解一下的。

一:extern

1. 變數定義和變數參照

在 C# 中並沒有聽說過有 extern 這種概念,其實變數可以有兩種方式存在。

  1. 變數定義

  2. 變數參照

變數定義概念很簡單,定義就得給它分配記憶體空間,比如下面這樣:


#include <iostream>
#include <Windows.h>

int k = 10;
const char* ch = "abcde";

int main()
{
	printf("ch=%d", strlen(ch));
}

接下來看下 變數參照,它其實和 檔案參照 以及 C# 的 using 概念相似,即把其他檔案中的變數引入到本檔案,目的就是為了使用,比如在 Arts 檔案下定義了一個 page.cpp 檔案,截圖如下:

為了能夠在 ConsoleApplication3.cpp 中使用 int i ,那怎麼辦呢? 這時候就需要用 extern 引入了。

因為 VisualStudio 可以幫我們自動連結,所以這裡就不需要 #include "page.cpp" 匯入,接下來把程式跑起來,就可以觀察到程式的結果。

如果不用 extern 匯入的話,就會出現編譯錯誤,說 n 是未定義的。

還有一點要注意 extern 是對外部變數的一個參照,它不會生成任何組合程式碼。

2. extern "C"

要理解這個關鍵詞,首先要明白 方法符號 的概念,因為 C 和 C++ 在給方法生成符號的邏輯是不一樣的,比如同樣的一個 fly 函數。


#include <stdio.h>

void fly() {
	printf("hello world");
}

int main()
{
}

在 C 中生成的函數名還是 fly 字樣。

可 C++ 不這麼認為,它會對 fly 函數名重新編排,比如下面的 ?fly@@YAXXZ

如果你在 C++ 中混用 C 的話,這時候就有理念衝突,那在C++中讓某些函數名還是原樣生成有辦法嗎?當然可以了,這就需要使用 extern "C" ,參考如下程式碼:


// page.cpp
#include <stdio.h>

extern "C"
{
	void fly() {
		printf("hello");
	}
}

void fly2() {
	printf("hello");
}

然後可以在 ConsoleApplication.cpp 中引入進來。


// ConsoleApplication.cpp

#include <iostream>

extern "C" void fly();

extern void fly2();

int main()
{
	fly();

	fly2();

	return 0;
}

好了,本篇就簡單說這麼多吧,相信再回頭看 CLR 中的那些 extern 關鍵詞,你會有一些新的理解。