變數


字面常數儲存於記憶體,現在的問題是若要將數值儲存在記憶體,並在稍後取回使用,該如何進行?

變數(Variable)提供一個有名稱的記憶體儲存空間,變數可包含的資訊包含變數資料型態、變數記憶體位址與變數儲存值;C++ 開發者也常用物件(object)這個名稱來表示變數,因為物件對 C++ 開發者來說,不只是表示類別實例之類的東西,而是代表著一塊記憶體區域,可能含有型態與值等資訊。

在 C++ 中要使用變數,必須先宣告變數名稱與資料型態,例如:

int age;            // 宣告一個整數變數
double money;      // 宣告一個倍精度浮點數變數

C++ 使用 intfloatdoublecharbool 等宣告變數名稱與型態,變數命名時不可以使用數字作為開頭,也不可以使用一些特殊字元,像是 *&^% 之類的字元,變數名稱不可以與 C++ 內定的關鍵字同名,例如 intfloatclass 等等。

在 C++ 宣告變數,會配置一塊記憶體空間,空間長度依宣告時的資料型態而定,如果在函式外宣告變數,變數會預設為對應型態的零值,若是在函式中宣告變數,被配置的這塊空間中原先可能就有資料,也因此變數在宣告後的值是不可預期的,應該在在變數宣告後初始其值,可以使用「指定運算子」(Assignment operator)= 來指定變數的值,例如:

int ageForStudent = 0;
double scoreForStudent = 0.0;
char levelForStudent = 'A';

上例中三個變數分別被初始為 00.0'A',還可以使用隱喻的方式來宣告變數並指定初值,例如:

int ageForStudent(0);
double scoreForStudent(0.0);
char levelForStudent('A');

這兩種宣告方式在宣告變數的時候,同時指定變數的儲存值,字元在指定時需使用單引數 '' 包括,在宣告並指定變數值之後,可以使用變數名稱來取得其所儲存的值,下面這個程式是個簡單的示範:

#include <iostream> 
using namespace std; 

int main() { 
    int ageForStudent; 
    double scoreForStudent; 
    char levelForStudent; 

    cout << "\n年級\t得分\t等級"; 
    cout << "\n" << ageForStudent 
         << "\t" << scoreForStudent 
         << "\t" << levelForStudent 
         << "\n"; 

    ageForStudent = 5; 
    scoreForStudent = 80.0; 
    levelForStudent = 'B'; 

    cout << "\n年級\t得分\t等級"; 
    cout << "\n" << ageForStudent 
         << "\t" << scoreForStudent 
         << "\t" << levelForStudent 
         << "\n"; 

    return 0;
}

這個程式先宣告變數,但沒有初始其值,第一次顯示時會出現不可預期的資料,而在指定變數的值之後,顯示變數值時就會出現指定的資料了,執行結果如下所示:

年級    得分    等級
4200459 1.55553e-306

年級    得分    等級
5       80      B

也可以在使用宣告變數後,使用以下的建構子(Constructor)方式將變數的值初始為各型態的零值:

int ageForStudent = int();
double scoreForStudent = double();
char levelForStudent = char();

在 C++ 11 中,增加了清單初始化(list initialization),其目的在統一初始化的方式,對於基本型態,也可以使用同一種風格來初始化。

int ageForStudent{0};
double scoreForStudent{0.0};
char levelForStudent{'A'};

這種初始化還有個特性,{} 中的值若指定給變數時,有可能造成精度遺失的話,編譯器會提出警訊。例如:

double pi = 3.14159;
int x = {pi};  // warning: narrowing conversion of 'pi' from 'double' to 'int' inside { }

如果如下撰寫,預設是不會提出警訊的:

double pi = 3.14159;
int x = pi; 

這是因為型態發生了隱式轉換(implicit conversion),pi 的小數會被截去,整數部份被指定給 x,若在這種情況下,也想要編譯器提出警訊,編譯時可以加上 -Wconversion 引數。

可以在宣告變數時使用 const 關鍵字來限定,如果程式中有其他程式碼試圖改變這個變數,編譯器會檢查出這個錯誤,例如:

const double PI = 3.14159;
PI = 3.14;

這一段程式碼中的 MAX 變數我們使用 const 來限定,所以它在指定為 10 之後,就不可以再指定值給它,所以第二次指定會被編譯器指出錯誤,在 g++ 編譯器時會出現這樣的錯誤訊息:

error: assignment of read-only variable 'PI'

使用 const 來限定的變數,目的通常就是不希望其它程式碼來變動值,例如圓周率 PI 的指定。

從 C++ 11 開始,可以使用 constexpr 請編譯器驗證常數初始時是在編譯時期決定,例如:

constexpr int A = 10;
constexpr int B = sizeof(10);
constexpr int C = sizeof(B);
constexpr int D = rand();    // error: call to non-'constexpr' function 'int rand()'

rand 傳回隨機亂數,因此 D 的實際值要在執行時期才能決定,而非編譯時期,因此發生錯誤;constexpr 的價值在於,在重重的運算式或函式呼叫下,編譯時期常數有時很難一眼看出,這時加上 constexpr 請編譯器來進行檢驗就方便多了。

如果要宣告無號整數變數,可以加上 unsigned 關鍵字,例如:

unsigned int i ;

bool 型態的變數雖然也可視為整數型態,但不能在宣告變數時加上 unsigned 來修飾。

從 C++ 11 開始,若變數宣告後有明確地初始化,可以使用 auto 讓編譯器推斷變數型態,例如:

int ageForStudent = 5; 
double scoreForStudent = 80.0; 
char levelForStudent = 'B'; 

可以改為:

auto ageForStudent = 5; 
auto scoreForStudent = 80.0; 
auto levelForStudent = 'B'; 

只用 auto 卻沒有初始,會發生編譯錯誤:

auto ageForStudent; // error: declaration of 'auto ageForStudent' has no initializer