C++和C的混合編譯的項(xiàng)目實(shí)踐
簡(jiǎn)介
C++
語言的創(chuàng)建初衷是 “a better C”,但是這并不意味著 C++
中類似 C
語言的全局變量和函數(shù)所采用的編譯和連接方式與 C
語言完全相同。作為一種欲與 C
兼容的語言, C++
保留了一部分過程式語言的特點(diǎn)(被世人稱為"不徹底地面向?qū)ο?quot;),因而它可以定義不屬于任何類的全局變量和函數(shù)。但是, C++
畢竟是一種面向?qū)ο蟮某绦蛟O(shè)計(jì)語言,為了支持函數(shù)的重載, C++
對(duì)全局函數(shù)的處理方式與 C
有明顯的不同。
本文將介紹如何通過 extern “C” 關(guān)鍵字在 C++
中支持 C
語言 和 在C
語言中如何支持 C++
。
某企業(yè)曾經(jīng)給出如下的一道面試題
為什么標(biāo)準(zhǔn)頭文件都有類似以下的結(jié)構(gòu)?
//head.h #ifndef HEAD_H #define HEAD_H #ifdef __cplusplus extern "C" { #endif /*...*/ #ifdef __cplusplus } #endif #endif /* HEAd_H */
問題分析
- 這個(gè)頭文件head.h可能在項(xiàng)目中被多個(gè)源文件包含(#include “head.h”),而對(duì)于一個(gè)大型項(xiàng)目來說,這些冗余可能導(dǎo)致錯(cuò)誤,因?yàn)橐粋€(gè)頭文件包含類定義或inline函數(shù),在一個(gè)源文件中head.h可能會(huì)被#include兩次(如,a.h頭文件包含了head.h,而在b.c文件中#include a.h和head.h)——這就會(huì)出錯(cuò)(在同一個(gè)源文件中一個(gè)結(jié)構(gòu)體、類等被定義了兩次)。
- 從邏輯觀點(diǎn)和減少編譯時(shí)間上,都要求去除這些冗余。然而讓程序員去分析和去掉這些冗余,不僅枯燥且不太實(shí)際,最重要的是有時(shí)候又需要這種冗余來保證各個(gè)模塊的獨(dú)立。
為了解決這個(gè)問題,上面代碼中的
#ifndef HEAD_H #define HEAD_H /*……………………………*/ #endif /* HEAD_H */
就起作用了。如果定義了HEAD_H,#ifndef/#endif之間的內(nèi)容就被忽略掉。因此,編譯時(shí)第一次看到head.h頭文件,它的內(nèi)容會(huì)被讀取且給定HEAD_H一個(gè)值。之后再次看到head.h頭文件時(shí),HEAD_H就已經(jīng)定義了,head.h的內(nèi)容就不會(huì)再次被讀取了。
那么下面這段代碼的作用又是什么呢?
#ifdef __cplusplus extern "C" { #endif /*.......*/ #ifdef __cplusplus } #endif
我們將在后面對(duì)此進(jìn)行詳細(xì)說明。
關(guān)于 extern “C”
前面的題目中的 __cplusplus
宏,這是C++中已經(jīng)定義的宏,是用來識(shí)別編譯器的,也就是說,將當(dāng)前代碼編譯的時(shí)候,是否將代碼作為 C++
進(jìn)行編譯。
首先從字面上分析extern “C”,它由兩部分組成:extern關(guān)鍵字、“C”。下面我就從這兩個(gè)方面來解讀extern "C"的含義。
首先,被它修飾的目標(biāo)是 extern
的;其次,被它修飾的目標(biāo)是 C
的。
extern關(guān)鍵字
被 extern “C” 限定的函數(shù)或變量是 extern
類型的。
extern是C/C++語言中表明函數(shù)和全局變量作用范圍(可見性)的關(guān)鍵字,該關(guān)鍵字告訴編譯器,其聲明的函數(shù)和變量可以在本模塊或其它模塊中使用。通常,在模塊的頭文件中對(duì)本模塊提供給其它模塊引用的函數(shù)和全局變量以關(guān)鍵字extern聲明。例如,如果模塊B欲引用該模塊A中定義的全局變量和函數(shù)時(shí)只需包含模塊A的頭文件即可。這樣,模塊B中調(diào)用模塊A中的函數(shù)時(shí),在編譯階段,模塊B雖然找不到該函數(shù),但是并不會(huì)報(bào)錯(cuò);它會(huì)在連接階段中從模塊A編譯生成的目標(biāo)代碼中找到此函數(shù)。
被extern修飾的函數(shù),需要在編譯階段去鏈接該目標(biāo)文件,并且與extern對(duì)應(yīng)的關(guān)鍵字是 static,被static修飾的全局變量和函數(shù)只能在本模塊中使用。因此,一個(gè)函數(shù)或變量只可能被本模塊使用時(shí),其一般是不可能被extern “C”修飾的。
**注意:**例如語句 extern int a;
僅僅是對(duì)變量的聲明,其并不是在定義變量 a
,聲明變量并未為 a
分配內(nèi)存空間。定義語句形式為 int a;
變量 a
在所有模塊中作為一種全局變量只能被定義一次,否則會(huì)出現(xiàn)連接錯(cuò)誤。
被 extern “C” 修飾的變量和函數(shù)是按照 C
語言方式編譯和連接的。
由于C++和C兩種語言的親密性,并且早期大量的庫(kù)都是由C語言實(shí)現(xiàn)的,所以不可避免的會(huì)出現(xiàn)在C++程序中調(diào)用C的代碼、C的程序中調(diào)用C++的代碼,但是它們各自的編譯和鏈接的規(guī)則是不同的。
函數(shù)名修飾
- 由于Windows下vs的修飾規(guī)則過于復(fù)雜,而Linux下gcc的修飾規(guī)則簡(jiǎn)單易懂,下面我們使用了gcc演示了這個(gè)修飾后的名字。
- 通過下面我們可以看出gcc的函數(shù)修飾后名字不變。而g++的函數(shù)修飾后變成【_Z+函數(shù)長(zhǎng)度+函數(shù)名+類型首字母】。
分別使用C的編譯器和C++的編譯器去編譯并獲得一個(gè)可執(zhí)行文件
使用C語言(gcc)編譯器編譯后結(jié)果
使用objdump -S 命令查看gcc生成的可執(zhí)行文件:
使用C++編譯器(g++)編譯后結(jié)果
使用objdump -S 命令查看g++生成的可執(zhí)行文件:
**linux:**修飾后的函數(shù)名= _Z + 函數(shù)名長(zhǎng)度 + 形參類型首字母,Windows下也是相似的,細(xì)節(jié)上會(huì)有所不同,本質(zhì)上都是通過函數(shù)參數(shù)信息去修飾函數(shù)名。
C++的編譯和鏈接方式
采用g++編譯完成后,函數(shù)的名字將會(huì)被修飾,編譯器將函數(shù)的參數(shù)類型信息添加到修改后的名字中,因此當(dāng)相同函數(shù)名的函數(shù)擁有不用類型的參數(shù)時(shí),在g++編譯器看來是不同的函數(shù),而我們另一個(gè)模塊中想要調(diào)用這些函數(shù)也就必須使用C++的規(guī)則去鏈接函數(shù)(找修飾后的函數(shù)名)才能找到函數(shù)的地址。
C的編譯和鏈接方式
對(duì)于C程序,由于不支持重載,編譯時(shí)函數(shù)是未加任何修飾的,而且鏈接時(shí)也是去尋找未經(jīng)修飾的函數(shù)名。
C和C++直接混合編譯時(shí)的鏈接錯(cuò)誤
在C++程序,函數(shù)名是會(huì)被參數(shù)類型信息修飾的,這就造成了它們之間無法直接相互調(diào)用。
例如:
print(int)函數(shù),使用g++編譯時(shí)函數(shù)名會(huì)被修飾為 _Z5printi,而使用gcc編譯時(shí)函數(shù)名則仍然是print,如果直接在C++中調(diào)用使用C編譯規(guī)則的函數(shù),會(huì)鏈接錯(cuò)誤,因?yàn)樗鼤?huì)去尋找 _Z5printi而不是 print。
【C和C++的編譯和鏈接方式的不同】參考:
extern“C”的使用
extern "C"指令非常有用,因?yàn)镃和C++的近親關(guān)系。注意:extern "C"指令中的C,表示的一種編譯和連接規(guī)約,而不是一種語言。
并且extern "C"指令僅指定編譯和連接規(guī)約,并不影響語義,編譯時(shí)仍是一個(gè)C++的程序,遵循C++的類型檢查等規(guī)則。
對(duì)于下面的代碼它們之間是有區(qū)別的
extern "C" void Add(int a, int b); //指定Add函數(shù)應(yīng)該根據(jù)C的編譯和連接規(guī)約來鏈接 extern void Add(int a, int b); //聲明在Add是外部函數(shù),鏈接的時(shí)候去調(diào)用Add函數(shù)
如果有很多內(nèi)容要被加上extern “C”,你可以將它們放入extern “C”{ }中。
通過上面的分析,我們知道extern "C"的真實(shí)目的是實(shí)現(xiàn)類C和C++的混合編程,在C++源文件中的語句前面加上extern “C”,表明它按照類C的編譯和連接規(guī)約來編譯和連接,而不是C++的編譯的連接規(guī)約。這樣在類C的代碼中就可以調(diào)用C++的函數(shù)or變量等。
那么混合編譯首先要處理的問題就是要讓我們所寫的C++程序和C程序函數(shù)的編譯時(shí)的修飾規(guī)則和鏈接時(shí)的修飾規(guī)則保持一致。
總共就有下面四種情況,也就是說一個(gè)C的庫(kù),應(yīng)該能同時(shí)被C和C++調(diào)用,而一個(gè)C++的庫(kù)也應(yīng)能夠同時(shí)兼容C和C++。
為了展示如上四種情況,我們分別建立一個(gè)C靜態(tài)庫(kù)和C++靜態(tài)庫(kù)。
C程序調(diào)用C的庫(kù),C++程序調(diào)用C++的庫(kù),這是理所應(yīng)當(dāng)?shù)?,因此我們關(guān)注的問題是如何交叉調(diào)用。
用法舉例
靜態(tài)庫(kù)是什么
庫(kù)是寫好的現(xiàn)有的,成熟的,可以復(fù)用的代碼。現(xiàn)實(shí)中每個(gè)程序都要依賴很多基礎(chǔ)的底層庫(kù),不可能每個(gè)人的代碼都從零開始,因此庫(kù)的存在意義非同尋常
之所以稱為【靜態(tài)庫(kù)】,是因?yàn)樵阪溄与A段,會(huì)將匯編生成的目標(biāo)文件.o與引用到的庫(kù)一起鏈接打包到可執(zhí)行文件中。因此對(duì)應(yīng)的鏈接方式稱為靜態(tài)鏈接。
試想一下,靜態(tài)庫(kù)與匯編生成的目標(biāo)文件一起鏈接為可執(zhí)行文件,那么靜態(tài)庫(kù)必定跟.o文件格式相似。其實(shí)一個(gè)靜態(tài)庫(kù)可以簡(jiǎn)單看成是一組目標(biāo)文件(.o/.obj文件)的集合,即很多目標(biāo)文件經(jīng)過壓縮打包后形成的一個(gè)文件。靜態(tài)庫(kù)特點(diǎn)總結(jié):
- 靜態(tài)庫(kù)對(duì)函數(shù)庫(kù)的鏈接是放在編譯時(shí)期完成的。
- 程序在運(yùn)行時(shí)與函數(shù)庫(kù)再無瓜葛,移植方便。
- 浪費(fèi)空間和資源,因?yàn)樗邢嚓P(guān)的目標(biāo)文件與牽涉到的函數(shù)庫(kù)被鏈接合成一個(gè)可執(zhí)行文件。
靜態(tài)庫(kù)在程序編譯時(shí)會(huì)被連接到目標(biāo)代碼中,程序運(yùn)行時(shí)將不再需要該靜態(tài)庫(kù),因此體積較大
創(chuàng)建C靜態(tài)庫(kù)
我們以一個(gè)棧的靜態(tài)庫(kù)為例:
首先新建項(xiàng)目Stack_C
新建源文件和頭文件
寫好棧的代碼
注意一定是C程序,即源文件后綴為c
更改輸出文件類型
右鍵項(xiàng)目名稱—>屬性
更改為配置類型為靜態(tài)庫(kù)
生成靜態(tài)庫(kù)
查看是否生成成功
VS一般在項(xiàng)目路徑下的x64\Debug路徑下:
至此,靜態(tài)庫(kù)已經(jīng)可以成功建立了。
再新建一個(gè)項(xiàng)目,寫一個(gè)去調(diào)用該靜態(tài)庫(kù)實(shí)現(xiàn)的棧的程序(以括號(hào)匹配問題為例)
不過對(duì)于VS我們的靜態(tài)庫(kù)是默認(rèn)不去使用的,因此我們需要將靜態(tài)庫(kù)的路徑和庫(kù)的名稱分別添加到庫(kù)目錄和依賴項(xiàng),才能讓程序能去調(diào)用該靜態(tài)庫(kù)。
更改鏈接器配置
右鍵項(xiàng)目名—>點(diǎn)擊屬性
“屬性面板“—>”配置屬性”—> “鏈接器”—>”常規(guī)”,附加依賴庫(kù)目錄中輸入,靜態(tài)庫(kù)所在目錄;
增加庫(kù)目錄(路徑為我們剛剛生成的靜態(tài)庫(kù)所在的Debug文件夾)
增加附加依賴項(xiàng)
名稱為Stack_C項(xiàng)目生成的靜態(tài)庫(kù)名,一般是項(xiàng)目名 + .lib
“屬性面板”—>”配置屬性”—> “鏈接器”—>”輸入”,附加依賴庫(kù)中輸入靜態(tài)庫(kù)名StaticLibrary.lib。
我們先嘗試使用C程序來調(diào)用該靜態(tài)庫(kù)
新建項(xiàng)目
將源文件后綴改為c;包含上Stack_C項(xiàng)目(靜態(tài)庫(kù)項(xiàng)目)的頭文件;點(diǎn)擊生成解決方案;
成功生成,說明成功調(diào)用。
嘗試使用C++程序調(diào)用C靜態(tài)庫(kù)
- 將源文件后綴改為cpp;
- 頭文件保持不變;
- 點(diǎn)擊生成解決方法
結(jié)果報(bào)錯(cuò)了:
這說明在鏈接的過程中出現(xiàn)了問題,也就是在我們的程序找不到靜態(tài)庫(kù)中函數(shù)的地址,原因是我們的靜態(tài)庫(kù)是C語言的,沒有對(duì)函數(shù)進(jìn)行修飾,但在我們的調(diào)用方是C++程序,在鏈接過程中找的是修飾過的函數(shù)名,因此無法找到函數(shù)的地址。
既然C語言的靜態(tài)庫(kù)只能按照C的規(guī)則去編譯這些函數(shù)(即不修飾函數(shù)名),那么我們只要讓C++程序按照C語言的鏈接規(guī)則(即找未經(jīng)修飾的函數(shù)名)去找到函數(shù)名不就解決了?
兩種思路:
- 改變C庫(kù)的編譯和鏈接方式為C++規(guī)則;
- 改變C++程序調(diào)用庫(kù)函數(shù)的編譯和鏈接方式為C的規(guī)則;
方法1是不行的,因?yàn)镃語言中可沒有extern “C++”這種東西,那么考慮方法2;
這時(shí)我們可以借助extern“C”改變C++程序的鏈接規(guī)則,讓C++去按照C的規(guī)則去找函數(shù)名,即未經(jīng)過任何修飾的函數(shù)名,那就一定能找到函數(shù)的地址,來去正確調(diào)用靜態(tài)庫(kù)。
在源文件test.cpp
使用extern “C”,去改變包含的頭文件中的函數(shù)的鏈接規(guī)則
//調(diào)用庫(kù)的的模塊的頭文件包含 extern "C" { #include"..\..\Stack_C\Stack_C\stack.h" } //程序的代碼 //...
那么在test.cpp去鏈接函數(shù)時(shí),就會(huì)直接去找原函數(shù)名。
這樣就解決了。
還有一個(gè)一步到位的解決方法,利用條件編譯,根據(jù)當(dāng)前程序的類型,選擇是否去執(zhí)行extern “C”指令。
- 調(diào)用方是C程序,不做處理;
- 調(diào)用方是C++程序,需要使用extern“C”將程序改為C的鏈接規(guī)則;
//調(diào)用庫(kù)的的模塊的頭文件包含 #ifdef __cplusplus//如果是c++程序,就執(zhí)行extern “C”,使用C的鏈接方式,去找未經(jīng)修飾的函數(shù)名 extern "C"{ #endif #include"..\..\Stack_C\Stack_C\stack.h" #ifdef __cplusplus } #endif //程序的代碼 //...
但是這樣的處理不太好,我們作為調(diào)用方自然是想可以直接通過頭文件包含的方式去使用庫(kù)里的函數(shù),因此采用下列方法,更改庫(kù)的頭文件函數(shù)聲明為:
#ifdef __cplusplus//如果定義了宏__cplusplus就執(zhí)行#ifdef 到 #endif之間的語句 extern "C" { #endif void StackInit(struct Stack* s); void StackPush(struct Stack* s, DataType x); void StackPop(struct Stack* s); DataType StackTop(struct Stack* s); int StackSize(struct Stack* s); void StackDestory(struct Stack* s); bool StackEmpty(struct Stack* s); #ifdef __cplusplus } #endif
這樣的一段代碼,無論是C++程序還是C程序都可以直接#include就能去調(diào)用該靜態(tài)庫(kù)了。
創(chuàng)建C++靜態(tài)庫(kù)
步驟和創(chuàng)建C的靜態(tài)庫(kù)相同,只不過要將項(xiàng)目中的源文件后綴改為cpp,就會(huì)生成一個(gè)C++的靜態(tài)庫(kù),因此不再闡述。
創(chuàng)建完成后,我們?nèi)允褂脛倓偟捻?xiàng)目,并且添加C++靜態(tài)庫(kù)路徑到庫(kù)目錄,添加C++靜態(tài)庫(kù)名稱到附加依賴項(xiàng),仍然以括號(hào)匹配問題為例去調(diào)用該庫(kù)。(記得刪除C靜態(tài)庫(kù)的庫(kù)目錄和附加依賴項(xiàng),否則我們的程序有可能還會(huì)去調(diào)用C的靜態(tài)庫(kù),這樣我們就無法探究如何去調(diào)用C++靜態(tài)庫(kù)的問題了)
嘗試使用C程序調(diào)用C++靜態(tài)庫(kù)
我們不著急調(diào)用,經(jīng)過先前的經(jīng)驗(yàn),這里可以判斷,C++的程序去調(diào)用C++的庫(kù)一定是沒問題的,但是C程序就不好說了,因此我們要搞定C程序調(diào)用C++庫(kù)的情況,先搞清楚它們的差異:
這里的C++程序去調(diào)用函數(shù)是去尋找修飾后的函數(shù)名,C程序是去找未修飾的函數(shù)名,要想讓它們保持一致有兩個(gè)思路:
改變C程序的編譯和鏈方式為C++的規(guī)則;改變C++靜態(tài)庫(kù)的編譯方式為C的規(guī)則;
但是方法1是不行的,之前也說過,C語言中沒有extern “C++”這種東西,那么考慮方法2;
對(duì)庫(kù)的頭文件中的函數(shù)做如下處理:
//用C的規(guī)則去搞庫(kù)的編譯和鏈接方式 extern "C" { void StackInit(struct Stack* s); void StackPush(struct Stack* s, DataType x); void StackPop(struct Stack* s); DataType StackTop(struct Stack* s); int StackSize(struct Stack* s); void StackDestory(struct Stack* s); bool StackEmpty(struct Stack* s); }
那么現(xiàn)在C++的靜態(tài)庫(kù)的函數(shù)名都是沒有經(jīng)過修飾的。(C的規(guī)則)
但是我們?nèi)ゾ幾g仍然報(bào)錯(cuò):
error C2059: 語法錯(cuò)誤:“字符串”
"StackInit”未定義;假設(shè)外部返回int
“StackPush”未定義;假設(shè)外部返回int
“StackEmpty”未定義;假設(shè)外部返回int
“StackTop”未定義;假設(shè)外部返回int
“StackPop”未定義;假設(shè)外部返回int
這是因?yàn)槲覀兪褂肅程序時(shí)也包含了此頭文件,但是C語言中無法識(shí)別extern“C”,因此報(bào)錯(cuò)。
我們嘗試使用條件編譯來決定是否使用extern“C”,根據(jù)調(diào)用方的不同改變函數(shù)鏈接規(guī)則:
- 調(diào)用方是C++程序,那么需要使用extern“C”將C++程序的函數(shù)鏈接規(guī)則變?yōu)镃的;
- 調(diào)用方是C程序,不使用extern“C”語句;
因此我們做如下處理,將庫(kù)的頭文件中的函數(shù)聲明加上:
#ifdef __cplusplus//如果定義了宏__cplusplus就執(zhí)行#ifdef 到 #endif之間的語句 extern "C" { #endif void StackInit(struct Stack* s); void StackPush(struct Stack* s, DataType x); void StackPop(struct Stack* s); DataType StackTop(struct Stack* s); int StackSize(struct Stack* s); void StackDestory(struct Stack* s); bool StackEmpty(struct Stack* s); #ifdef __cplusplus } #endif
總結(jié):C++和C之間的混合編譯,為了消除函數(shù)名修飾規(guī)則不同的的差別,我們需要使用extern ”C“來改變C++的編譯和連接方式。
但這樣問題也隨之而來:
C++的庫(kù)就失去了函數(shù)重載的特性,如果庫(kù)中有同名函數(shù),那么就無法正確編譯,因?yàn)榘凑誄的方式去編譯,函數(shù)名會(huì)沖突。
如何解決這個(gè)問題呢?
實(shí)際上這個(gè)問題無法解決,一旦選擇了將某個(gè)函數(shù)指定了按照C的方式去編譯鏈接,那么這個(gè)函數(shù)就已經(jīng)失去了重載的特性了,不過Cpp的庫(kù)中未被指定按照C的規(guī)則去編譯和鏈接的那些函數(shù),仍然可以被重載,并且具有C++的一切特性。
因此這個(gè)問題無解,只有通過避免“一刀切”的方法來保護(hù)那些我們想重載的函數(shù),也就是說一部分庫(kù)里的函數(shù)那就是實(shí)現(xiàn)給C程序調(diào)用的,我們就通過extern“C”改變它的編譯和鏈接方式,而對(duì)于那些實(shí)現(xiàn)給C++程序調(diào)用的函數(shù)接口,我們不做任何處理,并且不暴露給C程序。
想要實(shí)現(xiàn)上述過程,我們需要在靜態(tài)庫(kù)項(xiàng)目中創(chuàng)建兩個(gè)頭文件libc.h
和libcpp.h
,libc.h
聲明那些需要暴露給C程序的函數(shù)接口,并且使用上面介紹的條件編譯和extern“C”,libcpp.h
聲明那些暴露給給Cpp程序的函數(shù)接口,這樣兩個(gè)頭文件的函數(shù)的鏈接規(guī)范互不相同,也互不干擾。只需要將lic.h
在C程序調(diào)用的地方使用#include
包含,libcpp.h
在C++程序調(diào)用的地方使用#include
包含即可使用。
因此C++庫(kù)中哪個(gè)接口需要暴露給C,我們就用extern“C”修飾哪個(gè)接口。
總之,C的庫(kù)可以給C程序和C++程序調(diào)用,而C++庫(kù)也可以被C程序和C++程序調(diào)用
如果要滿足這個(gè)庫(kù)中所有的函數(shù)都能同時(shí)被C++和C調(diào)用,那么無論是C的庫(kù)還是C++的庫(kù),最終這個(gè)庫(kù)的編譯和鏈接方式都只能是C的規(guī)范,因?yàn)镃++可以使用C的鏈接規(guī)范但是C不能使用C++的鏈接規(guī)范,也就導(dǎo)致了如果庫(kù)的鏈接規(guī)范是C++的,那么無論如何,C程序都無法調(diào)用。
值得一提的是C++程序中的函數(shù)可以使用兩種鏈接規(guī)范,因此我們可以針對(duì)函數(shù)的使用場(chǎng)景來選擇該函數(shù)的編譯和鏈接規(guī)范,使得一部分函數(shù)保留C++的特性,但一部分函數(shù)就只能為了兼容C而犧牲C++的特性,想要既兼容C又保留C++的特性,這是做不到的。
到此這篇關(guān)于C++和C的混合編譯的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)C++和C混合編譯內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言實(shí)現(xiàn)用戶態(tài)線程庫(kù)案例
下面小編就為大家?guī)硪黄狢語言實(shí)現(xiàn)用戶態(tài)線程庫(kù)案例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05VS2019編寫C程序或者CUDA程序出現(xiàn)“無法啟動(dòng)程序,系統(tǒng)找不到指定的文件”問題的詳細(xì)解決方法
這篇文章主要介紹了VS2019編寫C程序或者CUDA程序出現(xiàn)“無法啟動(dòng)程序,系統(tǒng)找不到指定的文件”問題的詳細(xì)解決方法,文中通過圖文的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08關(guān)于C++友元函數(shù)的實(shí)現(xiàn)講解
今天小編就為大家分享一篇關(guān)于關(guān)于C++友元函數(shù)的實(shí)現(xiàn)講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12