C++定義了一套包括算術(shù)類型(arithmetic type) 和 空類型(void)在內(nèi)的基本數(shù)據(jù)類型。算數(shù)類型包括字符型、整型、布爾型和浮點型??疹愋筒粚?yīng)具體的值,大多數(shù)情況下當(dāng)函數(shù)不返回任何值的時候作為返回類型。
算數(shù)類型分為兩類:整型(字符型、布爾型,整型)和 浮點型。不同類型所占字節(jié)的大小在不同的機器上有所差異
類型 | 含義 | 最小尺寸 |
---|---|---|
bool | 布爾類型 | 未定義 |
char | 字符 | 8位 |
wchar_t | 寬字符 | 16位 |
char16_t | Unicode 字符 | 16位 |
char32_t | Unicode 字符 | 32位 |
short | 短整型 | 16位 |
int | 整型 | 16位 |
long | 長整型 | 32位 |
long long | 長整型 | 64位 |
float | 單精度浮點數(shù) | 6位有效數(shù)字 |
double | 雙精度浮點數(shù) | 10位有效數(shù)字 |
long double | 拓展精度浮點數(shù) | 10位有效數(shù)字 |
有符號類型和無符號類型
對象類型定義了對象能包含的數(shù)據(jù)和能參與的運算,其中一種運算被大多數(shù)類型支持,就是將對象從一種給定的數(shù)據(jù)類型轉(zhuǎn)換為另一種相關(guān)類型。
bool b = 42; // b 為true
int i = b; // i 為 1
i = 3.14; // i 為 3
double pi = i; //pi 為 3.0
unsigned char c = -1; //c 為 255
signed char c2 = 256;//c2 未定義
含有無符號類型的表達式:
unsigned u = 10;
int i = -42;
cout<< i + i<< endl; //84
cout<< u + i<< endl; //4294967264 即 -32 的二進制表示 看作無符號數(shù) 就是4294967264
當(dāng)一個表達式中既有無符號數(shù)又有int值時,這個int值就會轉(zhuǎn)換成無符號數(shù)。負數(shù)轉(zhuǎn)換為無符號數(shù)類似于直接給無符號數(shù)賦一個負值。
**注意:**如果在循環(huán)中使用無符號數(shù),可能會造成死循環(huán)
//會一直死循環(huán)下去
for (unsigned u = 10; u >= 0; u--) {cout<< u<< endl;
}
3.字面值常量一個形如42的值被稱為字面值常量,這樣的值一看就知。每一個字面值常量都對應(yīng)一種數(shù)據(jù)類型,字面值常量的形式和值決定了它的數(shù)據(jù)類型。
我們可以將整型字面值寫作10進制數(shù),8進制數(shù) 或 16進制數(shù)。默認情況下 10進制字面值是有符號數(shù),8進制數(shù) 和 16進制字面值既可能是有符號數(shù)也可能是無符號數(shù)。short沒有對應(yīng)的字面值
int a = 1;//10進制數(shù)
int b = 02;//8進制數(shù)
int c = 0x123;//16進制數(shù)
由單引號括起來的是char字面值,雙引號括起來的零個或多個字符稱為字符串字面值。字符串字面值的類型實際上是由常量字符構(gòu)成的數(shù)組array。
char c = 'a'; //字符字面值
string s = "abcd"; //字符串字面值
true 和 false 是布爾型的字面值。nullptr 是 指針字面值。
bool ok = true;
int * ptr = nullptr;
二、變量變量提供一個具有名字的,可供程序操作的內(nèi)存空間。C++中每一個變量都有其數(shù)據(jù)類型,數(shù)據(jù)類型決定著變量所占內(nèi)存空間的大小和布局方式,該內(nèi)存空間能存儲值的范圍,以及變量能參與的運算。
1. 變量定義形式:類型說明符 + 一個或者多個變量名組成的列表(其中變量名以逗號分隔)
什么是對象?
1.初始值通常情況下,對象是指一塊能存儲數(shù)據(jù) 并且 具有某種類型的內(nèi)存空間
當(dāng)對象在創(chuàng)建時獲得了一個特定的值,這個對象就被初始化了。
在C++中,初始化是一個非常復(fù)雜的問題,實際上初始化和賦值是兩個完全不同的操作。初始化不是賦值,初始化的含義是創(chuàng)建對象時賦予其一個初始值,而賦值的含義是把對象的當(dāng)前值擦除,而以一個新的值來替代。
作為C++ 11 的標(biāo)準(zhǔn),可以使用花括號來初始化變量,這種初始化形式稱為列表初始化。
//以下四種方式都可以初始化
int a = 10;
int b(10);
int c = {10 };
int d{10 };
注意:如果使用列表初始化 并且 初始化存在丟失信息的風(fēng)險時,編譯器會報錯
long double ld = 3.14159;
int a{ld }, b = {ld }; //編譯器報錯
int c(ld), d = ld; //正常執(zhí)行,丟失了小數(shù)部分的精度
3.默認初始化如果定義變量的時候沒有指定初始值,則變量被默認初始化,此時變量也被賦予默認值。
建議初始化每一個內(nèi)置類型的變量
2.變量聲明和定義的關(guān)系C++支持分離式編譯機制,該機制允許將程序分割為若干個文件,每一個文件都可以被獨立編譯。
如果想聲明一個變量而不是定義,可以在變量名前面添加 extern ,并且不要顯式的初始化變量
extern int a; //聲明 a
int b; //聲明 并且 定義 b
extern 語句如果包含初始值就不再是聲明了,而變成定義。如果在函數(shù)體內(nèi)部,初始化一個由extern標(biāo)記的變量,會引起錯誤:
void test2() {extern int a; //聲明 a
int b; //聲明 并且 定義 b
extern int c = 20; //報錯
}
注意:變量只能被定義一次,但是可以被聲明多次
如果要在多個文件中使用同一個變量就必須將聲明和定義分開。變量的定義只能出現(xiàn)在一個文件中,而其他用到該變量的文件必須對其進行聲明,絕對不能重復(fù)定義。
C++是一種靜態(tài)類型語言,其含義是在編譯階段檢查類型。其中,檢查類型的過程稱為類型檢查。
3.標(biāo)識符C++的標(biāo)識符由字母、數(shù)字和下劃線組成,其中必須以字母或者下劃線開頭。用戶自定義的標(biāo)識符中不能連續(xù)出現(xiàn)兩個下劃線,也不能以下劃線緊鄰大寫字母開頭。
變量名定義規(guī)范:
作用域是程序的一部分,在其中變量名有其特定的含義。C++中大多數(shù)作用域都以花括號分隔。
變量名的有效區(qū)域 始于變量名的聲明語句,以聲明語句所在的作用域末端為結(jié)束。
如果函數(shù)有可能用到某全局變量,則不宜再定義一個同名的局部變量
三、復(fù)合類型復(fù)合類型是指基于其他類型定義的類型。本節(jié)介紹其中的兩種:引用 和 指針
1. 引用引用為對象去了另一個名字,引用類型引用另外一種類型(這里的引用指的是左值引用)。定義引用時,程序把引用和它的初始值綁定到一起,而不是將初始值拷貝給引用。
int val = 10;
int& refval = val; //refval 是 val的另外一個名字
注意:引用并不是對象。相反,他只是為一個已經(jīng)存在的對象所起的另外一個變量名。因為引用不是對象,所以不能定義引用的引用
1.引用的定義允許再一條語句中定義多個引用,其中每一個引用標(biāo)識符都必須&開頭。除了兩種特殊情況外,其他所有引用的類型都要和與之綁定的對象嚴(yán)格匹配。并且,引用只能綁定在對象上,而不能與字面值或某個表達式的計算結(jié)果綁定到一起。
int& ref = 10; //錯誤:引用類型的初始值必須是一個對象
double dval = 3.14;
int& ref2 = dval; //錯誤:此處引用類型的初始值必須是int類型的
2.指針指針是指向另一種類型的復(fù)合類型。和引用類似,指針也實現(xiàn)了對其它對象的間接訪問。不同點:指針本身也是一個對象,允許對指針進行賦值和拷貝操作,而且在指針的生命周期內(nèi)他可以先后指向幾個不同的對象;指針不用再定義的時候賦初始值。
1.獲取對象的地址指針存放某個對象的地址,要想獲取該地址,需要使用取地址符&:
int a = 10;
int* p = &a; //p存放變量a的地址 或者說 p 是指向變量a 的指針
因為引用不是對象,沒有實際地址,所以不能定義指向引用的指針
除了兩種特殊的情況外,其他所有指針的類型都要和它所指向的對象的類型匹配:
double dval = 3.14;
double* pd = &dval;
double* pd2 = pd;
int* pi = pd; //錯誤:指針pi的類型和pd的類型不匹配
pi = &dval; //錯誤:試圖把double類型對象的地址賦給int型指針
2.指針值指針的值(地址)應(yīng)該屬于以下4種狀態(tài)之一:
試圖拷貝 或者以其他的方式訪問無效指針的地址都會引發(fā)錯誤
雖然第二種和第三種指針的形式是有效的,但是這些指針沒有指向任何具體的對象,所以試圖訪問此類指針的對象的行為是不被允許的
3.利用指針訪問對象如果指針指向了一個對象,則允許使用 解引用符 * 來訪問該對象:
int a = 42;
int* p = &a;
cout<< *p<< endl; //由* 得到指針p指向的對象,即42
注意:解引用操作僅適用于那些確定指向了某個對象的有效指針
4.空指針空指針(null pointer) 不指向任何對象,在試圖使用一個指針之前可以先判斷它是否為空
int* p1 = nullptr;
int* p2 = 0; //直接將p2 初始化為字面常量0
int* p3 = NULL; //等價于 int *p3 = 0;
推薦使用 nullptr 初始化空指針
把 int 變量直接賦值給指針是錯誤操作,即使這個int 變量的值恰好為0也不行
int zero = 0;
int* pi = zero; //錯誤:不能把int 變量直接賦值給指針
建議初始化所有指針
5.賦值和指針指針和引用都能夠間接的訪問其他對象,但是二者又有很大的不同,其中最重要的一點就是引用并不是一個對象。一旦定義了引用,就不能將其再綁定到另外的對象上。指針和它存放的地址之間就沒有這種限制了。
int a = 10;
int* p1 = 0;//p1初始化為空指針 不指向任何對象
int* p2 = &a;//p2 被初始化 指向 a
int* p3; //p3 沒有被初始化
p3 = p2; //現(xiàn)在 p2 和 p3 指向同一個對象 a
p2 = 0; //現(xiàn)在p2 不指向任何對象了
6.其他指針操作void* 是一種特殊的指針類型,可用于存放任意對象的地址。一個void*指針存放一個地址,這一點和其他指針類似。不同的是,我們不清楚這個指針指向什么類型的對象。
double a = 3.14, * p = &a;
void* pv = &a; //a 可以是任意類型的對象
pv = p; //pv 可以是任意類型的指針
void指針的功能比較少,不能直接操作void指針?biāo)傅膶ο?,因為我們并不知道他這個對象是什么類型。
概括說來,以void* 視角來看內(nèi)存空間也就只是內(nèi)存空間,沒辦法訪問內(nèi)存空間中的所有對象。
3.理解復(fù)合類型的聲明 1.定義多個變量經(jīng)常會有一種觀點,在定義語句時,類型修飾符(* 或 &)作用于本次定義的全部變量
int* p1, p2; //p1 是指向int的指針,p2 是int
2.指向指針的指針指針也是內(nèi)存中的對象,也有自己的地址,因此允許把指針的地址放到另一個指針當(dāng)中,即允許另外一個指針指向指針。
int a = 10;
int* p = &a; //p 指向一個 int 類型的數(shù)
int** pp = &p; //pp 指向一個 int 類型的指針
//要訪問最原始的那個對象,需要對指針的指針做兩次解引用
cout<< **pp<< endl; //a 的值
3.指向指針的引用引用本身不是一個對象,因此不能定義指向引用的指針。但指針是對象,所以存在對指針的引用
int a = 10;
int* p;
int*& r = p; // r 是一個對指針 p 的引用
r = &a; //r 引用了一個指針 因此給 r 賦值 &a ,就是讓指針 p 指向 a
*r = 0; //讓 p 指向的對象 也就是 a 的值變?yōu)?
四、const限定符有時我們希望第一這樣的一種變量,它的值不能夠被改變,可用const對變量的類型加以限定。因為const對象一旦創(chuàng)建后,其值就不能夠再修改,所以const對象必須初始化
const int a = get_size();// 運行時初始化
const int b = 42;//編譯時初始化
const int c; //錯誤:未初始化
1.初始化和const可以利用普通變量去初始化const變量
int a = 42;
const int b = a; //正確
int c = a; //正確
拷貝一個對象的值并不會改變它,一旦拷貝完成,新的對象就和原來的對象沒什么關(guān)系了。
2.默認情況下,const對象僅在文件內(nèi)有效編譯器將在編譯過程中把用到該變量的地方都替換為對應(yīng)的值。
為了避免對同一個變量的重復(fù)定義。默認情況下,const對象被設(shè)定為僅在文件內(nèi)有效。當(dāng)多個文件出現(xiàn)了同名的const變量時,其實等同于在不同的文件中分別定義了獨立的變量。
如果想在多個文件之間共享const對象,必須在變量的定義之前添加extern。
1.const的引用可以把引用綁定到const對象上,即對常量的引用。與普通引用不同的是,對常量的引用不能修改它所綁定的值。
const int a = 1024;
const int& r = a; //正確
r = 20; //錯誤:r是對常量的引用 不能改變
int& r2 = r; //錯誤:試圖讓一個非常量引用 指向 一個常量對象
1.初始化和對常量的引用之前提到的 引用類型 必須和其所引用的 對象類型 一致,但是有兩種例外。其中一種就是在初始化常量引用時允許用任意表達式作為初始值,只要表達式的結(jié)果能轉(zhuǎn)換成引用的類型即可。
int a = 10;
const int& r1 = a; //正確:允許將一個const int& 綁定到一個普通int對象上
const int& r2 = 42; //正確
const int& r3 = r1 * 2; //正確
int& r4 = r1 * 2; //錯誤:r4 是一個普通的非常量引用
2.對const的引用,可能用一個非const的對象常量引用僅僅對可參與的操作做了限定,對于引用的對象本身是不是常量并未做限定。因為對象也可能是一個變量,所以允許通過其他途徑改變它的值。
int a = 10;
const int& r1 = a; //正確:允許將一個const int& 綁定到一個普通int對象上
const int& r2 = 42; //正確
const int& r3 = r1 * 2; //正確
int& r4 = r1 * 2; //錯誤:r4 是一個普通的非常量引用
2.指針和const與引用一樣,也可以讓指針指向常量或者變量。類似于常量引用,指向常量的指針不能改變其指向的值。想要存放常量對象的地址,只能使用指向常量的指針。
const int a = 5; //a 是一個常量 它的值不能改變
int* ptr = &a; //錯誤: ptr 是一個普通指針
const int* cptr = &a; //正確: cptr 可以指向一個整型常量
*cptr = 42; //錯誤: 不能給 *cptr 賦值
之前提到過,指針的類型必須與所指向的對象類型一致,其中一種例外是允許一個指向常量的指針指向變量。
int a = 10;
const int* cptr = &a; //正確:但是不能通過 cptr 改變 a 的值
和常量引用一樣,指向常量的指針也沒有規(guī)定所指向的對象必須是常量。
1.const指針指針是對象而引用不是,因此可以把指針本身定義為常量。常量指針必須初始化,一旦初始化,它的指向就不能改變了。
int a = 10;
int* const p = &a; // 指針 p 就一直指向 a了,不能改變其指向了
指針本身是一個常量,也可以通過指針去修改其指向?qū)ο蟮闹?,只是指針的指向不能改變而?/p>3.頂層const
頂層const表示指針本身是一個常量,底層const表示指針?biāo)赶虻膶ο笫且粋€常量。
更一般的,頂層const可以表示任意對象是常量,這一點對任何數(shù)據(jù)類型都適用。
底層const則與指針和引用等復(fù)合類型有關(guān)。比較特殊的是:指針既可以是頂層const,也可以是底層const
int a = 0;
int* const p1 = &a; //不能改變 p1 的值,頂層const
const int ci = 42; //不能改變 ci 的值,頂層const
const int* p2 = &ci;// 允許改變 p2 的值 , 底層const
const int* const p3 = p2; //右邊時頂層const , 左邊的是底層const
const int& r = ci; //聲明引用的const 都是底層const
當(dāng)執(zhí)行對象拷貝操作時,常量是頂層const 還是 底層const區(qū)別明顯。頂層const不受影響:
a = ci; //ci 是一個頂層const 對此操作無影響
p2 = p3; //p2 p3 指向的對象類型相同,p3 頂層 const 的部分不受影響
執(zhí)行拷貝操作并不會改變對象的值,因此拷入拷出的對象是不是常量都沒什么影響
底層const的限制不能忽視。當(dāng)執(zhí)行拷貝操作時,拷入拷出的對象必須具有相同的底層const,或者兩個對象的數(shù)據(jù)類型能夠轉(zhuǎn)換。變量可以轉(zhuǎn)為常量,反之不行。
int* p = p3; //錯誤: p3 有底層const p沒有
p2 = p3; //正確: p2 和 p3 都是底層 const
p2 = &a; //正確: int* 能轉(zhuǎn)換為 const int*
int& r = ci; //錯誤:普通的int& 不能綁定到 int常量上
const int& r2 = a; //正確: const int& 可以綁定到一個普通int上
4.constexpr和常量表達式常量表達式是指值不會改變并且在編譯過程就能得到計算結(jié)果的表達式。顯然字面值屬于常量表達式,用常量表達式初始化的const對象也是常量表達式。
一個對象是不是常量表達式由它的數(shù)據(jù)類型和初始值共同決定。
const int max_files = 20; //是
const int limit = max_files + 1; //是
int staff_size = 27; //不是常量表達式
const int sz = get_size(); //不是常量表達式 get_size 的具體值 需要到運行時才能獲取到,所以不是
1.constexpr 變量C++ 11 標(biāo)準(zhǔn)規(guī)定,允許將變量聲明為constxepr類型以便由編譯器來驗證變量的值是否為一個常量表達式。聲明為constexpr的變量一定是一個常量,而且必須用常量表達式初始化
constexpr int mf = 20; //20 是常量表達式
constexpr int limit = mf + 1; // mf + 1 是常量表達式
const int sz = size(); // 只有當(dāng) size 是一個 constxepr函數(shù)時,才正確
一般來說,如果你認定一個變量是常量表達式,那就把它聲明為constexpr類型
2.字面值類型常量表達式的值需要編譯時就得到計算,因此聲明constexpr時 用到的類型必須有所限制。
算數(shù)類型,引用和指針都屬于字面值類型。
盡管指針和引用都能定義成constexpr,但是它們的初始值卻受到嚴(yán)格限制。一個constexpr指針的初始值必須為nullptr 或 0,或者是存儲與某個固定地址中的對象。
函數(shù)體內(nèi)部的變量一般來說并非存放在固定地址中,定義于函數(shù)體之外的對象地址固定不變,能用來初始化constexpr指針。
3.指針和constexpr在constexpr聲明中,如果定義了一個指針,限定符constexpr僅對指針有效,與指針?biāo)赶虻膶ο鬅o關(guān)系。
const int* p = nullptr; //p 是一個指向整型常量的指針
constexpr int* q = nullptr; // q 是一個指向 整數(shù)的常量指針
constexpr把它所定義的對象置為了頂層const
五、處理類型 1.類型別名類型別名(type alias) 是一個名字,他是某種類型的同義詞。
有兩種方式可用于定義類型別名。傳統(tǒng)的方式是使用關(guān)鍵字typedef
typedef double wages; //wages 是 double 的同義詞
typedef wages base, * p; //base 是 double 的同義詞,p 是 double* 的同義詞
C++ 11 標(biāo)準(zhǔn)規(guī)定了一種新的方法,使用別名聲明(alias declaration) 來定義類型別名
using PII = pair; //PII 是 pair的同義詞
建議使用新標(biāo)準(zhǔn)的方法
1.指針、常量和類型別名如果某一個類型別名指代的是復(fù)合類型或者常量,那么把他用到聲明語句中就會產(chǎn)生意外的結(jié)果。
typedef char* pstring;
const pstring cstr = 0; //cstr 是一個指向char 的常量指針
const pstring* ps; //ps 是一個指針 ,它的對象是指向char的 常量指針
2.auto 類型說明符C++ 11 標(biāo)準(zhǔn)引入了auto,auto能讓編譯器通過初始值來推算變量類型。顯然,auto定義的變量必須有初始值。
int a = 10, b = 20;
auto c = a + b; //c 也是 int 類型
1.復(fù)合類型、常量和auto編譯器推斷出來的auto類型有時候和初始值的類型并不完全一樣,編譯器會適當(dāng)改變結(jié)果類型使其更符合初始化規(guī)則。
使用引用其實是使用引用的對象,特別是當(dāng)引用被用作初始值時,真正參與初始化的時引用對象的值。
int a = 20, & r = a;
auto b = r; // b = 20
auto 一般會忽略頂層const,保留底層const。
設(shè)置一個類型為auto的引用時,初始值中的頂層const常量屬性仍然保留。
3.decltype 類型指示符有時候會遇到這種情況:希望通過表達式的類型推斷出要定義的變量類型,但是不想用該表達式的值初始化變量。為了滿足這一要求,C++ 11 標(biāo)準(zhǔn)引入了decltype,作用是選擇并返回操作數(shù)的數(shù)據(jù)類型。編譯器會分析表達式得到它的類型,卻不實際計算表達式的值。
decltype(fun()) sum = 10; // sum 的類型就是 函數(shù)fun 的返回類型(編譯器實際上并不會調(diào)用fun函數(shù))
與auto不同的是,如果decltype使用的是一個變量,則decltype返回改變量的類型(包括頂層const和引用在內(nèi))
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是一個引用,必須初始化
1.decltype 和 引用如果decltype 使用的是一個表達式,則decltype 返回表達式結(jié)果對應(yīng)的類型。
另一方面,如果表達式內(nèi)容是解引用操作,則decltpye會得到引用類型。
int i = 42, * p = &i, & r = i;
decltype(r + 0) b; //r 是int&類型 , r + 0 就是 int 類型
decltype(*p) c; //錯誤: c 是 int&類型 , 需要初始化
如果變量名加上了一對括號,則得到的類型與不加括號時有所不同。如果給變量名加了一層或多層括號,編譯器會把它當(dāng)成時一個表達式。
//decltype 的表達式如果是加上了括號的變量 結(jié)果將是引用
decltype((i)) d; //錯誤: d 是 int& 類型 , 需要初始化
decltype(i) e;
注意:decltpye((var)) 注意是雙層括號,結(jié)果永遠是引用。而decltype(var) 結(jié)果只有當(dāng)var本身是一個引用時才是引用。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧
網(wǎng)頁標(biāo)題:C++Primer讀書筆記——2.變量和基本類型-創(chuàng)新互聯(lián)
當(dāng)前鏈接:http://aaarwkj.com/article18/cdhogp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護、網(wǎng)站排名、品牌網(wǎng)站制作、定制開發(fā)、用戶體驗、網(wǎng)站改版
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容