《C++Primer》第二章-變量和基本類型-學習筆記(2)-字面值常量&變量

《C++Primer》第二章-變量和基本類型-學習筆記(2)

日誌:
1,2020-02-26 筆者提交文章的初版V1.0

作者按:
最近在學習C++ primer,初步打算把所學的記錄下來。

傳送門/推廣
上一篇
《C++Primer》第二章-變量和基本類型-學習筆記(1)

字面值常量

字面值常量(literal constant):像 42 這樣的值(就像《銀河系漫遊指南》裏關於宇宙終極問題的答案"42"),在C++程序當作字面值常量。稱之爲字面值是因爲只能用它的值稱呼它》而稱之爲常量是因爲它的值不能修改。每個字面值都有相應的類型,例如:42 是 int 型,3.14159 是 double 型。只有內置類型存在字面值,而類類型是沒有字面值的。因此,也沒有任何標準庫類型的字面值。
常量可以是任何的基本數據類型,可分爲整型數字、浮點數字、字符、字符串和布爾值。

整型字面值規則

定義字面值整數常量可以使用以下三種進制中的任一種:十進制、八進制和十六進制
字面值整數常量的前綴指定了基數:0x 或 0X 表示十六進制,0 表示八進制,不帶前綴則默認表示十進制。

//下面是值20的三種進制的表示
20    // 十進制(decimal)
024   // 八進制(octal)
0x14  // 十六進制(hexadecimal)

字面值整數常量的後綴能夠強制將字面值整數常量轉換爲 long、unsigned 或 unsigned long類型。後綴是 U 和 L 的組合,U 表示無符號整數(unsigned),L 表示長整數(long)。後綴可以是大寫,也可以是小寫,U 和 L 的順序任意。(注意!定義長整型時,應該使用大寫字母 L。小寫字母 l 很容易和數值 1 混淆。)

//整型常量20帶着不同的後綴
20u   // 無符號整數 
20l   // 長整數 
20ul  // 無符號長整數
20lu  // 無符號長整數

浮點字面值規則

通常可以用十進制或者科學計數法來表示浮點字面值常量。使用科學計數法時,指數用 E 或者 e 表示。默認的浮點字面值常量爲 double 類型。在數值的後面加上 F 或 f 表示單精度。同樣加上 L 或者 l 表示擴展精度(再次提醒,不提倡使用小寫字母l)。

//下面每一組字面值表示相同的值: 
3.14159F    3.14159E0f
.001f       1E-3F
12.345L     1.2345E1L 
0e0         0.

布爾字面值規則

布爾常量只有兩個,而且都是標準的 C++ 關鍵字。

true 值代表真。
false 值代表假。

需要說明的是 true 和 false 的值不要當成1和 0,二者是有區別的。

字符字面值規則

字符常量可以是一個普通的字符(例如 ‘x’)、一個轉義序列(例如 ‘\n’),或者一個通用的字符(例如 '\u5427 ')
C11標準中引入了通用字符名(Universal Character Names)概念。通用字符名也是Unicode。C11中採用兩種方法來表示通用字符名:

  • \u 後面加正好4位十六進制數(只能是4位,不夠補0)。
  • \U 後面加正好8位十六進制數(只能是8位,不夠補0)。

這兩者也稱爲“短標識符”。\u****或 \U********中的 **** 或 ******** 部分所表示的值是Unicode碼點值(Unicode code point)

可打印的字符型字面值通常用一對單引號來定義:

'a'          'b'

這些字面值都是 char 類型的。在字符字面值前加 L 就能夠得到 wchar_t類型的寬字符字面值。例如:

L'a'          L'b'

非打印字符的轉義序列

有些字符是不可打印的。不可打印字符實際上是不可顯示的字符,比如退格或者控制符。還有一些在語言中有特殊意義的字符,例如單引號、雙引號和反斜線符號。不可打印字符和特殊字符都用轉義字符書寫。轉義字符都以反斜線符號開始.
C++ 語言中定義瞭如下轉義字符:

