C++入門教程(20):變數、不變數和常量

小古銀發表於2019-05-13

小古銀的官方網站(完整教程):http://www.xiaoguyin.com/
C++入門教程視訊:https://www.bilibili.com/vide…


變數

前面簡單講解過變數,現在就再詳細講解變數。

先給出幾個關於變數的建議

  • 基本資料型別的變數要初始化。
  • 避免使用全域性變數。
  • 每個變數宣告時單獨一行。
  • 變數名稱應該符合變數的用途。

基礎示例

#include <iostream> // std::cout std::endl
#include <string> // std::string

std::string global = "這是全域性變數"; // 全域性變數, 作用域是全域性, 即在程式結束是才會銷燬

int main(int argc, char *argv[]) // 函式引數也是變數, 作用域是該函式內部, 即函式以外就不存在
{
    std::string text1 = "這是區域性變數"; // 區域性變數, 作用域是最近的左右大括號, 即作用域是主函式

    // 在程式碼中加入左右大括號可以限制變數的作用域
    {
        std::string text2 = "這也是區域性變數"; // 作用域是最近的左右大括號, 即下一行以後它佔用的記憶體就會被銷燬而不存在
        std::cout << text1 << std::endl << text2 << std::endl;
    }
    std::cout << text1 << std::endl;
    std::cout << global << std::endl;
    std::cout << argc << std::endl;

    // 這個輸出的是記憶體中的地址
    // 由於每次程式啟動都是由系統重新分配記憶體
    // 所以每次輸出的記憶體地址都不會一樣
    std::cout << argv << std::endl;

    return 0;
}

輸出結果:

這是區域性變數
這也是區域性變數
這是區域性變數
這是全域性變數
1
034D6538

基礎講解

前面教程提到過std::string宣告的變數用來儲存字串,如:

std::string text = "這是字串";

宣告變數的位置大致分為三種:

  • 全域性變數:在所有函式之外宣告的變數。它的作用域是全域性,整個程式所有地方都可以使用這個變數,程式結束時才釋放變數的記憶體。
  • 區域性變數:除全域性變數以外的都是區域性變數。它的作用域是離它最近的一對左右大括號{ }之間,在程式執行到},那麼,這個變數的記憶體就會釋放,換言之,在它的作用域以外的地方,這個變數是不存在的。
  • 形式引數:形式引數是特殊的區域性變數,它在函式名後面的一對圓括號中定義。它的作用域就在這個函式內,在這個函式以外的地方,這個變數不存在。程式碼中的argc和argv就是形式引數。

基礎擴充

注意

  1. 如果兩個不同作用域有著兩個變數,而這兩個變數名稱是一樣的,那麼它們並不是同一個變數。
  2. 如果在一個作用域中有一個子作用域,子作用域與外部作用域都含有相同名字的變數(如全域性變數和區域性變數),那麼,在子作用域使用的變數是子作用域的變數。

建議變數初始化的原因:基本資料型別的變數宣告之後,它會儲存一個不確定的數,為了防止程式使用這個不確定的數導致程式出現問題(其他資料型別則要看情況,如std::string則不需要初始化)。

避免使用全域性變數的原因:上面注意中的第二條就是原因,防止寫程式時誤操作。如果為區域性變數起一個名字與全域性變數,當全域性變數多了,開發過程中就會思考區域性變數起名字的問題,會導致開發變慢和程式碼難讀的問題。

為注意中的兩點舉一個例子:

#include <iostream> // std::cout std::endl
#include <string> // std::string

std::string global = "這是全域性變數";

int main(void)
{
    std::string global = "這個global並不是全域性變數";
    std::cout << global << std::endl;

    {
        std::string text = "區域性變數1";
        std::cout << text << std::endl;
    }

    {
        std::string text = "區域性變數2";
        std::cout << text << std::endl;
    }
    return 0;
}

輸出結果:

這個global並不是全域性變數
區域性變數1
區域性變數2

在編譯的過程中,應該會看到一個警告,說是區域性變數隱藏了全域性變數。那麼這個全域性變數global在區域性變數global的作用域(即主函式)裡都是不能使用的。這就是新手要注意的:不能忽視警告資訊

const

既然有變數,那麼相對應的就應該有不變數。

有一些變數,初始化之後就並不想它再改變,那麼它就是不變數。然而,實際上這個變數儲存的值還是可以改變。為了防止以後不小心給這些不想被改變的變數賦值,所以出現了關鍵字const

const就是為了禁止變數儲存的值改變而出現的。用const宣告的變數,當試圖改變這個變數的值,那麼出現編譯錯誤。所以const宣告的變數就是不變數。

由於const宣告的變數,後面不能被改變,所以const宣告的變數必須被初始化

基礎示例

#include <iostream> // std::cout std::endl

int main(void)
{
    const int a = 100; // 不變數a, 如果去掉賦值, 編譯就會報錯, 提示必須初始化
    std::cout << a << std::endl;

    // 如果去掉下面的註釋, 那麼編譯的時候會報錯, 說a是不能修改的
    // a = 2333;

    return 0;
}

輸出結果:

100

基礎講解

由於變數a宣告的時候使用了const,所以下面它儲存的值都不能發生變化。

建議:每個const變數宣告時單獨一行。

注意const變數的注意事項跟變數的注意事項相同。

基礎擴充

像數字123、字元`a`等這些字面量,它們是不變的值,所以它們是常量

上面程式碼中由於變數a儲存的是字面量,而且儲存的值不會改變,所以變數a也是常量。

constexpr

關鍵字constexpr是更嚴格的constconst作用是不改變變數,而constexpr變數儲存的值必須可以在編譯的時候求出來。這意味著constexpr變數必須儲存字面量:

int a = 1;
constexpr int b = 2;
constexpr int c = b;
// 去掉下一行程式碼將會報錯
// constexpr int d = a;

由於變數a的值在程式中可變,並不能在編譯期確定,所以constexpr int d = a;將會報錯。而constexpr int b = 2;,2是字面量,那麼constexpr宣告的變數b就可以在編譯期確定它的值,就是2,並且不會被改變。由於變數bconstexpr,所以constexpr int c = b;也是成立的;編譯器在編譯期會轉化成constexpr int c = 2;

初始值

我們知道,變數宣告時就應該初始化,接下來,來看看比較有意思的初始值。

基本資料型別初始化時,除了指定明確的數值以外,還可以使用預設初始值:

基礎示例

#include <iostream>

int main(void)
{
    int a = int();
    float b = float();
    unsigned long long c = unsigned long long();
    std::cout << a << std::endl;
    std::cout << b << std::endl;
    std::cout << c << std::endl;
    return 0;
}

輸出結果:

0
0
0

基礎講解

從程式碼中可以看出,基本資料型別的預設初始值就是基本資料型別後面加上(),如int()。而預設初始值的實際數值就是0

當然,你也可以在括號內寫數值,如int a = int(5);,那麼就等價於int a = 5;

補充知識(瞭解即可)

關鍵字constexpr是從C++11起加入。

相關文章