C++基礎總結:變量和基本類型
文章目錄
一、基本內置類型
C++基本數據類型爲算數類型和空類型(void)。算數類型包含字符、整數型、浮點型和布爾值。
1. 算數類型
算數類型分爲整型(包括字符和布爾值)和浮點型。
類型 | 含義 | 最小尺寸 |
---|---|---|
bool | 布爾類型 | 未定義 |
char | 字符 | 8位 |
wchar_t | 寬字符 | 16位 |
char16_t | Unicode字符 | 16位 |
char32_t | Unicode字符 | 32位 |
short | 短整型 | 16位 |
int | 整型 | 16位 |
long | 長整型 | 32位 |
long long | 長整型 | 62位 |
float | 單精度浮點型 | 6位有效數字 |
double | 雙精度浮點型 | 10位有效數字 |
long double | 擴展精度浮點型 | 10位有效數字 |
基本字符類型是char,一個char的大小和一個機器字節一樣,大多數機器字節由8比特構成。
*注:可尋址的最小內存塊稱爲“字節(byte)”,存儲的基本單元稱位“字(word)”。一個字節由8bit構成,字則由32或64 bit構成,也就是4或8 byte。
帶符號類型和無符號類型
除布爾型和擴展的字符型之外,其他整型可以分爲帶符號(signed) 和 無符號(unsigned)。
對於int、short、long和long long類型,都是帶符號的,其signed標識符可省略,在類型前加unsigned,可使其成爲無符號類型。unsigned int可省略爲unsigned。
與上面類型不同的是,字符型被分爲三種:char、signed char和unsigned char,實際爲signed char和unsigned char。char表現爲signed char和unsigned char中的一種,具體由編譯器決定。
2. 類型轉換
類型所能表示的值的範圍決定了轉換的過程
- 非布爾類型轉換爲布爾類型:0爲false,否則爲true;
- 布爾值賦給非布爾類型:false爲0,否則爲1;
- 浮點數賦給整數型:僅保留小數點之前的部分;
- 整數值賦給浮點值:小數部分記爲0;
- 賦給無符號類型超過範圍的值:初始值對無符號類型表示數值總數取模後的餘數;
- 賦給帶符號類型超過範圍的值:結果是未定義。
3. 字面值常量
字面值可以通過字面形式看出其數據類型,用於將值按照其具體數據類型保存到內存。例如20是int類型,20L是long類型。
整型和浮點型字面值
整型字面值可以寫作十進制、八進制和十六進制形式:
- 默認爲十進制: 20
- 以0開頭的整數爲八進制:024
- 以0x或0X開頭爲十六進制:0x14
浮點型字面值表現爲小數或以科學計數法表示的指數,其中指數部分用E或e標識:
3.14159 3.14159E0 0. 0e0 .001
字符和字符串字面值
單引號括起來的一個字符稱爲char型字面值,雙引號括起來的0個或多個字符則構成字符串的字面值。
轉義序列
字符 | 含義 | 字符 | 含義 | 字符 | 含義 |
---|---|---|---|---|---|
\n | 換行符 | \t | 橫向製表符 | \a | 報警符 |
\v | 縱向製表符 | \b | 退格符 | \" | 雙引號 |
\\ | 反斜線 | \? | 問號 | \’ | 單引號 |
\r | 回車符 | \f | 進紙符 |
指定字面值的類型
字符和字符串字面值
前綴 | 含義 | 類型 |
---|---|---|
u | Unicode16字符 | char16_t |
U | Unicode32字符 | char32_t |
L | 寬字符 | wchar_t |
u8 | UTF-8(僅用於字符串字面常量) | char |
整型字面值
後綴 | 最小匹配類型 |
---|---|
u or U | unsigned |
l or L | long |
ll or LL | long long |
浮點型字面值
後綴 | 類型 |
---|---|
f or F | float |
l or L | long double |
例:
L’a’ //寬字符型字面值,類型是wchar_t
u8"hi!" //utf-8字符串字面值(utf-8用8位編碼一個Unicode字符)
42ULL //無符號整型字面值,類型是 unsigned long long
1E-3F //單精度浮點型字面值,類型是float
3.14159L //擴展精度浮點型字面值,類型是long double
布爾字面值和指針字面值
布爾類型:true和false是布爾類型字面值
指針:nullptr是指針字面值
二、變量
1. 變量定義
對象是指一塊能存儲數據並具有某種類型的內存空間。
初始化不是賦值:初始化時創建變量時賦予其一個初始值,而賦值是把對象的當前值擦除,以一個新值替代。
列表初始化
- 使用花括號初始化變量的方式稱作列表初始化
- 內置類型使用列表初始化的特點:如果使用列表初始化存在丟失信息的風險,則編譯器將報錯。
默認初始化
- 如果定義變量時沒有指定初始值,則變量被默認初始化
- 如果內置類型的變量沒有顯式初始化,它的值由定義的位置決定
- 定義於任何函數體之外的變量被初始化爲0
- 定義在函數體內部的內置類型變量將不被初始化,一個未被初始化的內置類型變量的值是未定義的,如果試圖拷貝或訪問,將會報錯。建議初始化每個內置類型的變量。
- 類決定其初始化對象的方式,是否允許不經初始化就定義對象也由類決定,同時可以決定初始值
2. 變量聲明和定義的關係
- 變量聲明規定了變量的類型和名字,定義還申請了存儲空間,也可能會爲變量賦一個初始值。
- 如果想申明一個變量而非定義它,就在變量名前添加關鍵字extern,並且不要顯式地初始化變量
例:extern int i; //聲明i而非定義i
int j; //聲明並定義j
*變量能且只能被定義一次,但可以被多次聲明 - 變量的定義必須出現在且只能出現在一個文件中,而其它用到該變量的文件必須對其進行聲明,絕對不能重複定義。
三、複合類型
1. 引用
- 引用是爲對象起另外一個名字,因不能給引用重新綁定另一對象,所以引用必須初始化。
- 因爲引用不是對象,所以不能定義引用的引用。
2. 指針
- 指針是指向另一類型的符合類型。
- 指針本身就是一個對象,允許對指針賦值和拷貝
- 指針無須再定義時賦初值,和其它內置類型一樣,在塊作用域內定義的指針如果沒有初始化,將擁有一個不確定的值
- 指針的四個狀態:
1 指向一個對象
2 指向緊鄰對象所佔空間的下一個位置
3 空指針,意味着指針沒有指向任何對象
4 無效指針,也就是上述情況之外的其它值 - 如果拷貝或訪問無效指針都將引發錯誤
- void*指針
- void*是特殊的指針類型,可用於存放任意的對象類
- 輸出void*指向的值,可以先轉換爲對應類型的指針,再取值。例:*(int*)p
四、const限定符
- const int i = get_size() //正確:運行時初始化
const int j = 42; //正確:編譯時初始化
const int k; //錯誤:k是未經初始化的常量 - 編譯時初始化時,編譯器將在編譯過程中把用到該變量的地方都替換成對應的值
- 爲避免對同一個變量的重複定義,默認情況下,const對象被設定爲僅在文件內有效。當多個文件中出現了同名的const變量時,其實等同於在不同文件中分別定義了獨立的變量。
1. const引用
- const引用可以綁定到一個普通對象或常量對象,但不能通過const引用修改其值
- const引用可以綁定非常量的對象、字面值,甚至是一個表達式
- 當const引用綁定另一個類型對象時:
double dval = 3.14;
const int& ri = dval;
其實實際出現的轉換爲:
const int temp = dval;
const int& ri = temp;
所以,修改dval的值後,ri的值仍爲3
2. 指針和const
指針和const組合,會出現三種形式:
const int *p; //p是一個指針,指向int類型,不能改變指針p指向對象的值。p也叫指針常量
int* const p; //指針p指向int類型對象,可以改變int對象的值,但是不能改變指針p的指向。p也叫常量指針
const int* const p; //指針p指向int類型對象,不能改變int對象的值,也不能改變指針p的指向
3. constexpr和常量表達式
- 常量表達式是指值不會改變,並且在編譯過程就能得到計算結果的表達式。所以,字面值和用常量表達式初始化的const對象也是常量表達式。
- 判斷一個變量是不是常量表達式,可以通過它的值是否是在運行時計算所得或者在運行時是否會改變來判斷。
- constexpr變量
- 可以將變量聲明爲constexpr類型以便由編譯器來驗證變量的值是否是一個常量表達式
- 一般情況下,如果認定變量是一個常量表達式,那就把它聲明成constexpr
- 字面值類型
- 顯而易見的值,就把它成爲“字面值類型”
- 算數類型、引用和指針都是字面值類型
- 雖然指針和引用都能定義成constexpr,但是constexpr指針的初始值必須是nullptr或者0,或者是存儲於某個固定地址中的對象
- 指針和constexpr
- constexpr限定符僅對指針有效,與指針所指對象無關
const int* p = nullptr; //p是指向整形常量的指針
constexpr int* p = nullptr; //p是指向整數的常量指針 - constexpr指針既可以指向常量,也可以指向非常量
constexpr int* np = nullptr;
int j = 0;
constexpr int i = 42;
//i和j都必須定義在函數體外
constexpr const int* p = &i; //p是常量指針,指向整型常量i
constexpr int* p1 = &j; //p1是常量指針,指向整數j
- constexpr限定符僅對指針有效,與指針所指對象無關
五. 處理類型
1. 類型別名
-
有兩種方法可以定義類型別名:typedef和using
- typedef
typedef double wages; //wages是double同義詞
typedef wages base, *p; //base是double的同義詞,p是double*的同義詞 - using
using SI = Sales_item; //SI是Sales_time的同義詞
- typedef
-
指針、常量和類型別名
typedef char* pstring;
const pstring cstr = 0; //cstr是指向char的常量指針,所以不能修改指針,但可以修改指針指向的值。所以它等價的是char* const cstr = 0,而不是const char* cstr = 0。
const pstring* ps; //ps是指向char常量指針的指針,所以不能修改它指向的指針的指向,但可以修改ps的指向和指向指針對象指向的值
2. auto類型說明符
- auto讓編譯器通過初始值來推算變量的類型,所以,auto定義的變量必須有初始值
- auto一般會忽略掉頂層const,同時底層const則會保留下來,比如初始值是指向常量的指針時:
int i = 0;
const int ci = i, &cr = ci;
auto b = ci; //b是一個整數
auto c = cr; //c是一個整數
auto d = &i; //d是一個指向整型的指針
auto e = &ci; //e是一個指向整數常量的指針 - 如果希望推斷出的類型有頂層const,需要明確指出
const auto f = ci; - 或者將引用類型設置爲auto
auto &g = ci;
auto &h = 42; //錯誤:不能爲非常量引用綁定字面值
const auto &j = 42; //正確:可以爲常量引用綁定字面值
3. decltype類型指示符
- decltype可以實現從表達式的類型推斷出要定義的變量的類型,而不使用其值初始化變量
decltype(f()) sum = x; //sum的類型就是函數f的返回類型
編譯器在推斷類型時,並不實際調用,而僅僅使用返回值類型 - decltype和auto處理頂層const和引用的方式有一點不同:
如果decltype使用的表達式是一個變量,則decltype返回該變量的類型(包括頂層const和引用在內)
const int ci = 0, &cj = ci;
decltype(ci) x = 0; //x的類型是const int
decltype(cj) y = x; //y的類型是const int&, y綁定到x
decltype(cj) z; //錯誤:z是引用,所以必須初始化
4. decltype和引用
- 如果表達式的內容是解引用操作,則decltype將得到引用類型
int i = 42, *p = &i;
decltype(*p) c; //錯誤:c是int&,必須初始化 - 如果decltype的表達式加上一層或多層括號,結果將是引用
decltype((i)) d; //錯誤:d是int&,必須初始化
decltype(i) e; //正確:e是一個未初始化的int
總結:decltype((variable))有雙層括號,結果永遠是引用,而decltype(variable)結果只有當variable本身是一個引用時纔是引用