\\	\ 字符
\'	' 字符
\"	" 字符
\?	? 字符
\a	警報鈴聲
\b	退格鍵
\f	換頁符
\n	換行符
\r	回車
\t	水平製表符
\v	垂直製表符

\ooo與\xddd表示法

我們可以將任何字符表示爲以下形式的通用轉義字符:\ooo。這裏 ooo 表示三個八進制數字,這三個數字表示字符的數字值。
下面的例子是用 ASCII 碼字符集表示字面值常量:

\7 (bell)  \12 (newline)  \40 (blank)  \0 (null)  \062 ('2')  \115 ('M')

字符’\0’通常表示“空字符(null character)”,它有着非常特殊的意義。
同樣也可以用十六進制轉義字符來定義字符:它由一個反斜線符、一個 x 和一個或者多個十六進制數字組成。

"Hello\tWorld!\n" // string literal using newlines and tabs

字符串字面值規則

前面介紹的所有字面值都有基本內置類型,但是字符串字面值很複雜,並沒有基本內置類型。字符串字面值是一串常量字符。
字符串字面值常量用雙引號括起來的零個或者多個字符表示。不可打印字符表示成相應的轉義字符。

'h' // single quote: character literal  表示單個字符 h
"h" // double quote: character string literal 表示包含字母 h 和空字符兩個字符的字符串。

正如存在寬字符字面值,如

L'h'

也存在寬字符串字面值,一樣在前面加“L”,如

L"hello world" 

寬字符串字面值是一串常量寬字符,同樣以一個寬空字符結束。

字符串字面值的連接

兩個相鄰的僅由空格、製表符或換行符分開的字符串字面值(或寬字符串字面值),可連接成一個新字符串字面值。這使得多行書寫長字符串字面值變得簡單:

// concatenated long string literal  連接長字符串常量
std::cout << "a multi-line "
"string literal "
"using concatenation"
65
<< std::endl;

//上述輸出的結果是
a multi-line string literal using concatenation

如果連接字符串字面值和寬字符串字面值,將會出現什麼結果呢?例如:

// Concatenating plain and wide character strings is undefined
std::cout << "multi-line " L"literal " << std::endl;

//其結果是未定義的,也就是說,連接不同類型的行爲標準沒有定義。

這個程序可能會執行,也可能會崩潰或者產生沒有用的值,而且在不同的編譯器下程序的動作可能不同。

多行字面值

處理長字符串有一個更基本的(但不常使用)方法,這個方法依賴於很少使用的程序格式化特性:在一行的末尾加一反斜線符號可將此行和下一行當作同一行處理。
可以使用這個特性來編寫長字符串字面值:

// multiline string literal
std::cout << "a multi-line \
string literal \
using a backslash"
<< std::endl;
return 0;
}
//注意反斜線符號必須是該行的尾字符,並且不允許有註釋或空格符。

上面這種用法在一行輸入不下的情況下經常使用,在Linux命令行下輸入命令也是這種用法。

變量

變量的基本概念

變量(variable)提供了程序可以操作的有名字的存儲區。C++ 中的每一個變量都有特定的類型,該類型決定了變量的內存大小和佈局、能夠存儲於該內存中的值的取值範圍以及可應用在該變量上的操作集。C++ 程序員常常把變量稱爲“變量(variable)”或“對象(object)”。
變量是左值,因此可以出現在賦值語句的左邊。數字字面值是右值,因此不能被賦值。

變量名

變量名,即變量的標識符,可以由字母、數字和下劃線組成。變量名必須以字母或下劃線開頭,並且區分大小寫字母:C++ 中的標識符都是大小寫敏感的。變量的命名經驗參照《匈牙利命名法》。文件的命名經驗參照阮一峯的開發者手冊《爲什麼文件名要小寫?》

需要說明的是,C++ 保留了一組詞用作該語言的關鍵字。關鍵字不能用作程序的標識符。

asm do if return try auto double inline short typedef bool dynamic_cast int 
signed typeid break else long sizeof typename case enum mutable static union
catch explicit namespace static_cast unsigned char export new struct using
class extern operator switch virtual const false private template void
const_cast float protected this volatile continue for public throw wchar_t
default friend register true while delete goto reinterpret_cast
//上表中爲C++98/03中的63個關鍵字

