日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++编译链接的那些小事

發(fā)布時間:2025/3/15 c/c++ 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++编译链接的那些小事 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文轉(zhuǎn)載,尊重原創(chuàng)!受益良多!點擊打開鏈接


最近,有同事向我多次問及C++關于編譯鏈接方面的問題,包括如下:

1:什么樣的函數(shù)以及變量可以定義在頭文件中

2:extern "C"的作用

3:防止重復包含的宏的作用

4:函數(shù)之間是怎么鏈接起來的

我認為,這些問題不難,書上基本上都有,但要是沒有真正思考過,就憑死記硬背,也就是只能“嘴上說說”而已,遇到問題還真棘手,所以我覺得有必要說一下。


C/C++的編譯鏈接過程

其實,“編譯”這個詞大多數(shù)時候,我們指的是由一堆.h,.c,.cpp文件生成鏈接庫或者可執(zhí)行文件的過程。但是拿C/C++來說,其實這是很模糊的,由一堆C/C++文件生成應用程序包括預處理---編譯文件---鏈接(寫的比較粗糙,不影響本文論述)。

首先,要明白什么是編譯單元,一個編譯單元可以認為是一個.c或者.cpp文件,每一個編譯單元首先會經(jīng)過預處理得到一個臨時的編譯單元,這里稱為tmp.cpp,預處理會把.c或者.cpp直接或者間接包含的其它文件(不只局限于.h文件,只要是#include即可)的內(nèi)容替換進來,并展開宏調(diào)用等。

下面首先看一個例子:

a.h

#ifndef A_H_ #define A_H_ static int a = 1; void fun(); #endif

a.cpp

#include "a.h"static void hello_world() { }

只有a.h和a.cpp這兩個文件,及其簡單。首先通過g++的-E參數(shù)得到a.cpp預處理之后的內(nèi)容

coderchen@coderchen:~/c++$ g++ -E a.cpp > tmp.cpp

查看tmp.cpp

# 1 "a.cpp" # 1 "<built-in>" # 1 "<command-line>" # 1 "a.cpp" # 1 "a.h" 1static int a = 1; void fun(); # 2 "a.cpp" 2static void hello_world() { }tmp.cpp就是只經(jīng)過預處理得到的文件,這個文件才是編譯器能夠真正看到的文件。這個過程就是 預處理。

其中#define A_H_的作用是防止重復包含a.h這個頭文件,很多人都知道這一點,但是再仔細問,我見過大多數(shù)人都說不清楚。

這種宏是為了防止一個編譯單元(cpp文件)重復包含同一個頭文件。它在預處理階段起作用,預處理器發(fā)現(xiàn)a.cpp內(nèi)已經(jīng)定義過A_H_這個宏的話,在a.cpp中再次發(fā)現(xiàn)#include "a.h"的時候就不會把a.h的內(nèi)容替換進a.cpp了。
編譯器看到tmp.cpp的時候,會編譯成一個obj文件,最后由鏈接器對這一個對obj文件進行鏈接,從而得到可執(zhí)行程序。


編譯錯誤和連接錯誤

編譯錯誤指的是一個cpp編譯單元在編譯時發(fā)生的錯誤,這種錯誤一般都是語法錯誤,拼寫錯誤,參數(shù)不匹配等。

以main.cpp為例(只有一個main函數(shù))

int main() { hello_world(); }

編譯(加-c參數(shù)表示只編譯不鏈接)

coderchen@coderchen:~/c++$ g++ -c -o main.o main.cpp main.cpp: In function ‘int main()’: main.cpp:4: error: ‘hello_world’ was not declared in this scope

這種錯誤就是編譯,原因是hello_world函數(shù)未聲明,把void hello_world();這條語句加到main函數(shù)前面,再次編譯

coderchen@coderchen:~/c++$ g++ -c -o main.o main.cpp coderchen@coderchen:~/c++$

編譯成功,雖然我們調(diào)用了hello_world函數(shù),卻沒有定義這個函數(shù)。好,接下來,我們把這個main.o文件鏈接下,

coderchen@coderchen:~/c++$ g++ -o main main.o main.o: In function `main': main.cpp:(.text+0x7): undefined reference to `hello_world()' collect2: ld returned 1 exit status

看到了吧,鏈接器ld報出了鏈接錯誤,原因是hello_world這個函數(shù)找不到。這個例子很簡單,基本上可以區(qū)分出編譯錯誤和鏈接錯誤。我們再添加一個hello_world.cpp

void hello_world() { }

編譯


coderchen@coderchen:~/c++$ g++ -c -o hello_world.o hello_world.cpp

鏈接

coderchen@coderchen:~/c++之所以$ g++ -o main main.o hello_world.ook,我們的main程序已經(jīng)生成了,我們經(jīng)歷了預處理---編譯---鏈接的過程。

有的人說為什么不需要寫一個hello_world.h的頭文件,聲明hello_world函數(shù),然后再讓main.cpp包含hello_world.h呢?這樣寫自然是標準的做法,不過預處理過后,和我們現(xiàn)在寫的一樣的,預處理會把hello_world.h的內(nèi)容替換到main.cpp中。


問題:在鏈接的時候,main.o怎么知道hello_world函數(shù)定義在hello_world.o中呢?

答案:main.o不知道hello_world函數(shù)定義在那個obj文件中,每個obj文件都有一個導出符號表,對于這個例子,hello_world.o的導出符號表中有hello_world這個函數(shù),而main.o需要用到這個函數(shù),可以想象就像幾個插槽一樣。鏈接器通過掃描obj文件發(fā)現(xiàn)這個函數(shù)定義在hello_world.o中,然后就可以鏈接了。

問題:為什么函數(shù)不能定義在頭文件中?

這個問題是不恰當?shù)?#xff0c;因為用inline和static修飾的函數(shù)可以定義在頭文件中,而inline修飾的函數(shù)必須定義在頭文件中。

