深入理解extern用法

from:https://blog.csdn.net/z702143700/article/details/46805241

一、 extern做變量聲明

l? 聲明extern關鍵字的全局變量函數可以使得它們能夠跨文件被訪問

我們一般把所有的全局變量和全局函數的實現都放在一個*.cpp文件里面,然后用一個同名的*.h文件包含所有的函數和變量的聲明。如:

[cpp]?view plaincopy
  1. /*Demo.h*/??
  2. #pragma??once??
  3. extern?int a;??
  4. extern?int b;??
  5. int add(int a,int b);??
[cpp]?view plaincopy
  1. /*Demo.cpp*/??
  2. #include?"Demo.h"?/*這句話寫或者不寫在本例中都行,不過建議不寫*/??
  3. /*不寫不會出問題,寫了有些情況下會出問題,下面有解釋*/??
  4. int?a?=10;??
  5. int?b?=20;??
  6. ???
  7. int?add(intl,intr)??
  8. {??
  9. ??????return?l?+r;??
  10. }??

如果將Demo.cpp寫成了Demo.c,編譯器會告訴你說無法解析的外部符號。

因為Demo.c里面的實現會被C編譯器處理,然而C++和C編譯器在編譯函數時存在差異,所以會存在找不到函數的情況。

?

l? 全局函數的聲明語句中,關鍵字extern可以省略,因為全局函數默認是extern類型的。

l 聲明和定義

extern?int?a; //屬于聲明??extern?int?a = 10; //屬于定義,同下

extern?char?g_str[]="123456";//這個時候相當于沒有extern

如果在一個文件里定義了char g_str[] = "123456";在另外一個文件中必須使用extern char g_str[ ];來聲明。不能使用extern char* g_str;來聲明。extern是嚴格的聲明。且extern char* g_str只是聲明的一個全局字符指針。

注:聲明可以拷貝n次,但是定義只能定義一次。

二、extern “C”

l? extern "C" 包含雙重含義,從字面上即可得到:首先,被它修飾的目標是“extern”的;其次,被它修飾的目標是“C”的。

被extern "C"限定的函數或變量是extern類型的:

extern是C/C++語言中表明函數和全局變量作用范圍(可見性)的關鍵字,該關鍵字告訴編譯器,其聲明的函數和變量可以在本模塊其它模塊中使用。記住,下列語句:

extern int a;

僅僅是一個變量的聲明,其并不是在定義變量a,并未為a分配內存空間。變量a在所有模塊中作為一種全局變量只能被定義一次,否則會出現連接錯誤。

通常,在模塊的頭文件中對本模塊提供給其它模塊引用的函數和全局變量以關鍵字extern聲明。

例如,如果模塊B欲引用該模塊A中定義的全局變量和函數時只需包含模塊A的頭文件即可。這樣,模塊B中調用模塊A中的函數時,在編譯階段,模塊B雖然找不到該函數,但是并不會報錯;它會在連接階段中從模塊A編譯生成的目標代碼中找到此函數。

與extern對應的關鍵字是static,被它修飾的全局變量和函數只能在本模塊中使用。因此,一個函數或變量只可能被本模塊使用時,其不可能被extern “C”修飾。

實現C++與C及其它語言的混合編程:

參考:https://blog.csdn.net/gobitan/article/details/1532769

被extern"C"修飾的變量和函數是按照C語言方式編譯和連接的,未加extern “C”則按照聲明時的編譯方式。

l? extern "C"的慣用法

(1)“C++使用C在C++中引用C語言中的函數和變量,在包含C語言頭文件(假設為cExample.h)時,需進行下列處理:

extern "C"

{

#include "cExample.h" //C++中使用C的函數和變量

}

而在C語言的頭文件中,對其外部函數只能指定為extern類型,C語言中不支持extern"C"聲明,在.c文件中包含了extern"C"時會出現編譯語法錯誤。

(2)“C使用C++在C中引用C++語言中的函數和變量時,C++的頭文件需添加extern"C",但是在C語言中不能直接引用聲明了extern"C"的該頭文件,應該僅將C文件中將C++中定義的extern"C"函數聲明為extern類型。

三、 extern 和static

