c++起始(名詞修飾,extern “C” ,引用)

名字修飾(name Mangling)

在C/C++中,一個程序要運行起來,需要經歷以下幾個階段:預處理、編譯、匯編、鏈接。

Name Mangling是一種在編譯過程中,將函數、變量的名稱重新改編的機制,簡單來說就是編譯器為了區分各 個函數,將函數通過某種算法,重新修飾為一個全局唯一的名稱。
C語言的名字修飾規則非常簡單,只是在函數名字前面添加了下劃線。比如,對于以下代碼,在后鏈接時就 會出錯:

int Add(int left, int right);int main() 
{ Add(1, 2); 
return ;
}

編譯器報錯:error LNK2019: 無法解析的外部符號 _Add,該符號在函數 _main 中被引用。
上述Add函數只給了聲明沒有給定義,因此在鏈接時就會報錯,提示:在main函數中引用的Add函數找不到函 數體。從報錯結果中可以看到,C語言只是簡單的在函數名前添加下劃線。因此當工程中存在相同函數名的函 數時,就會產生沖突。
具體例子看上篇博客有詳細介紹。
由于C++要支持函數重載,命名空間等,使得其修飾規則比較復雜,不同編譯器在底層的實現方式可能都有差 異。

int Add(int left, int right);int main() { Add(1, 2); return 0; }
int Add(int left, int right); double Add(double left, double right);int main() { Add(1, 2); Add(1.0, 2.0); return 0; }

在vs下,對上述代碼進行編譯鏈接,后編譯器報錯:
error LNK2019: 無法解析的外部符號 “double cdecl Add(double,double)” (?Add@@YANNN@Z) error LNK2019: 無法解析的外部符號 “int __cdecl Add(int,int)” (?Add@@YAHHH@Z)

通過上述錯誤可以看出,編譯器實際在底層使用的不是Add名字,而是被重新修飾過的一個比較復雜的名字, 被重新修飾后的名字中包含了:函數的名字以及參數類型。這就是為什么函數重載中幾個同名函數要求其參數 列表不同的原因。只要參數列表不同,編譯器在編譯時通過對函數名字進行重新修飾,將參數類型包含在終 的名字中,就可保證名字在底層的全局唯一性。

在這里插入圖片描述

extern “C”

c++的函數庫,c語言中并不能直接用。
有時候在C++工程中可能需要將某些函數按照C的風格來編譯,在函數前加extern “C”,意思是告訴編譯器,將 該函數按照C語言規則來編譯

extern "C" int Add(int left, int right);int main(){    Add(1,2);  return 0;}

鏈接時報錯:error LNK2019: 無法解析的外部符號_Add,該符號在函數 _main 中被引用
而不是c++中的報錯。

引用

在之前的學習中,我們傳參數的兩種方式分別為,傳值和傳地址。
在這里插入圖片描述
有沒有什么方法能夠將讓傳值的方式也能夠起到交換的作用?
引用不是新定義一個變量,而是給已存在變量取了一個別名,編譯器不會為引用變量開辟內存空間,它和它引 用的變量共用同一塊內存空間。

使用方法

類型& 引用變量名(對象名) = 引用實體;
在這里插入圖片描述
注意:引用類型必須和引用實體是同種類型的

引用特性

  1. 引用在定義時必須初始化
  2. 一個變量可以有多個引用
  3. 引用一旦引用一個實體,再不能引用其他實體

在這里插入圖片描述

常引用

在這里插入圖片描述
在這里插入圖片描述
因為10本身是常量,修改不了

在這里插入圖片描述
類型不同

使用場景

  1. 做參數

    void Swap(int& left, int& right)
    { int temp = left;
    left = right;
    right = temp
    }

  2. 做返回值

    int& TestRefReturn(int& a)
    { a += 10;
    return a;
    }

int& Add(int a, int b) 
{    int c = a + b;    return c; }int main() {   int& ret = Add(1, 2);  Add(3, 4);  cout << "Add(1, 2) is :"<< ret <<endl;   return 0; }