同時下面兩篇文章也做了很好的總結

C++ 還保留了一些詞用作各種操作符的替代名。這些替代名用於支持某些不支持標準C++操作符號集的字符集。它們也不能用作標識符。表 2列出了這些替代名。

and bitand compl not_eq or_eq xor_eq
and_eq bitor not or xor
表2. C++ 還保留了一些詞用作各種操作符的替代名(11個)

變量的定義

每個定義都是以類型說明符開始,後面緊跟着以逗號分開的含有一個或多個
說明符的列表。分號結束定義。例如:

int A;
double B, C;
std::string D;
int E,F,
G;           //多個變量可以定義在同一條語句中:比如這個int E,F,G;還換行了

類型說明符指定與對象相關聯的類型:int 、double、std::string 都是類型名。其中 int 和 double 是內置類型,std::string 是標準庫定義的類型。

變量的初始化

變量定義指定了變量的類型和標識符,也可以爲對象提供初始值。定義時指定了初始值的對象被稱爲是已初始化的。
C++ 支持兩種初始化變量的形式:

  • 複製初始化(copy-initialization)。複製初始化語法用等號(=)
  • 直接初始化(direct-initialization)。直接初始化則是把初始化式放在括號中

例如:

int A(1024); //  直接初始化
int A = 1024; // 複製初始化,這裏是初始化,而不是賦值

C++ 中理解“初始化不是賦值”是必要的。初始化創建變量並給它賦初始值,而賦值則是擦除對象的當前值並用新值代替。使用=來初始化變量使得許多 C++ 編程新手感到迷惑,他們很容易把初始化當成是賦值的一種形式。但是在 C++ 中初始化和賦值是兩種不同的操作。這個概念特別容易誤導人,因爲在許多其他的語言中這兩者的差別不過是枝節問題因而可以被忽略。即使在 C++ 中也只有在編寫非常複雜的類時纔會凸顯這兩者之間的區別。但是這是一個很關鍵的概念.

初始化的形式

初始化“內置類型的對象”只有一種方法:提供一個值,並且把這個值複製到新定義的對象中。對內置類型來說,複製初始化和直接初始化幾乎沒有差別。
對類類型的對象來說,有些初始化僅能用直接初始化完成。這是由其構造函數決定的。

初始化多個變量

當一個定義中定義了兩個以上變量的時候,每個變量都可能有自己的初始化式。 對象的名字立即變成可見,所以可以用同一個定義中前面已定義變量的值初始化後面的變量。已初始化變量和未初始化變量可以在同一個定義中定義。前面說的兩種形式的初始化文法可以相互混合。

#include <string>
// ok: salary defined and initialized before it is used to initialize wage
double salary = 9999.99,
wage(salary + 0.01);
// ok: mix of initialized and uninitialized
int interval,
month = 8, day = 7, year = 1955;
// ok: both forms of initialization syntax used
std::string title("C++ Primer, 4th Ed."),
publisher = "A-W";

對象可以用任意複雜的表達式(包括函數的返回值)來初始化:

double price = 109.99, discount = 0.16;
double sale_price = apply_discount(price, discount);

變量初始化規則

當定義沒有初始化式的變量時,系統有時候會幫我們初始化變量。這時,系統提供什麼樣的值取決於變量的類型,也取決於變量定義的位置

內置類型變量的初始化
內置類型變量是否自動初始化取決於變量定義的位置。在函數體外定義的變量都初始化成 0,在函數體裏定義的內置類型變量不進行自動初始化。除了用作賦值操作符的左操作數,未初始化變量用作任何其他用途都是沒有定義的。未初始化變量引起的錯誤難於發現。

