前面說了C++的基本數(shù)據(jù)類型,下面來看看在C++中如何定義變量和常量。
變量
定義和初始化
C++定義變量的方式和C語言一樣,也可以在定義的同時初始化。值得一提的是列表初始化,在原來的C++版本中可以用于初始化數(shù)組等。C++ 11標準增加了更廣泛的列表初始化,所以可以用列表初始化來初始化單個變量。
//定義變量
int a;
//定義并初始化
int b = 1;
//同時定義多個變量
int c = 5, d = 6;
//C++ 11 新特性:列表初始化
int e{3};
列表初始化有一個限制,如果要初始化的值超過了可容納的范圍,就會引發(fā)編譯錯誤,而直接賦值就可以。
//不能編譯
//short s{3.1415};
//可以編譯
short s = 3.1415;
對于函數(shù)內(nèi)部的局部變量來說,如果不初始化的話,值是未定義的。對于未初始化的局部變量進行操作可能導致無法預料的后果。對于全局變量,如果沒有給定初始值,默認值是0。
定義和聲明變量
變量定義告訴編譯器,我要創(chuàng)建一個變量,以后再用它。而變量聲明告訴編譯器,我要引用一個變量,所以你先按照這個變量的類型和名字去找它。聲明變量需要使用extern
關(guān)鍵字,而且聲明的時候不能賦值。如果使用extern
關(guān)鍵字并賦值,那么變量聲明就變成了變量定義,而且這只能用于全局變量的聲明和定義。如果對一個函數(shù)內(nèi)部的本地變量聲明添加初始化式,就會引發(fā)編譯錯誤。
//定義了一個全局變量
int global_count;
//聲明在另一個文件中定義的全局變量
extern int global_count;
標識符
標識符也就是變量、函數(shù)、類的名字,用于標識不同的對象。和大多數(shù)編程語言一樣,C++的標識符需要以字母或下劃線開頭,有數(shù)組、字母和下劃線組成,而且對大小寫敏感。
作用域
如果一個標識符定義在花括號外面,那么這個標識符的作用域就是全局作用域。全局作用域的變量可以在本文件的任何地方訪問,如果在其他文件中聲明這個標識符,那么還可以在其他文件中訪問。
如果一個標識符在某對花括號中定義,那么這個標識符的作用域就在這對花括號中,這就是局部作用域。局部作用域的標識符在超出這個塊后,就無法被訪問了。如果有一個全局變量,然后在某個作用域中又定義了一個同名變量,那么這個局部變量就會屏蔽對全局變量的訪問。如果希望訪問全局變量,需要使用域操作符::
來指定。
//聲明在另一個文件中定義的全局變量
extern int global_count;
void declare_and_define()
{
cout << global_count << endl;
if (true)
{
int global_count = 5;
cout << "同名局部變量覆蓋全局變量:" << global_count << endl;
cout << "使用全局變量:" << ::global_count << endl;
}
}
復合類型
復合類型指的是基于其他類型定義的更復雜的類型,這些復合類型也是C++語言的重點和難點。
指針
指針是C++語言從C語言中繼承的類型。每個變量在內(nèi)存中都有一個地址來存儲,指針就是這個地址。利用指針我們可以直接對變量進行修改。定義指針需要在指針名前添加星號*
。如果要在一行定義多個指針,那么每一個指針前都需要星號。
//指針
int *p1, *p2;
有了指針,還需要將變量的地址賦給它,這需要使用取地址符&
。注意指針和變量的類型必須匹配,將int
型變量的地址賦給double *
類型的指針是錯誤的。
int d1 = 5, d2 = 6;
//指針
int *p1, *p2;
p1 = &d1;
p2 = &d2;
cout << "d1=" << d1 << ",p1=" << *p1 << endl;
如果想由指針訪問其指向的對象,使用解引用符*
。由于指針指向的是對象本身,所以使用解引用符修改對象會修改實際對象的值。
*p1 = 100;
cout << "d1=" << d1 << ",p1=" << *p1 << endl;
指針有兩種狀態(tài),一種是指向某個內(nèi)存空間,另一種是無效狀態(tài)。對于無效狀態(tài)的指針進行解引用會引發(fā)不可預料的后果,所以這種情況應該盡量避免。對于無效狀態(tài)的指針,最好將其清空。在老版本的C++語言中,我們需要引用cstdlib
頭文件,并且使用其中預定義的NULL
來清空指針,這個預定義的值實際上是0。在C++ 11標準中引入了一個新的字面量nullptr
來代替NULL
,所以在以后的程序中,我們最好使用nullptr
。
引用
引用是C++語言新增的一種類型,它和指針既有相似之處,也有不同之處。
先來看看如何定義引用。
int d1 = 5;
//d2是d1的引用
int& d2 = d1;
如果要在一行同時定義多個引用,需要在每個引用名前添加&
。
int &r1 = d1, &r2 = d2;
引用實際上是一個別名,一旦定義好,對引用的所有操作都相當于直接對原變量進行操作。這一點和指針很類似。
//修改引用也會修改原變量
d2 = 100;
cout << "d1=" << d1 << ",d2=" << d2 << endl;
但是需要注意一點,指針也是一個變量,所以一個指針可以多次賦值,指向不同變量的地址。而引用只能和一個變量綁定,所以引用在定義的時候必須初始化,而且一旦初始化,無法再綁定到其他變量。
復合類型總結(jié)
前面介紹了引用和指針兩種復合類型,這些復合類型還可以互相組合,生成更加復雜的類型聲明。對于指針和引用聲明,它們是和變量組合在一起的。所以下面的定義中,p是一個指針,而d是一個變量。如果希望聲明多個指針, 需要在每一個變量名前添加*
號。
int *p, d;
//即使星號和類型放在一起,p仍然是指針,d仍然是變量
//int* p, d;
符合類型還可以互相組合。比如說,我們可以定義指針的指針。
int **pp = &p;
引用是一個別名,所以無法定義引用的指針。但是反過來可以定義指針的引用。
int*& r = p;
常量
常量定義
常量和變量一樣,唯一的不同點是常量一經(jīng)定義,它的值就不能夠在改變。常量定義和變量差不多,只不過需要使用const
限定符修飾。由于常量一經(jīng)賦值就無法再改變,所以常量在定義的時候必須初始化。
//定義常量
const int const_count = 5;
編譯器在處理常量的時候,會直接將常量替換為其對應的值,所以編譯器需要知道常量的值。默認情況下,常量定義只在本文件中有效。如果在多個文件中定義了同名的常量,那么這些常量是各不相同的常量。如果需要在文件之間共享常量,就需要在常量定義和聲明上都添加extern
關(guān)鍵字。
變量的const引用
我們可以把引用標記為const
的,這種情況下這個引用變?yōu)橹蛔x的,我們可以修改原變量,可以通過引用讀取原變量,但是無法通過引用修改原變量。
//引用常量
int i = 5;
const int& r = i;
i = 10;
//r = 10;
指針常量
指針存儲的就是對象的地址,如果我們把指針本身定義為const
的,那么我們將無法將這個指針指向其他對象的地址,但是我們可以通過這個指針修改指向?qū)ο蟮闹?/strong>。需要注意,這種定義必須將const
關(guān)鍵字置于緊挨的變量名的位置。
int j = 100;
//const指針
int *const cp = &i;
//可以修改指針指向的對象的值
*cp = 10;
//無法修改指針指向的對象
//cp = &j;
指向常量的指針
這種情況和上面那種情況正好相反,這次是將指針指向的對象聲明為const
的,這樣一來,我們無法修改指針指向的對象的值,但是我們可以修改指針指向其他對象的地址。這種定義需要將const
放到符合聲明的最前面。雖然這種情況叫做指向常量的指針,但是這是對指針類型的聲明,實際上這個指針完全可以指向一個變量,只不過我們無法通過指針修改這個變量的值。
const int *p = &i;
//可以修改指針指向其他對象
p = &j;
//無法通過指針修改值
//*p = 200;
頂層const和底層const
前面我們看到了,指針是一個非常復雜的話題。指針本身是一個對象,而它又指向另一個對象。這些情況和常量常量聲明組合在一起,將會變得非常復雜。所以我們需要對其做出分類。我們把本身是const
的對象叫做頂層const,而指向的對象是const
的就叫做底層const。這樣一來就比較清楚了,指向常量的指針就是底層const,而指針常量就是頂層const。
下面這種情況,變量ccp
即使頂層const又是底層const。
//既是頂層const又是底層const
const int*const ccp = &i;
constexpr和常量表達式
有時候編譯器要求程序中的某些值不能改變,而且必須在編譯期就能計算出來,這樣的值叫做常量表達式。顯然,字面量和用常量表達式初始化的const對象都是常量表達式。
當然,一個變量并不是常量表達式,哪怕我們在程序中沒有修改過變量的值也不行。一個用變量初始化的const
對象也不是常量表達式。
C++ 11標準新規(guī)定了一個關(guān)鍵字constexpr
,它可以讓編譯器檢查聲明的常量。如果這個常量不是合法的常量表達式,那么就無法編譯。
//常量表達式
constexpr int MAX_COUNT = 100;
constexpr int MIN_COUNT = -MAX_COUNT;
//i不是常量,所以下面的代碼不能編譯
//constexpr int VARIABLE_COUNT = i;