(1)extern表明該變量在別的地方已經定義過了,在這里要使用那個變量。

(2)static?表示靜態的變量,分配內存的時候,存儲在靜態區,不存儲在棧上面。

static作用范圍是內部連接的關系這和extern有點相反。它和對象本身是分開存儲的,extern也是分開存儲的,但是extern可以被其他的對象用extern引用,而static不可以,只允許對象本身用它。具體差別首先,staticextern是一對“水火不容”的家伙,也就是說externstatic不能同時修飾一個變量;其次,static修飾的全局變量聲明與定義同時進行,也就是說當你在頭文件中使用static聲明了全局變量后,它也同時被定義了;最后,static修飾全局變量的作用域只能是本身的編譯單元,也就是說它的“全局”只對本編譯單元有效,其他編譯單元則看不到它,如:

[cpp]?view plaincopy
  1. /*test1.h*/??
  2. #ifndef?TEST1H??
  3. #define?TEST1H??
  4. static?char?g_str[]="123456";??
  5. void?fun1();??
  6. #endif??
[cpp]?view plaincopy
  1. /*test1.cpp*/??
  2. #include?"test1.h"??
  3. void?fun1()??
  4. {??
  5. ??????cout?<<g_str<<endl;??
  6. }??

[html]?view plaincopy
  1. /*test2.cpp*/??
  2. #include?"test1.h"??
  3. void?fun2()??
  4. {??
  5. ??????cout?<<g_str<<endl;??
  6. }??

以上兩個編譯單元可以連接成功,當你打開test1.obj時,你可以在它里面找到字符串"123456",同時你也可以在test2.obj中找到它們,它們之所以可以連接成功而沒有報重復定義的錯誤是因為雖然它們有相同的內容,但是存儲的物理地址并不一樣,就像是兩個不同變量賦了相同的值一樣,而這兩個變量分別作用于它們各自的編譯單元。也許你比較較真,自己偷偷的跟蹤調試上面的代碼,結果你發現兩個編譯單元(test1,test2)的g_str的內存地址相同,于是你下結論static修飾的變量也可以作用于其他模塊,但是我要告訴你,那是你的編譯器在欺騙你,大多數編譯器都對代碼都有優化功能,以達到生成的目標程序更節省內存,執行效率更高,當編譯器在連接各個編譯單元的時候,它會把相同內容的內存只拷貝一份,比如上面的"123456",位于兩個編譯單元中的變量都是同樣的內容,那么在連接的時候它在內存中就只會存在一份了,如果你把上面的代碼改成下面的樣子,你馬上就可以拆穿編譯器的謊言:

[html]?view plaincopy
  1. /*test1.cpp*/??
  2. #include?"test1.h"??
  3. void?fun1()??
  4. {??
  5. ??????g_str[0]=''a'';??
  6. ??????cout?<<g_str<<endl;??
  7. }??
[html]?view plaincopy
  1. /*test2.cpp*/??
  2. #include?"test1.h"??
  3. void?fun2()??
  4. {??
  5. ??????cout?<<g_str<<endl;??
  6. }??

[html]?view plaincopy
  1. /*main.cpp*/??
  2. void?main()??
  3. {??
  4. ??????fun1();//?a23456??
  5. ??????fun2();//?123456??
  6. }??

這個時候你在跟蹤代碼時,就會發現兩個編譯單元中的g_str地址并不相同,因為你在一處修改了它,所以編譯器被強行的恢復內存的原貌,在內存中存在了兩份拷貝給兩個模塊中的變量使用。正是因為static有以上的特性,所以一般定義static全局變量時,都把它放在原文件中而不是頭文件,這樣就不會給其他模塊造成不必要的信息污染,同樣記住這個原則吧!


四、extern和const

C++中const修飾的全局常量具有跟static相同的特性,即它們只能作用于本編譯模塊中,且static修飾的是全局變量,但是const可以與extern連用來聲明該常量可以作用于其他編譯模塊中,如extern?const?char g_str[];

然后在原文件中別忘了定義:const?char g_str[] = "123456";

所以當const單獨使用時它就與static相同,而當與extern一起合作的時候,它的特性就跟extern的一樣了!所以對const我沒有什么可以過多的描述,我只是想提醒你,

