C++中的extern聲明變量詳解
extern聲明變量無外乎如下兩種:
1、聲明全局變量
2、聲明函數(shù)
今天我們只談extern,什么const、static之類等等與之相關(guān)或不相關(guān)的一律忽略,下面就分別對以上兩種情況一一講解
聲明和定義
既然提到extern聲明變量,那我們就必須搞清楚聲明和定義的區(qū)別。
這里我們將普通數(shù)據(jù)變量和函數(shù)統(tǒng)稱變量。從內(nèi)存分配角度來說,聲明和定義的區(qū)別在于聲明一個(gè)變量不會(huì)分配內(nèi)存,而定義一個(gè)變量會(huì)分配內(nèi)存。一個(gè)變量可以被聲明多次,但是只能被定義一次。
基于以上前提,我們可以把聲明和定義類比為指針和內(nèi)存的關(guān)系。我們知道,指針其實(shí)就是指向內(nèi)存的一個(gè)符號(hào),變量的定義就好比一塊內(nèi)存區(qū)域,而聲明就好比它的指針,可以有多個(gè)指針指向同一個(gè)內(nèi)存區(qū)域,而一個(gè)指針只能指向一個(gè)內(nèi)存區(qū)域,這樣就很好理解為什么變量只能被定義一次,如果被定義多次,那就會(huì)分配多個(gè)內(nèi)存,這樣你通過變量的聲明到底去找哪塊內(nèi)存區(qū)域呢,這會(huì)是個(gè)問題。
對于數(shù)據(jù)來說,聲明和定義往往是同時(shí)存在的,比如下面的一行語句
int data;
這樣既聲明了data同時(shí)也定義了data,怎樣做到只聲明而不定義呢,用extern就可以了
extern int data;
對于函數(shù)來說,聲明和定義就很容易區(qū)分了,一般我們會(huì)將聲明放在頭文件而將定義放在源文件里
void hello();
這是一個(gè)函數(shù)的聲明,而
void hello()
{
printf("hello world!\n");
}
這是一個(gè)函數(shù)的定義。當(dāng)然,函數(shù)的聲明和定義也可以同時(shí)發(fā)生,如果我們沒有頭文件而只有源文件,并且在源文件里并沒有void hello();這樣的語句,那么這個(gè)函數(shù)的聲明和定義就同時(shí)發(fā)生了,此時(shí)如果我們在原文件里想要調(diào)用函數(shù)hello(),你調(diào)用的代碼必須在函數(shù)定義之后。
其實(shí)上面的要點(diǎn)只在于一句話:使用變量之前必須聲明,聲明可以有多次,而定義只能有一次。記住這句話,后面的就都很容易理解了。
extern聲明全局變量
我們先來看如下例子,現(xiàn)有三個(gè)文件:test.h, test.cpp, main.cpp,其中main.cpp和test.cpp需要共享一個(gè)變量g_name,三個(gè)文件的內(nèi)容如下
/* test.h */
#ifndef _TEST_H_
#define _TEST_H_
#include <string>
std::string g_name;
void hello();
#endif
/* test.cpp */
#include <stdio.h>
#include "test.h"
void hello()
{
printf("hello %s!\n", g_name.c_str());
}
/* main.cpp */
#include "test.h"
std::string g_name;
int main()
{
g_name = "Handy";
hello();
return 0;
}
三者關(guān)系為,test.cpp包含了test.h,main.cpp也包含了test.h,這里的包含其實(shí)就是include。我們執(zhí)行編譯命令
g++ main.cpp test.cpp
編譯報(bào)錯(cuò)redefinition of 'g_name',說的是g_name被重定義了
我們看一下g_name出現(xiàn)的地方,一個(gè)是在test.h里,一個(gè)是在main.cpp里,兩條語句都是std::string g_name,前面我們已經(jīng)說過,這樣的方式既聲明也定義了變量,那g_name是如何被重定義的呢,首先我們需要理解include的含義,我們可以將include一個(gè)頭文件理解為在該行展開頭文件里的所有代碼,由于main.cpp包含了test.h,我們在那一行將test.h的內(nèi)容展開,就會(huì)發(fā)現(xiàn)main.cpp里有兩句std::string g_name;所以在main.cpp里,g_name被定義了兩次。
由于我們可以將include頭文件理解為展開代碼,所以編譯的時(shí)候其實(shí)不需要指定頭文件,只需要源文件就夠了。需要注意的是,重定義并不是指在同一個(gè)原文件里定義多次,而是指在整個(gè)代碼空間里,比如上面的例子是就是指在test.cpp和main.cpp里,其實(shí)上面的例子里g_name是被重定義了三次,其中test.cpp里一次,main.cpp里兩次。
那上面重定義的問題怎么解決呢,很簡答,將test.h里的std::string g_name;改為extern std::string g_name;就可以了,由于extern語句只聲明變量而不定義變量,因此test.cpp和main.cpp展開頭文件后,也只是將g_name聲明了兩次,而真正的定義還是在main.cpp里
extern聲明函數(shù)
還是上面的例子,我們怎么在main.cpp里不包含頭文件就可以調(diào)用hello函數(shù)呢,既然今天的主題是extern,不用提醒也知道,使用extern就可以了,代碼如下
/* test.cpp */
#include <string>
#include <stdio.h>
// 聲明g_name
extern std::string g_name;
// 聲明和定義void hello()
void hello()
{
printf("hello %s!\n", g_name.c_str());
}
/* main.cpp */
#include <string>
// 聲明和定義g_name
std::string g_name;
// 聲明void hello()
extern void hello();
int main()
{
g_name = "Handy"
hello();
return 0;
}
注意這里用到extern聲明變量和函數(shù)兩種場景,我分別在語句后面做了注釋。編譯命令如下
g++ main.cpp test.cpp
這里我們并沒有用到頭文件,但是依然可以在不同文件間共享變量和函數(shù),這一切都是extern的功勞!
總結(jié)
要了解extern主要搞清以下幾個(gè)概念:
1、聲明和定義的區(qū)別。全局代碼空間里,變量可以有多個(gè)聲明,但只能有一個(gè)定義
2、include頭文件等同于展開頭文件里的代碼
了解了以上兩點(diǎn),再來分析extern的用法,是不是就會(huì)清晰很多了
相關(guān)文章
Pipes實(shí)現(xiàn)LeetCode(194.轉(zhuǎn)置文件)
這篇文章主要介紹了Pipes實(shí)現(xiàn)LeetCode(194.轉(zhuǎn)置文件),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
C語言實(shí)現(xiàn)基于最大堆和最小堆的堆排序算法示例
這篇文章主要介紹了C語言實(shí)現(xiàn)基于最大堆和最小堆的堆排序算法示例,分別是基于最大堆的升序排序和基于最小堆的降序排序?qū)嵗?需要的朋友可以參考下2016-06-06
C++ 網(wǎng)絡(luò)連通性檢測的實(shí)現(xiàn)方法
這篇文章主要介紹了C++ 網(wǎng)絡(luò)連通性檢測的實(shí)現(xiàn)方法的相關(guān)資料,這里提供實(shí)例幫助大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09
C++實(shí)現(xiàn)LeetCode(170.兩數(shù)之和之三 - 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(170.兩數(shù)之和之三 - 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
C語言循環(huán)隊(duì)列的表示與實(shí)現(xiàn)實(shí)例詳解
這篇文章主要介紹了C語言循環(huán)隊(duì)列的表示與實(shí)現(xiàn),對于數(shù)據(jù)結(jié)構(gòu)與算法的研究很有幫助,需要的朋友可以參考下2014-07-07
C/C++動(dòng)態(tài)分配與釋放內(nèi)存的區(qū)別詳細(xì)解析
以下是對C與C++中動(dòng)態(tài)分配與釋放內(nèi)存的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下2013-09-09
ubuntu系統(tǒng)vscodeC++編譯環(huán)境配置與使用方式
這篇文章主要介紹了ubuntu系統(tǒng)vscodeC++編譯環(huán)境配置與使用方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
Species Tree 利用HashTable實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了Species Tree 利用HashTable實(shí)現(xiàn)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01