類類型變量的初始化
每個類都定義了該類型的對象可以怎樣初始化。類通過定義一個或多個構造函數來控制類對象的初始化。如果定義某個類的變量時沒有提供初始化式,這個類也可以定義初始化時的操作。它是通過定義一個特殊的構造函數即默認構造函數來實現的。這個構造函數之所以被稱作默認構造函數,是因爲它是“默認”運行的。如果沒有提供初始化式,那麼就會使用默認構造函數。不管變量在哪裏定義,默認構造函數都會被使用。
有些類類型沒有默認構造函數。對於這些類型來說,每個定義都必須提供顯式的初始化式。沒有初始值是根本不可能定義這種類型的變量的。(意思是說沒有默認構造函數的話,必須給它顯式初始化,否則出錯。

變量聲明和定義

變量的定義用於爲變量分配存儲空間,還可以爲變量指定初始值。在一個程序中,變量有且僅有一個定義。
變量的聲明用於向程序表明變量的類型和名字。定義也是聲明:當定義變量時我們聲明瞭它的類型和名字。
可以通過使用extern 關鍵字聲明變量名而不定義變量
不定義變量的聲明包括對象名、對象類型和對象類型前的關鍵字extern:

extern int i; // declares but does not define i
int i; // declares and defines i

extern 聲明不是定義,也不分配存儲空間事實上,它只是說明變量定義在程序的其他地方。程序中變量可以聲明多次,但只能定義一次。
只有當聲明也是定義時,聲明纔可以有初始化式,因爲只有定義才分配存儲空間。初始化式必須要有存儲空間來進行初始化。

如果聲明有初始化式,那麼它可被當作是定義,即使聲明標記爲 extern:

extern double pi = 3.1416; // definition

上述代碼雖然使用了 extern ,但是這條語句還是定義了 pi,分配並初始化了存儲空間。只有當 extern 聲明位於函數外部時,纔可以含有初始化式。

聲明和定義之間的區別可能看起來微不足道,但事實上卻是舉足輕重的。

在 C++ 語言中,變量必須且僅能定義一次,而且在使用變量之前必須定義或聲明變量。(聲明可以多次啦)

任何在多個文件中使用的變量都需要有與定義分離的聲明。在這種情況下,一個文件含有變量的定義,使用該變量的其他文件則包含該變量的聲明(而不是定義)。//需要使用某個變量的時候,要麼聲明它,要麼定義它。

在變量使用處定義變量

一般來說,變量的定義或聲明可以放在程序中能擺放語句的任何位置。變量在使用前必須先聲明或定義。

通常把一個對象定義在它首次使用的地方是一個很好 的辦法。

在對象第一次被使用的地方定義對象可以提高程序的可讀性。讀者不需要返回到代碼段的開始位置去尋找某一特殊變量的定義,而且,在此處定義變量,更容易給它賦以有意義的初始值。
放置聲明的一個約束是,變量只在從其定義處開始到該聲明所在的作用域的結束處纔可以訪問。必須在使用該變量的最外層作用域裏面或之前定義變量。

參考資料

【1】C++ Primer 中文版(第四版·特別版)
【2】C++ 的關鍵字(保留字)完整介紹
【3】C++關鍵字詳解

註解

【4】左值(L-value):左值可以出現在賦值語句的左邊或右邊。
右值(R-value):右值只能出現在賦值的右邊,不能出現在賦值語句的左邊。
【5】什麼是對象(object)?:C++ 程序員經常隨意地使用術語對象。一般而言,對象就是內存中具有類型的區域。
說得更具體一些,計算左值表達式就會產生對象。嚴格地說,有些人只把術語對象用於描述變量或類類型的值。有些人還區別有名字的對象和沒名字的對象,當談到有名字的對象時一般指變量。還有一些人區分對象和值,用術語對象描述可被程序改變的數據,用術語值描述只讀數據。
《C++ primer》遵循更爲通用的用法,即對象是內存中具有類型的區域。我們可以自由地使用對象描述程序中可操作的大部分數據,而不管這些數據是內置類型還是類類型,是有名字的還是沒名字的,是可讀的還是可寫的。

本文許可證

本文遵循 CC BY-NC-SA 4.0(署名 - 非商業性使用 - 相同方式共享) 協議,轉載請註明出處,不得用於商業目的。
CC BY-NC-SA 4.0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章