注意:如果函數返回時,離開函數作用域后,其棧上空間已經還給系統,因此不能用棧上的空間作為引用類型 返回。如果以引用類型返回,返回值的生命周期必須不受函數的限制(即比函數生命周期長)。

具體參考https://blog.csdn.net/qq_40550018/article/details/81225519函數棧幀
在這里插入圖片描述

傳值、傳引用效率比較

以值作為參數或者返回值類型,在傳參和返回期間,函數不會直接傳遞實參或者將變量本身直接返回,而是傳遞實 參或者返回變量的一份臨時的拷貝,因此用值作為參數或者返回值類型,效率是非常低下的,尤其是當參數或者返回 值類型非常大時,效率就更低

#include <time.h> struct A {    int a[10000]; };void TestFunc1(A a) {}void TestFunc2(A& a) {}void TestRefAndValue() {  A a;// 以值作為函數參數   size_t begin1 = clock();   for (size_t i = 0; i < 10000; ++i)       TestFunc1(a);    size_t end1 = clock();// 以引用作為函數參數   size_t begin2 = clock();  for (size_t i = 0; i < 10000; ++i)   TestFunc2(a);    size_t end2 = clock();// 分別計算兩個函數運行結束后的時間cout << "TestFunc1(int*)-time:" << end1 - begin1 << endl;   cout << "TestFunc2(int&)-time:" << end2 - begin2 << endl;}// 運行多次,檢測值和引用在傳參方面的效率區別 
int main(){    for (int i = 0; i < 10; ++i)  {      TestRefAndValue();    }          return 0; }

值和引用的作為返回值類型的性能比較

#include <time.h> struct A {    int a[10000]; };A a;A TestFunc1() {    return a; }A& TestFunc2() {    return a; }void TestReturnByRefOrValue() {   // 以值作為函數的返回值類型   size_t begin1 = clock();   for (size_t i = 0; i < 100000; ++i)    TestFunc1();    size_t end1 = clock();// 以引用作為函數的返回值類型    size_t begin2 = clock(); for (size_t i = 0; i < 100000; ++i)      TestFunc2();    size_t end2 = clock();// 計算兩個函數運算完成之后的時間   cout << "TestFunc1 time:" << end1 - begin1 << endl; cout << "TestFunc2 time:" << end2 - begin2 << endl; } 

通過上述代碼的比較,發現傳值和指針在作為傳參以及返回值類型上效率相差很大。

引用和指針的區別

在語法概念上引用就是一個別名,沒有獨立空間,和其引用實體共用同一塊空間。

int main() {int a = 10; int& ra = a;cout<<"&a = "<<&a<<endl; cout<<"&ra = "<<&ra<<endl;return 0; }

在底層實現上實際是有空間的,因為引用是按照指針方式來實現的

int main() { 
int a = 10;int& ra = a;ra = 20;int* pa = &a;*pa = 20;return 0;}

我們來看下引用和指針的匯編代碼對比:
在這里插入圖片描述
引用和指針的不同點:

  1. 引用在定義時必須初始化,指針沒有要求
  2. 引用在初始化時引用一個實體后,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型實 體
  3. 沒有NULL引用,但有NULL指針
  4. 在sizeof中含義不同:引用結果為引用類型的大小,但指針始終是地址空間所占字節個數(32位平臺下占4 個字節)
  5. 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小
  6. 有多級指針,但是沒有多級引用
  7. 訪問實體方式不同,指針需要顯式解引用,引用編譯器自己處理 8. 引用比指針使用起來相對更安全

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383358.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383358.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383358.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux進程間通信方式--本地socket

先上一個代碼 服務端&#xff1a; [cpp] view plaincopy //s_unix.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #define UNIX_DOMAIN "/tmp/UNIX.domain" int main(void) { so…

extern和static的區別

