日韩性视频-久久久蜜桃-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网站 | 不卡成人 | 精品国产乱码久久久久久图片 | 久久国产精品久久国产精品 | 海角社区id | 天天摸日日干 | 欧美一区二区三区爽爽爽 | 欧美日韩中文字幕在线观看 | 成人黄色在线免费观看 | 69色堂 | 一区二区三区精品 | 久久sp| 国产激情第一页 | 在线观看免费高清在线观看 | 伊人色综合网 | 日韩黄色精品 | 巨乳美女被爆操 | 亚洲精视频 | 天堂8在线天堂资源bt | 大地资源在线观看免费高清版粤语 | 台湾性生生活1 | 欧美少妇bbw | 性欧美丰满熟妇xxxx性仙踪林 | 97视频总站| 国产蜜臀av一区二区 | 亚洲综合社区 | 亚洲成人免费网站 | 人人爽人人做 | 亚洲在线资源 | 涩色网| 初尝人妻少妇中文字幕 | 午夜激情网址 | 久久久夜夜夜 | 国产高清在线观看 | 正在播放av| 青青草91久久久久久久久 | 欧美特级一级片 | 欧美多人猛交狂配 | 人妻熟女一区二区aⅴ水 | 精品免费久久久 | 67194少妇在线观看 | 乌克兰黄色片 | 久久综合九色综合网站 | 久久成人免费网站 | 男裸体无遮挡网站 | 国产精品果冻传媒 | 国产毛片18| 青草精品视频 | 日日日夜夜操 | 91午夜免费视频 | 波多野结衣小视频 | 成人久久久久久久 | 国语一区二区 | 豆豆色成人网 | 国产稀缺真实呦乱在线 | 免费 成 人 黄 色 | 精品久操 | www.四虎精品 | 久草色在线 | 澳门免费av | 国产精品香蕉国产 | 揉我啊嗯~喷水了h视频 | 日产精品久久久久久久蜜臀 | 免费视频福利 | 久久大尺度 | 视色视频在线观看 | juliaann精品艳妇hd| 欧美三级网站 | 成人高清在线观看 | 成人av视屏 | 久久嫩草视频 | 黄色一级片网站 | 美日韩视频 | 在线免费视频 | 在线天堂中文 | 欧美日韩中文国产 | 伊人久久久久久久久久久 | 激情视频区| 黄色网免费观看 | 中文成人无字幕乱码精品区 | 最新av在线播放 | 国产专区在线播放 | 色网站免费 | 你懂的在线观看视频 | 欧美精品国产精品 | 嫩草嫩草嫩草嫩草嫩草嫩草 | 国产寡妇色xxⅹ交肉视频 | 九九热8| 91少妇丨porny丨 | 国产精品精品国产色婷婷 | 国产精品久久婷婷六月丁香 | 亚洲成人自拍网 | 久久永久免费 | 欧美浓毛大泬视频 |