如果函數(shù)定義在頭文件中,并且有多個cpp文件都包含了這個頭文件的話,那么這些cpp文件生成的obj文件的導出符號表中都有這個頭文件中定義的函數(shù),單文件編譯的時候是不會出錯的,但是鏈接的時候就會報錯。鏈接器發(fā)現(xiàn)了多個函數(shù)實體,但卻無法確定應該使用哪一個。這是一個鏈接錯誤。

inline修飾的函數(shù),通常都不會存在函數(shù)實體,即便編譯器沒有對其內(nèi)聯(lián),那么obj文件也不會導出inline函數(shù),所以鏈接不會出錯。

static修飾的函數(shù),只能由定義它的編譯單元調(diào)用,也不會導出。如果頭文件中頂一個static修飾的函數(shù),就相當于多個obj文件中都頂一個了一個一模一樣的函數(shù),大家各用各的,互補干擾。

問題:什么樣的變量可以定義在頭文件中?

其實變量于函數(shù)很類似,由static或const修飾的變量可以定義在頭文件中。

static修飾的變量于static修飾的函數(shù)一樣,道理同上。

const修飾的變量默認是不會進入導出符號表的,相當于每個obj中都定義了一個一模一樣的const變量,各用各的。而const可以再用extern修飾,如果用extern const修飾的變量定義在頭文件中,那么就會出現(xiàn)鏈接錯誤,原因就是“想一想extern是干嘛的”

問題:extern "C"是干嘛的?

如果有人回答“兼容C和C++”,我只能說“這是一個正確答案,但我不知道你是否真的知道”。

首先要知道C不支持重載,C++支持重載,C++為了支持重載,引入了函數(shù)重命名的機制,就像下面這樣:

int hello_world(type1 param); int hello_world(type2 param);
通常第一個函數(shù)會被編譯成hello_world_type1這樣子,第二個函數(shù)會被編譯成hello_world_type2這樣子。 不管是定義的地方還是調(diào)用的地方,都會把函數(shù)改成同樣的名字,所以鏈接器可以正確的找到函數(shù)實體。

而我們寫C++程序的時候,通常會引入由c編寫的庫(gcc編譯的c文件),而c不支持重載,自然不會對函數(shù)重命名。而我們在C++中調(diào)用的地方很可能會重命名,這就造成了調(diào)用的地方(C++編譯)和定義的地方(C編譯)函數(shù)名不一致的情況,這也是一種鏈接錯誤。

所以我們經(jīng)常會看到在C++中用extern "C" { #include "some_c.h" }這種代碼。這就是告訴c++編譯器,some_c.h中的函數(shù)要按照c的方式編譯,不要重命名,這樣在鏈接的時候就ok了。











總結(jié)

以上是生活随笔為你收集整理的C++编译链接的那些小事的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 看av网址| 亚洲精品国产电影 | 91污片| 处女朱莉第一次 | 中国超碰 | 天天操综合 | 欧美熟妇精品一区二区蜜桃视频 | 午夜骚影| 特级av片| 色大师在线观看 | 国产一区二区黄 | 久久中文字幕在线观看 | 精品人妻一区二区三区在线视频 | 国产精品色图 | 欧洲在线观看 | 久色网 | 日韩午夜伦 | 国产色呦呦 | 欧美日韩五月天 | 青青草视频在线看 | 女人18毛片水真多 | 欧美国产日韩精品 | 91porny丨首页入口在线 | 日本天堂影院 | 日韩高清在线观看一区 | 处女朱莉 | 另类小说色综合 | 亚洲免费一级 | 欧美三级韩国三级日本三斤 | 91视频色| 欧美区视频 | 97超碰资源总站 | 无码国模国产在线观看 | 亚洲三级电影网站 | 色老板精品凹凸在线视频观看 | 中文字幕av亚洲精品一部二部 | 亚洲精品在线观 | 一级片大片 | 亚洲福利视频网 | 欧美午夜精品久久久 | 高潮一区二区三区 | 久草视频在线观 | 丝袜+亚洲+另类+欧美+变态 | 黑森林av导航| 久久伊人色 | 日本免费a视频 | www日本色| 国产盗摄在线观看 | 成人午夜sm精品久久久久久久 | 国产精品久久网站 | 日本一二三区视频 | 麻豆视频在线观看免费网站黄 | 狂野欧美性猛交免费视频 | 亚洲精品久久久久国产 | 免费成人深夜夜国外 | 一区二区三区成人 | 午夜偷拍福利视频 | 丁香六月激情综合 | 先锋av资源网 | 美女裸体网站久久久 | 成人黄色电影网址 | 91精产国品一二三区在线观看 | 中文字幕一区二区三区精华液 | 日日操天天操夜夜操 | 亚洲视频在线视频 | 污污在线免费观看 | 欧美大片免费观看网址 | 美女av一区二区 | 久久久久久久久久久久久久久久久久 | 青青视频一区二区 | 极品少妇xxx| 国产精品麻豆果冻传媒在线播放 | 久久ww| 久草福利网 | 午夜av导航 | 老局长的粗大高h | 欧美精品大片 | 岛国福利视频 | 久草视频在线资源 | 在线免费观看污片 | 欧美日韩一级二级三级 | 一区二区三区av夏目彩春 | 天天干夜夜夜夜 | 色中文在线 | 日本欧美国产 | 中文字幕中出 | 中国美女一级黄色片 | 免费精品视频在线观看 | 日本黄色片.| 扒下小娇妻的内裤打屁股 | 国产婷婷| 精品在线视频一区二区 | 久久久精品视频免费 | 国产传媒视频在线观看 | 天堂视频在线免费观看 | 午夜男人的天堂 | 免费国产在线观看 | 国产在线欧美在线 | 日韩 欧美 亚洲 |