c語言中的 static&#xff1a; 修飾局部變量&#xff1a;存放在靜態數據區&#xff0c;生命周期位整個程序結束&#xff0c;但作用于仍為函數局部。 修飾全局變量&#xff1a;無法被同一工程其他源文件訪問。 修飾函數&#xff1a;與全局變量類似。 extern&#xff1a; 可被…

RT5350原廠SDK及AP移植步驟詳解

最近想搞一下rt5350&#xff0c;所以找了個原廠的SDK包進行了編譯&#xff0c;很快路由器就可以用了&#xff0c;把我的編譯操作步驟寫了下分享給更多的愛好者&#xff0c;供大家參靠&#xff0c;下一步準備移植攝像頭玩玩。有興趣的可以一起交流。 RT5350移植Toolchain工具的安…

linux系統編程之進程概念(操作系統---管理,進程創建,進程狀態,進程優先級, 環境變量,程序地址空間,進程O(1)調度方法)

系統編程&#xff1a; 進程概念->進程控制->基礎IO->進程間通信->進程信號->多線程進程概念 馮諾依曼體系結構----現代計算機硬件體系結構 馮諾依曼體系結構----現代計算機硬件體系結構 計算機五大硬件單元&#xff1a;輸入設備&#xff1a;鍵盤輸出設備&#…

Make Menuconfig詳解 (配置內核選擇)

Make Menuconfig簡介 make menuconfig 圖形化的內核配置make mrproper -----刪除不必要的文件和目錄. #make config&#xff08;基于文本的最為傳統的配置界面&#xff0c;不推薦使用&#xff09; #make menuconfig&#xff08;基于文本選單的配置界面&#xff0c;字符終端下…

Linux系統編程之進程控制(進程創建,fork函數,進程中止,進程等待,程序替換)

進程創建 fork()------復制&#xff0c;返回值&#xff0c;寫時復制 vfork()創建子進程—子進程與父進程共用同一塊虛擬地址空間&#xff0c; 為了防止調用棧混亂&#xff0c;因此阻塞父進程直到子進程調用exit&#xff08;&#xff09;退出或者進行程序替換 vfork創建的子…

Linux內核配置系統淺析

隨著 Linux 操作系統的廣泛應用&#xff0c;特別是 Linux 在嵌入式領域的發展&#xff0c;越來越多的人開始投身到 Linux 內核級的開發中。面對日益龐大的 Linux 內核源代碼&#xff0c;開發者在完成自己的內核代碼后&#xff0c;都將面臨著同樣的問題&#xff0c;即如何將源代…

Linux系統編程下做一個簡易的shell

自主實現一個shell--------minshell shell&#xff1a;命令行解釋器-------解釋執行用戶的輸入&#xff08;完成相對應的功能&#xff09; 步驟 1. 獲取標準輸入中的字符串 2. 對字符串進行解析[ls -l -a][ls ] [-l ] [-a] 3. 創建子進程 4. 子進程中進行程序替換 5. 父進程…

C++起始(內聯函數,宏的優缺點,const關鍵字,auto關鍵字(C++11)基于范圍的for循環(C++11). 指針空值nullptr(C++11))

內聯函數 概念 以inline修飾的函數叫做內聯函數&#xff0c;編譯時C編譯器會在調用內聯函數的地方展開&#xff0c;沒有函數壓棧的開銷&#xff0c; 內聯函數提升程序運行的效率 函數前增加inline關鍵字將其改成內聯函數&#xff0c;在編譯期間編譯器會用函數體替換函數的調用…

linux內核中的匯編語言

在Linux內核代碼中&#xff0c;有一部分是用匯編語言編寫的。其大部分是關于中斷與異常處理的底層程序&#xff0c;還有就是與初始化有關的程序&#xff0c;以及一些核心代碼中調用的公用子程序。 用匯編語言編寫內核代碼中的部分代碼&#xff0c;大體上是出于如下幾個方面考慮…

數據結構課程設計---c語言實現通訊錄(動態擴容+文件存儲)

