C++變量的作用域有多種,綜述:
(1)作用域為全局的變量在定義位置到文件結尾之間都可用
(2)自動變量的作用域為局部
(3)靜態變量的作用域是全局還是局部取決於它是如何被調定義的
變量的定義是通過變量聲明語句來實現的,變量聲明語句的一般格式為:
[<存儲類>]<類型名><變量名>[=<初值表達式>],...;
<存儲類>有四種,它們分別是auto、register、static、extern。
<類型名>為已存在的一種數據類型名稱,如char,short,int,long,float,double等基本數據類型名,或者用戶定義的數據類型名。
<變量名>是用戶定義的一個標識符,用來表示一個變量,該變量可以通過后面的可選項賦予一個值,稱為給變量賦初值,也叫做對變量進行初始化。C+ +中標識符是區分大小寫的,也就是說,大寫字母和小寫字母被認為是不同的字母。
變量名的命名遵循如下規則:
(1) 不能是C+ +關鍵字;
(2)第一個字符必須是字母或下划線;
(3)中間不能有空格;
(4)變量名中不能包括;,′″+-之類的特殊符號。
實際上變量名中除了能使用26個英文大小寫字母和數字外,只能使用下划線“_”。
2變量的使用方式
(1)全局變量和局部變量
全局變量是在所有函數定義、類定義和程序塊之外聲明的變量。聲明全局變量時如果在程序中不對它進行專門的初始化,該變量會被系統自動初始化為0。在程序的任何一個函數、類或程序塊之內均可以訪問全局變量。
局部變量是在某個函數定義、類定義或程序塊之內聲明的變量。局部變量只能在聲明它的函數、類或程序塊中被訪問。
(2)生存期與作用域
生存期是指從一個變量被聲明且分配了內存開始,直到該變量聲明語句失效,它占用的內存空間被釋放為止。一個全局變量的生存期從它被聲明開始,直到程序結束;一個局部變量的生存期從它被聲明開始,直到包含它的最近的一個程序塊結束。
作用域是指變量名可以代表該變量存儲空間的使用范圍。
一般情況下,變量的作用域與其生存期一致,但由於C+ +語言允許在程序的不同部分為不同變量取同一名字,因此一個變量名的作用域可能小於其生存期。
(3)變量的存儲類屬性
在C+ +中變量還可以按存儲分配方式的不同被划分為4種不同的存儲類別,它們分別是:
①auto變量:用關鍵字auto聲明的局部變量稱為自動變量。auto為變量聲明時的默認存儲類別,即在變量定義時,如果不顯式標明存儲類別,則系統自動按auto變量處理。auto變量所占用存儲空間的分配和釋放工作將由系統自動完成。
②register變量:用關鍵字register聲明的局部變量稱為寄存器變量。register變量可能以寄存器作為其存儲空間。聲明寄存器變量時,關鍵字register的作用只能是建議(而不是強制)系統使用寄存器,原因是寄存器雖然存取速度快,但空間有限,當寄存器不夠用時,該變量仍然按自動變量處理。
③static變量:用關鍵字static聲明的變量稱為靜態變量。任何靜態變量的生存期將延續到整個程序的終止。與全局變量一樣,為靜態變量分配的存儲空間在整個程序運行過程中不再被釋放;如果靜態變量未被賦初值,系統將自動為其賦初值為0。
④extern變量:用關鍵字extern聲明的變量稱為外部變量。變量一旦被聲明為外部變量,系統就不必像一般變量那樣為其分配內存,因為該變量已在這一局部的外面被定義。外部變量一般用於多個文件組成的程序中,有些變量在多個文件中被聲明,但卻是指同一變量。標明某一變量為外部變量可以避免為其重復分配內存。
1.自動變量
a.函數中聲明的函數參數和變量
b.代碼塊中定義的變量
C++編譯器對自動變量的實現為,程序留出一段內存,並將其視為棧(由於新數據放在原數據的上面,且新數據會最先被銷毀,類似棧),當程序使用完該自動變量時,會將其從棧中刪除。當函數或者代碼塊運行結束的時候,自動變量將不再存在
2.靜態變量
靜態變量提供了3中鏈接性:
外部鏈接性(可在其他文件中訪問):在代碼塊的外部聲明
內部鏈接性(只能在當前文件中訪問):在代碼塊的外部聲明,且使用static限定符
無鏈接性(只能在當前函數或代碼塊中訪問):在代碼塊內聲明,且使用static限定符
不論哪種鏈接性變量,在整個程序的執行過程中,會一直存在,與自動變量相比,它們的壽命更長。如果未進行初始化,編譯器將其初始化為0;
自動變量和靜態變量最大的區別在於:編譯器對兩者的處理不一樣,對自動變量,采用棧;對靜態變量,編譯器將分配固定的內存塊來存儲所有的靜態變量,這些變量在整個程序的執行期間一直存在。
C++提供兩種變量的聲明:
一種是定義聲明,即定義;它給變量分配存儲空間,可進行初始化,有兩種方式
Int a;
Extern int a = 1;//進行初始化
另一種是引用聲明,它不給變量分配存儲空間
如果在多個文件中使用外部變量(全局,且具有外部鏈接性),只需在一個文件中包含該變量的定義,但在使用該變量的其他所有文件中,都必須使用關鍵字extern聲明它
(4)typedef類型說明
使用關鍵字typedef可以為已有類型名定義一個新類型名。其語法格式為:
typedef<已有類型名><新類型名>
typedef類型說明並沒有真正地定義新的數據類型,它只是相當於給某個已有的數據類型起了一個別名。在規模較大的程序中為了提高代碼可讀性常采用這種形式。
3符號常量聲明語句
符號常量在使用之前必須先進行聲明。符號常量聲明語句同變量聲明語句類似,其語法格式為:
const<類型名><符號常量名><初值表達式>……;
其中,關鍵字const指明這是一條符號常量聲明語句,后面跟着符號常量的類型名,接着是符號常量名,它是一個用戶定義的標識符,符號常量名之后為一個賦值號和一個初值表達式。由此可見,必須在聲明符號常量的同時為其賦初值。該語句也可以聲明多個符號常量。
系統執行符號常量聲明語句時,需要依次為每個符號常量分配存儲單元並賦初值。一個符號常量被聲明后,它的值就是聲明所賦予的初值,作為常量,這個值以后將始終保持不變,因為系統只允許讀取它的值,而不允許再次向它賦值。另外,在符號常量聲明語句中,若<類型名>為int,則int可省略。
符號常量聲明語句既可以出現在函數體外,也可以出現在函數體內,這一點也跟變量定義語句相同。
C+ +關鍵字中的true和false就是系統預先定義的兩個符號常量,它們的值分別為1和0。使用符號常量往往可以提高程序的可讀性和可維護性。由於符號常量和變量同樣要求系統為其分配內存單元,所以可以把符號變量視為一種不允許賦值改變的或只讀不寫的變量,稱其為const變量。
4使用#define命令定義符號常量
# define命令是一條預處理命令,也可以用它來定義符號常量。其命令格式為:
#define<符號常量名><字符序列>
<符號常量名>是用戶定義的標識符,又稱為宏或宏標識符;<字符序列>也是由用戶給定的用來代替宏的一串字符序列,也稱為宏替換體,它可以是數值常量、可計算值的表達式或字符串。宏被該命令定義后就可以使用在其后的程序中。當程序被編譯時將把所有地方使用的宏標識符替換為對應的字符序列,並把宏命令刪除掉。
其中AUTO關鍵字:
初始化變量的類型推斷
初始化變量時,auto可以使用關鍵字代替,type以告訴編譯器從初始化程序的類型推斷出變量的類型。這稱為類型推斷(有時也稱為類型推斷)。
例如:
auto d{ 5.0 }; // 5.0 is a double literal, so d will be type double auto i{ 1 + 2 }; // 1 + 2 evaluates to an int, so i will be type int |
這也適用於函數的返回值:
int add(int x, int y) { return x + y; }
int main() { auto sum { add(5, 6) }; // add() returns an int, so sum's type will be deduced to int return 0; } |
雖然auto代替基本數據類型使用僅節省了一些(如果有的話)擊鍵,但在以后的課程中,我們將看到一些示例,其中類型變得復雜而冗長。在這種情況下,使用auto可以節省很多打字時間。
函數的類型推斷
在C ++ 14中,對auto關鍵字進行了擴展,以便能夠從函數體內的return語句推斷出函數的返回類型。考慮以下程序:
auto add(int x, int y) { return x + y; } |
由於x + y計算為int,編譯器將推斷此函數的返回類型為int。使用auto返回類型時,所有return語句必須返回相同的類型,否則將導致錯誤。
盡管這看起來很簡潔,但我們建議對於常規功能避免使用此語法。函數的返回類型在幫助調用者記錄期望函數返回的內容方面非常有用。如果未指定特定類型,則調用者可能會誤解該函數將返回的類型,這可能會導致意外錯誤。
最佳實踐
避免對函數返回類型使用類型推斷。
感興趣的讀者可能想知道為什么auto在初始化變量時可以使用,但不建議用於函數返回類型。一個好的經驗法則是auto可以在定義變量時使用,因為該變量從其推斷類型的對象在語句的右側可見。但是,對於函數而言並非如此-沒有上下文可以幫助指示函數返回的類型。用戶實際上必須深入研究函數主體本身,以確定函數返回的類型。它不那么直觀,因此更容易出錯。
尾隨返回類型語法
的auto關鍵字,也可用於使用聲明函數后返回的語法,其中,所述函數原型的其余部分之后指定的返回類型。
考慮以下功能:
int add(int x, int y) { return (x + y); } |
使用auto,可以等效地寫為:
auto add(int x, int y) -> int { return (x + y); } |
在這種情況下,auto不執行類型推斷-使用尾隨返回類型只是語法的一部分。
您為什么要使用它?
一件好事是,它使您所有的函數名稱都對齊:
auto add(int x, int y) -> int; auto divide(double x, double y) -> double; auto printSomething() -> void; auto generateSubstring(const std::string &s, int start, int len) -> std::string; |
目前,我們建議繼續使用傳統的函數返回語法。但是在第7.15節-lambdas簡介(匿名函數)中,我們將再次看到這種尾隨的返回類型語法。
功能參數類型的類型推斷
許多新的程序員嘗試這樣的事情:
#include <iostream>
void addAndPrint(auto x, auto y) // only valid starting in C++20 { std::cout << x + y; }
int main() { addAndPrint(2, 3); // int addAndPrint(4.5, 6.7); // double } |
在C ++ 20之前,這是行不通的,因為編譯器在編譯時無法推斷函數參數x和y的類型。在C ++ 20之前的版本中,如果要創建可用於各種不同類型的通用函數,則應使用function templates(在下一章中介紹),而不是type inference。
從C ++ 20開始,auto可以將關鍵字用作創建的簡捷方法function templates,因此上述代碼將編譯並運行。請注意,這種使用auto不會執行類型推斷。
對於高級讀者
Lambda expressionsauto自C ++ 14起已支持參數。我們將在以后的課程中介紹lambda表達式。