const?char* g_str = "123456" 與?const?char g_str[] ="123465"是不同的,前面那個const修飾的是char *而不是g_str,它的g_str并不是常量,它被看做是一個定義了的全局變量(可以被其他編譯單元使用), 所以如果你像讓char* g_str遵守const的全局常量的規則,最好這么定義const?char*?const?g_str="123456"。


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

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

相關文章

收集整理的非常有用的PHP函數

為什么80%的碼農都做不了架構師&#xff1f;>>> 1、PHP加密解密 2、PHP生成隨機字符串 3、PHP獲取文件擴展名&#xff08;后綴&#xff09; 4、PHP獲取文件大小并格式化 5、PHP替換標簽字符 6、PHP列出目錄下的文件名 7、PHP獲取當前頁面URL 8、PHP強制下載文件 9、…

進程間的通信方式——pipe(管道)

from&#xff1a;https://blog.csdn.net/skyroben/article/details/715133851.進程間通信每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到&#xff0c;所以進程之間要交換數據必須通過內核,在內核中開辟一塊緩沖區,進程A把數據從用戶空間拷到內…

bash中(),{},(()),[],[[]]的區別

前言:在bash中遇到各種括號&#xff0c;同時在進行字符數值比較判定時&#xff0c;總是不斷出現問題&#xff0c;于是通過參考《advanced bash-scripting guide》&#xff0c;同時在centos 6.7版本上進行測試&#xff0c;現況總結如下。如有紕漏&#xff0c;望指正。一.()一個命…

多進程和多線程之間的通信方式及通信實現步驟小結

進程間通信方式 # 管道( pipe )&#xff1a;管道是一種半雙工的通信方式&#xff0c;數據只能單向流動&#xff0c;而且只能在具有親緣關系的進程間使用。進程的親緣關系通常是指父子進程關系。 # 有名管道 (namedpipe) &#xff1a; 有名管道也是半雙工的通信方式&#xff0c;…

highcharts 顯示網格