1 題目一 &#xff1a; 通訊錄 1.1問題描述 編寫一個通訊錄管理系統&#xff0c;以把所學數據結構知識應用到實際軟件開發中去。每條信息至包含 &#xff1a;姓名&#xff08;NAME &#xff09;街道&#xff08;STREET&#xff09;城市&#xff08;CITY&#xff09;郵編&#…

linux內核panic

1. Linux Kernel Panic的產生的原因 panic是英文中是驚慌的意思&#xff0c;Linux Kernel panic正如其名&#xff0c;linux kernel不知道如何走了&#xff0c;它會盡可能把它此時能獲取的全部信息都打印出來。 有兩種主要類型kernel panic&#xff0c;后面會對這兩類panic做詳細…

數據結構課程設計------c實現散列表(二次探測再哈希)電話簿(文件存儲)

題目二 &#xff1a;散列表的設計與實現 2.1問題描述 設計散列表實現電話號碼查找系統&#xff0c;使得平均查找長度不超過2基本要求 &#xff08;1&#xff09;設每個記錄有下列數據項&#xff1a;電話號碼、用戶名、地址&#xff1b; &#xff08;2&#xff09;從鍵盤輸入各…

科技論文----論搜索引擎現狀及發展趨勢

搜索引擎現狀及發展趨勢 【摘要】 隨著最近10年中國互聯網的快速發展菜互聯網已經徹底改變了人們的生活方式&#xff0c;而在互聯網的發展過程中。搜索引擎發揮了巨大的推動作用。本文對搜索引擎的發展歷史采用的技術&#xff0c;發展現狀出現的問題以及未來發展方向進行了綜述…

inittab文件格式

/etc/inittab文件是Linux系統第一個進程init的配置文件。其每個記錄占一行&#xff0c;每行最多512個字符。該文件的每個記錄的格式為&#xff1a; id:runlevel:action:process 其中&#xff0c;id是一個不超過4個字符的標識&#xff0c;用來唯一標識一條記錄。runlevel表明該條…

數據結構課程設計------掃雷游戲(升級版,可展開)

本程序由團隊中的一個人所寫&#xff0c;本人看懂并寫下此文章 題目&#xff1a;掃雷 3.1問題描述 掃雷游戲 [基本要求] &#xff08;1&#xff09;完成棋盤的初始化并在標準顯示器中顯示 &#xff08;2&#xff09;通過輸入行列值確定用戶輸入 &#xff08;3&#xff09;游…

C語言的編譯鏈接過程的介紹

發布時間: 2012-11-08 10:17 作者: 未知 來源: 51Testing軟件測試網采編 字體: 小 中 大 | 上一篇 下一篇 | 打印 | 我要投稿 | 推薦標簽&#xff1a; DotNet 軟件開發 | 感言十年 C語言的編譯鏈接過程要把我們編寫的一個c程序&#xff08;源代碼&#x…

vs2013鏈接Mysql時出現 (由于找不到libmysql.dll,無法繼續執行代碼。重新安裝程序可能會解決此問題)

將MySQL安裝目錄下的lib文件夾中 的libmysql.dll文件拷貝到C:\Windows\System32目錄下即可

gcc 優化選項 -O1 -O2 -O3 -Os 優先級,-fomit-frame-pointer

少優化->多優化&#xff1a; O0 -->> O1 -->> O2 -->> O3 -O0表示沒有優化,-O1為缺省值&#xff0c;-O3優化級別最高 英文解析&#xff1a; -O -O1 Optimize. Optimizing compilation takes somewhat more time, an…

const 和 #define 區別總結

const有類型&#xff0c;可進行編譯器安全檢查&#xff0c;#define 無類型&#xff0c;不可進行類型檢查const 有作用域&#xff0c;而#define 不重視作用域&#xff0c;默認定義在指定作用域下有效的常量&#xff0c;那么#define 就不能用&#xff08;可以用#undef結束宏定義生…