2019獨角獸企業重金招聘Python工程師標準>>> xAxis: { gridLineColor: #197F07, gridLineWidth: 1 }, yAxis: { gridLineColor: #197F07, gridLineWidth: 2 }, 轉載于:https://my.oschina.net/LingBlog/blog/697885

Cheat—— 給Linux初學者和管理員一個終極命令行備忘單

編譯自&#xff1a;http://www.tecmint.com/cheat-command-line-cheat-sheet-for-linux-users/作者&#xff1a; Avishek Kumar原創&#xff1a;LCTT https://linux.cn/article-3760-1.html譯者&#xff1a; su-kaiyao原文稍有改動 當你不確定你所運行的命令&#xff0c;尤其是…

串口操作之API篇 CreateFile

CreateFile http://bbs.fishc.com/thread-72944-1-1.html(出處: 魚C論壇) ------------------------------------------------------------------------CreateFile用于打開串口,如果操作成功,返回一個句柄.1 function CreateFile(lpFileName: PChar; dwDesiredAccess, dwShareM…

云數據庫·ApsaraDB 產品6月刊

【重點關注】RDS發布新規格 RDS于5月下旬發布新產品規格&#xff0c;新規格對齊ECS配置:1.連接數大幅提升 互聯網型的應用特點是發展快速&#xff0c;在云上應用層會基于VM進行橫向擴展&#xff0c;對數據庫的要求除了資…

【同行說技術】教你玩轉iOS的5篇技術干貨

在文章《iOS從小白到大神必讀資料匯總一到四》這個系列中&#xff0c;深入介紹了iOS入門學習及進階的相關技術資料&#xff0c;今天小編繼續發布iOS學習的5篇干貨文章&#xff0c;趕緊來看看吧 &#xff01;喜歡寫博客的工程師博主可以加工程師博主交流群&#xff1a;391519124…

Qt Console Application 與 Qt GUI Application互轉

在桌面開發中&#xff0c;總的來說&#xff0c;包含兩種類型的應用程序&#xff1a;無界面的Console程序和有界面的GUI程序。Qt也不例外&#xff0c;包含Qt Console Application和Qt GUI Application。一、Qt Console Application在VS2015中創建一個Qt Console Application&…

Create Volume 操作(Part I) - 每天5分鐘玩轉 OpenStack(50)

2019獨角獸企業重金招聘Python工程師標準>>> 前面已經學習了 Cinder 的架構和相關組件&#xff0c;從本節我們開始詳細分析 Cinder 的各種操作&#xff0c;首先討論 Cinder 如何創建 volume。 Create 操作流程如下&#xff1a; 客戶&#xff08;可以是 OpenStack 最…

如何有效解決C與C++的相互調用問題

from&#xff1a;https://blog.csdn.net/gobitan/article/details/1532769在實際工作中可能經常要進行C和C的混合編程&#xff0c;C調用C語言的代碼通常都比較容易&#xff0c;但也有一些細節需要注意。C要調用C的代碼就略為麻煩一些&#xff0c;因為C不支持面向對象的特征。一…

Eclipse開發工具之崩潰和備份

1.通過在命令行中輸入“where java”&#xff0c;找到除jdk目錄下的所有java相關程序&#xff0c;直接刪掉&#xff08;一般會在C:WINDOWSsystem32下&#xff09;以后再也不用怕找不到目錄了 2.內存不足&#xff0c;打開Eclipse目錄下的eclipse.ini&#xff0c;把里面的-Xmx512…

IOS-網絡(監聽網絡狀態)

1 //2 // BWNetWorkTool.h3 // IOS_0131_檢測網絡狀態4 //5 // Created by ma c on 16/1/31.6 // Copyright © 2016年 博文科技. All rights reserved.7 //8 9 #import <Foundation/Foundation.h> 10 11 interface BWNetWorkTool : NSObject 12 ///是否是WiFi …

C++中的friend詳細解析

C中的友元機制允許類的非公有成員被一個類或者函數訪問&#xff0c;友元按類型分為三種&#xff1a;普通非類成員函數作為友元,類的成員函數作為友元&#xff0c;類作為友元。友元包括友元的聲明以及友元的定義。 友元的聲明默認為了extern&#xff0c;就是說友元類或者友元函數…

【VMCloud云平臺】擁抱Docker(六)關于DockerFile(1)

之前我們說過通過Docker pull來下載Images創建容器&#xff0c;這一次我們來聊下如何通過DockerFile創建Images再創建容器&#xff0c;Dockerfile也是Docker中的重點&#xff0c;使用DockerFile能夠更加便捷輕量的存儲標準化環境&#xff0c;也是環境管理的重要手段&#xff0c…

C++中相對路徑與絕對路徑以及斜杠與反斜杠的區別 及 處理代碼

轉載自&#xff1a;http://blog.csdn.net/sszgg2006/article/details/8447176文件路徑正斜杠和反斜杠正斜杠&#xff0c;又稱左斜杠&#xff0c;符號是"/"&#xff1b;反斜杠&#xff0c;也稱右斜杠&#xff0c;符號是"\"。文件路徑的表示可以分為絕對路徑…

01參考資料

《Visual C#經典游戲編程開發》 目標&#xff1a; 掌握小游戲開發的思路&#xff0c;在實際開發過程中&#xff0c;思考實踐面向對象&#xff0c;為cocos2d-x開發打下基礎轉載于:https://www.cnblogs.com/sharpfeng/p/5181260.html

WPF的二維繪圖(一)——DrawingContext

DrawingContext比較類似WinForm中的Graphics 類&#xff0c;是基礎的繪圖對象&#xff0c;用于繪制各種圖形&#xff0c;它主要API有如下幾種&#xff1a; 繪圖API 繪圖API一般形為DrawingXXX系列&#xff0c;常用的基礎的繪圖API有&#xff1a; DrawEllipseDrawGeometryDrawG…

Windows系統編程之進程間通信

Windows系統編程之進程間通信作者&#xff1a;北極星2003來源&#xff1a;看雪論壇&#xff08;www.pediy.com&#xff09;Windows 的IPC&#xff08;進程間通信&#xff09;機制主要是異步管道和命名管道。&#xff08;至于其他的IPC方式&#xff0c;例如內存映射、郵槽等這里…