寫在前面
今天給大家講述的是C++內(nèi)置類型,并讓大家理解下C++是如何支持更復(fù)雜的數(shù)據(jù)類型的。由于本人水平有限,內(nèi)容難免會出現(xiàn)錯(cuò)誤,如有錯(cuò)誤,可以私信或者評論來指正我。如果你喜歡我的文章,可以留下一個(gè)大大的點(diǎn)贊。
2.1 基本內(nèi)置類型
2.1.1 算數(shù)類型
算數(shù)類型分為兩種:整型和浮點(diǎn)型,字符型(char)和布爾類型(bool)包括在整型里面。
| 類型 | 含義 |
|---|---|
| bool | 布爾類型 |
| char | 字符 |
| short | 短整型 |
| int | 整型 |
| long | 長整型 |
| longlong | 長整型 |
| float | 單精度浮點(diǎn)型 |
| double | 雙精度浮點(diǎn)型 |
| long double | 擴(kuò)展進(jìn)度浮點(diǎn)數(shù) |
上述除了字符和浮點(diǎn)型外,存在一種C++約束,這種C++約束就是一個(gè)int類型至少和一個(gè)short類型大,一個(gè)long至少和一個(gè)int一樣大,一個(gè)longlong類型至少比一個(gè)long大。
帶符號類型和無符號類型
除了布爾類型和擴(kuò)展字符型之外,其他類型都可以分為帶符號和無符號類型。帶符號顧名思義可表示正數(shù)、0、負(fù)數(shù)。不帶符號則只能表示0、和整數(shù)。這些類型數(shù)在計(jì)算機(jī)里面表示的范圍由計(jì)算機(jī)的位數(shù)決定,假設(shè)是32位計(jì)算機(jī),8bit也就是1位char類型可表示的數(shù)值數(shù)為256個(gè)(2^8)無符號范圍是0-255,有符號范圍是-128-127。
帶符號類型在代碼中表示為:int
無符號類型在代碼中表示為:unsigned int
在此處唯一特別的是char類型,char類型分為三類char、signed char、unsigned char。但是雖然有三種情況但表現(xiàn)形式只有兩種,有符號字符和無符號字符。類型char 會表示char 和signed char 其中一種 ,具體是由編譯器決定的。
該如何在編程中選擇我們要用的類型
- 1.當(dāng)我們不知道數(shù)值不可能為負(fù)時(shí),我們應(yīng)當(dāng)使用無符號型
- 2.使用int時(shí),如果數(shù)值范圍超出應(yīng)該用long long 而不是 long
- 3.執(zhí)行浮點(diǎn)數(shù)運(yùn)算時(shí),我們要多用double 類型 少用float 類型,原因有如下,首先在計(jì)算機(jī)運(yùn)行過程中,double類型執(zhí)行速度快于float,其次double進(jìn)度高于float。
2.1.2 類型轉(zhuǎn)換
當(dāng)我們程序運(yùn)行著運(yùn)行著我們使用的一種對象其實(shí)應(yīng)該去另外一種類型時(shí)就會發(fā)生類型轉(zhuǎn)換
double i = 1.1;
int j = 0;
j = i;//此處發(fā)生類型轉(zhuǎn)換 j = 1;
類型所能表示的值決定了轉(zhuǎn)換的過程
- 1.非布爾類型轉(zhuǎn)成布爾類型,0轉(zhuǎn)換為false,否則轉(zhuǎn)換為true
- 2.布爾類型轉(zhuǎn)換成非布爾類型,true轉(zhuǎn)換為1,flase轉(zhuǎn)換為0
- 3.浮點(diǎn)數(shù)轉(zhuǎn)換成整型,只取浮點(diǎn)數(shù)小數(shù)點(diǎn)前部分,后面全部舍棄
- 4.整型轉(zhuǎn)成浮點(diǎn)型,小數(shù)部分為0,如果整數(shù)所占空間超浮點(diǎn)數(shù)類型的容量則會造成精讀損失
- 5.我們給無符號類型賦予超出他范圍的值,該值會和該類型所能表示的數(shù)值數(shù)取模運(yùn)算,如 把-1賦給unsigned char(8比特大小)值會變成255(最大范圍數(shù)為256(2^8))
- 6.我們給一個(gè)有符號的值賦給他超出范圍的值,結(jié)果是未定義的,可能程序還會繼續(xù)工作,但也有可能程序會崩潰。
含無符號類型的表達(dá)式
我們在進(jìn)行無符號類型表達(dá)式的運(yùn)算時(shí),一定要保證無符號類型最后不能是負(fù)數(shù),如果為負(fù)數(shù)就會形成數(shù)值錯(cuò)誤。
unsigned int x = 1;
int y = -2;
cout<< x + y << endl;//輸出4,294,967,295(4,294,967,296 - 1)
2.1.3 字面值常量
字面值常量表示形式相對復(fù)雜,這里因?yàn)檫@只是快速上手,所以不予以討論,此處只說明轉(zhuǎn)義序列
轉(zhuǎn)移序列
這是一類不可打印的字符,一般由\開始,如\n。
| 名字 | 表現(xiàn)形式 |
|---|---|
| 換行符 | \n |
| 橫向制表 | \t |
| 響鈴符 | \a |
| 縱向制表符 | \v |
| 退格符 | \b |
| 雙引號 | \" |
| 反斜線 | \\ |
| 問號 | \? |
| 單引號 | \' |
| 回車符 | \r |
| 進(jìn)紙符 | \f |
2.2 變量
2.2.1 變量定義
變量一個(gè)具有自己名字以及有一塊程序可以操作的內(nèi)存空間的東西。
對于C+而言變量和對象一般可以相互使用。
那么何為對象呢?
對象是一塊具有某種類型且能儲存數(shù)據(jù)的內(nèi)存空間。
初始化
對象在創(chuàng)建時(shí)獲得了一個(gè)特定的值,就叫做對象的初始化,但是大部分人會說一開始進(jìn)行了對象賦值,這里要說一下賦值和初始化是兩種不同的操作?。〕跏蓟窃趧?chuàng)建對象時(shí)對象獲得的一個(gè)值,之前沒有值。而賦值是對象原先具有一個(gè)值,現(xiàn)在將這個(gè)值去掉,賦予一個(gè)新的值。
默認(rèn)初始化
在C++中存在著默認(rèn)初始化,如果對象定義在函數(shù)外面,這個(gè)對象會被初始化為0,但是如果這個(gè)對象定義在函數(shù)里面,這個(gè)對象將會被賦予一個(gè)不確定的值。因此作為C++程序員的我們應(yīng)當(dāng)在對象創(chuàng)建的時(shí)候就進(jìn)行人為初始化。
2.2.2變量聲明和定義的關(guān)系
C++語言支持分離式編譯,分離式編譯可以理解成,把一個(gè)程序分為多個(gè)文件編寫,每個(gè)文件單獨(dú)編譯。文件之間共享代碼就需要用到extern來聲明變量
extern int i ;
聲明和定義不同,聲明沒有初始化,定義有初始化。在C++中如果在一個(gè)聲明中初始化程序則會報(bào)錯(cuò)且變量只能定義一次但可以聲明多次
2.2.3 標(biāo)識符
C++標(biāo)識符不限制長度,但對大小寫敏感且不可以用C++保留的名字如int。
變量命名規(guī)范
- 標(biāo)識符要有具體含義
- 變量名用小寫字母,如index ,不能使用Index
- 用戶自定義的類名由大小字母開頭,如Sales_item
- 如果標(biāo)識由多個(gè)字母構(gòu)成,字母之間應(yīng)有明顯區(qū)分,如student_loan或者studentLoan
2.2.4 名字的作用域
我們定義的變量具有其作用區(qū)域,這塊區(qū)域我們成為作用域。
作用域分為全局作用域和塊作用域變量定義在函數(shù)外面稱為全局變量,他的作用域?yàn)槿肿饔糜?。與之相反變量定義在函數(shù)里面成為局部變量,他的作用域成為塊作用域。其生存周期取決于該塊
int main()
{
if(1)
{
int i = 1;//局部變量
cout << i << endl;//成立
}
cout << i << endl; //顯示i未定義
return 0;
}
嵌套作用域
我們在編輯程序過程時(shí)候應(yīng)當(dāng)避免使用到嵌套作用域,嵌套作用域的意思即為一個(gè)名字用于定義了全局變量,有用于定義了局部變量。這樣很影響程序可讀性,因?yàn)榫植孔兞繒采w全局變量。如果我們在一個(gè)程序中可能用到全局變量,那么他的名字就不要在定義成局部變量了,畢竟名字又不限制長度對吧!
2.3 復(fù)合類型
復(fù)合類型有兩種引用和指針
2.3.1 引用
C++11中引入了一種右值引用這里暫時(shí)不予以討論。所以這里的引用為左值引用
引用可以理解成給對象起了另一個(gè)名字,這個(gè)名字是這個(gè)對象的別名,但是引用不是對象,對象在初始化時(shí)會將一個(gè)值拷貝給對象,但是引用初始化是和一個(gè)對象進(jìn)行綁定,無法將一個(gè)值拷貝給引用,引用也無法拷貝他人。故所以,引用必須要初始化。
引用并非對象,相反的,它只是為一個(gè)已經(jīng)存在了的對象起了一個(gè)別名
引用的定義
int i = 1;
int &j = i;
cout << j << endl; //輸出為1
2.3.2指針
指針是另外一種復(fù)合類型,他的用處可以理解成“指向”某一對象。指針本身是一種對象,于是他和引用不同,他可以被賦值和拷貝,他的生命周期可以指向很多很多個(gè)對象,比如他現(xiàn)在指向了和他同一基本類型的A,下一秒他也可以指向和他同一基本類型的B。
指針通常比較難以理解,再有經(jīng)驗(yàn)的程序員有時(shí)候?qū)χ羔槻僮饕矔霈F(xiàn)錯(cuò)誤,因此我們要細(xì)心耐心的去學(xué)習(xí)指針
獲取對象的地址
指針用于存放對象地址,想要獲得對象地址,我們要用&符。
int i = 1;
int *j = &i;//獲取i的地址
cout << j << endl; //打印的是i的地址
這里要說明一點(diǎn),由于引用不是對象,他沒有一塊屬于自己的存儲空間,所以我們不能定義指向引用的指針,但是我們卻可以定義引用指針的名字
int i = 1;
int *j = i;
int &k = *j;
cout << k << endl;//k的值為1;
注意:指針的類型一定要和它指向的對象相互匹配,也就是說,在這里無隱式類型轉(zhuǎn)換
指針值
一般指針有如下四種狀態(tài):
- 1.指向一個(gè)對象
- 2.指向緊鄰對象所占空間的下一段空間
- 3.空指針,沒有指向任何對象。
- 4.無效指針,也可以被成為“野指針”他沒有指向任何區(qū)域,十分危險(xiǎn)!
如果拷貝或者引用指向未知區(qū)域的指針將會引發(fā)錯(cuò)誤,但是這類錯(cuò)誤編譯器無法識別出,所以我們要在定義指針的時(shí)候就要將它進(jìn)行初始化!
利用指針訪問對象
如果我們要訪問指針存放指向?qū)ο髢?nèi)存區(qū)域的值的時(shí)候,我們可以用到指針操作符“*”,來訪問該對象。
int i = 42;
int *j = &i;
cout << *j << endl;//輸出42,而不是i的地址。
空指針
在C++11中,有字面值nullptr來給指針賦予空初始化,也就是指針為0,在C語言<cstdlib>中也有編譯器預(yù)定義NULL(預(yù)處理變量),當(dāng)然我們也可以直接給指針初始化賦為0;
int *a = nullptr;
int *b = NULL;
int *c = 0;
這里再次強(qiáng)調(diào)!!定義指針的時(shí)候必須要初始化??!所以我們一定要形成一種定義變量時(shí)就進(jìn)行變量初始化!
賦值和指針
指針和引用的區(qū)別之一是,一旦定義了引用就無法綁定了其他對象。引用十分專一,指針卻在指向一個(gè)對象的時(shí)候,下一秒還可以不指向之前指的對象,指向一個(gè)新的對象。所以讀到這里的你,你在感情方面是引用呢還是指針呢?
可能很多人會分不清,我究竟是改變的指針?biāo)赶騾^(qū)域,還是改變指針?biāo)赶騾^(qū)域的值呢?
很簡單我們只需要看等號左邊被操作數(shù)是什么如果是沒有帶星號*改變的是指向區(qū)域,帶星號則改變你指向區(qū)域的值
int i = 1;
int *j = &i;
cout << *j << endl;//輸出為i的值->1
int k = 3;
*j = 2;
cout << *j << endl;//輸出2
cout << i <<endl;//輸出2 i值被改變
j = &k;
cout << *j << endl;//輸出 3 j指向的區(qū)域發(fā)生改變
void指針
void 指針可以指向任何類型的對象,但是由于他不知道他具體指向什么對象,所以我們無法對它進(jìn)行操作,所以void*只是存放內(nèi)存區(qū)域的一塊內(nèi)存空間。
2.3.3理解符合類型的聲明
變量組成包括了一個(gè)基本數(shù)據(jù)類型,一組聲明符。一條語句中基本數(shù)據(jù)類型只有一個(gè),但是聲明符可以有很多。
定義多個(gè)變量
在定義多個(gè)變量時(shí),我們應(yīng)該具有比較好的編寫習(xí)慣,不好的編寫習(xí)慣會在邏輯上面比較難理解,甚至出錯(cuò)。
int* a , b ;//a是整型指針,b是整型變量
上面一條語句就容易讓人誤解成定義兩個(gè)整型指針我們應(yīng)該這樣寫
int *a,*b;
或者
int *a = nullptr;
int *b = nullptr;
筆者比較喜歡下面一種方式。
指向指針的指針
指針是可以分等級的,等級級別由聲明符決定,
int *p1 = nullptr;
int **p2 =nullptr;
int i =1;
p1 = &i;
p2 = &p1;
cout << **p2 << endl; //輸出1,解除兩級引用
指向指針的引用
引用不是對象,所以指針不能指向引用。但是指針是對象,所以引用可以綁定指針。
int i = 1;
int *p = &i;
int *&j = p;
cout << *j << endl;//1
*j = 2;
cout << *j << endl;//2
cout << *p << endl;//2
cout << i << endl;//2
要理解j的類型具體是什么我們應(yīng)該從右到左閱讀j的定義,距離變量名最近的類型有最直接的影響,此處為&。所以他是指向指針的引用。
2.4 const限定符
const 用于修飾數(shù)據(jù)類型在定義之后不可被改變,于是const變量必須要定義的時(shí)候就初始化。
const int i = 1;
初始化和const
const的作用是保證該變量不能夠在被定義后修改,但是卻可以用const常量來賦值給變量,且具有相應(yīng)的隱式轉(zhuǎn)換。
const int i = 1;
int b = i;
bool c = i;
cout << b << endl;//1
cout << c << endl;//1
默認(rèn)狀態(tài)下,const對象僅在文件內(nèi)有效
前面提到了extern可以讓變量在不同文件內(nèi)共享代碼,const也不例外,const也只能定義一次,但可以在其他文件聲明。只不過表示方式不同
extern const int i = 1;//定義了一個(gè)可在多文件內(nèi)聲明使用的常量
如果你想在多文件使用const,則需要在定義const時(shí)加上extren
2.4.1const引用
變量有引用,常量也有引用。
const int i = 1;
const int &j = i;
cout<< j <<endl;//1
初始化和對const的引用
const引用很特別她不僅可以綁定常量還可以綁定變量。
int i = 1;
const int &i1 = i ;
cout << i1 <<endl;//1
之所以可以這樣是因?yàn)镃++編譯器里面產(chǎn)生了一個(gè)臨時(shí)量,通過臨時(shí)量來初始化常量引用
int i = 1 ;
const int temp = i;//temp為臨時(shí)量
const int &i1 = temp;
對const引用可能引用一個(gè)非const的對象
一個(gè)變量可被多個(gè)引用給綁定,被const引用綁定過的對象也可以被非const對象所引用。通過改變i的值也可以改變const引用的值,但C++語言規(guī)定這樣做是非法的,因?yàn)榇蠹一旧喜粫梢媒壎ǖ脚R時(shí)量上面
int i = 1;
int &j = i;
const int &j1 = i;
cout << j1 <<endl;//1
cout << j << endl;//1
cout << i <<endl;//1
j = 2;
cout << j1 <<endl;//2 通過j 改變了j1
cout <<j <<endl;//2
cout << i <<endl;//2
2.4.2 指針和const
與引用一樣,指針也可以指向常量或者非常量。
int x = 1;
const int *p = &x;
cout << *p <<endl;//1
和常量引用一樣,并沒有規(guī)定右值是否為常量。
const指針
const指針有兩種,一種是可以通過被引用的變量來改變指針的值,另一種不可以通過被引用的變量來改變指針的值。
int x = 1;
const int *p1 = &x;
cout << *p1 <<endl;//1
x = 2;
cout << x <<endl;//2
cout << *p1 << endl;//2
const int x = 1;
const int *const p1 = &x;
cout << *p1 <<endl;
x = 2;//出錯(cuò)不可被改變,程序停止編譯
cout << x << endl;
cout << *p1 <<endl;
小技巧:前面說到,判斷一個(gè)變量的類型,從右向左看,里變量名最近那個(gè)決定了這個(gè)變量的性質(zhì),此處 是const p1 ,離p1最近的是const 故所以不能通過改變x的值來改變p1的值**
2.4.3 頂層const
- 頂層const:表示指針本身是個(gè)常量,不可以用任何方式來改變這個(gè)指針的的值。即為頂層const不受什么影響。
- 底層const:指針?biāo)赶虻膶ο笫且粋€(gè)常量。
int x = 1;
int *const p1 = &x;//頂層const
const int *p2 = &x;//底層const
指針類型既可以是頂層const也可以是底層const
const int x = 1;//頂層const
const int *const p1 = &x;//又是頂層const也是底層const
頂層const可以定義底層const,但是底層const不可以定義頂層const
int x = 1;
int *const p1 = &x;//頂層const
const int *p2 = p1;//底層const
cout << *p2 << endl;//1
int x = 1;
const int *p2 = &x;//底層const
int *const p1 = p2;//頂層const 出錯(cuò),底層const不可以賦值給頂層const
cout << *p2 << endl;//1
2.4.4 constexpr和常量表達(dá)式
常量表達(dá)式,在編譯過程(注意運(yùn)行和編譯的區(qū)別)中就能得到結(jié)果且不能被改變,用常量表達(dá)式初始化的const對象也是常量表達(dá)式。字面值屬于常量表達(dá)式。
const int x = 20;//x是常量表達(dá)式
const int x1 = x + 1;//x1也是常量表達(dá)式
int t = 27;//不是常量表達(dá)式
const int t1 = get_size()//不是常量表達(dá)式,他的值得到運(yùn)行g(shù)et_size()函數(shù)時(shí)才可以得到。
constexpr變量
盡管我們可以用const來定義常量表達(dá)式,但是在實(shí)際使用中const很難區(qū)別出我們一開始究竟定義的是不是一個(gè)常量,于是C++11給了我們一個(gè)更好用的東西,constexpr變量。它定義出來的東西一定是一個(gè)常量。而且必須常量表達(dá)式初始化。
constexpr int my = 20;
constexpr int my_1 = my + 1;
constexpr int t = size()//當(dāng)size是一個(gè)constexpr函數(shù)時(shí)成立
字面值類型
算數(shù)類型、引用、指針,都屬于字面值類型可以被定義成constexpr,而自己定義的類、IO庫、string類型不屬于字面值類型,不可以被定義成constexpr。
指針和constexpr
const int *p =nullptr;//底層const
constexpr int *p = nullptr;//頂層const
注意:定義constexpr的時(shí)候,右值必須經(jīng)過合法初始化
2.5 處理類型
2.5.1類型別名
類型別名:typedef ,用法 typedef 需要被起別名的東西 別名名字
typedef int zheng
zheng i = 1; //i是整型變量
別名聲明:using ,用法 using 別名 = 被起別名
using zheng = int ;
zheng i = 1;//i為整型
2.5.2 auto類型說明符
當(dāng)我們編程越來越多,越難記得當(dāng)前運(yùn)算我們需要什么類型,于是在C++11中就有了auto類型說明符
double d1 = 1.1;
double d2 = 1.2;
auto x = d1 + d2;//x為整型
復(fù)合類型和常量
一般來講auto和int之類差不多,且&和*在算數(shù)類型中只服務(wù)于聲明符而并不是基本數(shù)據(jù)類型的一部分,
所以再用auto去弄復(fù)合類型或者常量時(shí)有幾點(diǎn)需要注意
- 1.auto一般會忽略掉頂層const,底層const則會被保留下來
- 2.定義引用
int i = 1;
auto & j = i;//j是整型引用
也就是說auto只決定基本數(shù)據(jù)類型,如需復(fù)合數(shù)據(jù)類型則需自己添加
2.5.3 decltype類型指示符
在我們有些時(shí)候,我們想從表達(dá)式類型判斷出要定義的類型,但是不想用該表達(dá)式的值初始化變量。C++11給出了我們decltype類型指示符。
int x = 1;
decltype(x) a = 2;//不用x=1只用x的類型,a是整型
decltype處理頂層const與引用的方式不同,decltype不忽略頂層const和引用。就是說我們不用重新使用const 、&也可以定義const或者&類型。
decltype和引用
使用decltype定義引用必須初始化!
且在decltype使用有一個(gè)需要注意的地方
int x = 1;
int& x1 = x;
decltype((x1)) a = x; //a是整型引用。
cout << a << endl;//1
x = 2;
cout << a << endl;//2
切記,decltype((variable))結(jié)果永遠(yuǎn)是引用!如果只有一個(gè)括號,則括號內(nèi)是引用,定義的類型才是引用
創(chuàng)作不易,感謝您能看完。如果您喜歡,可以在線贊賞鼓勵(lì)我,您的鼓勵(lì)是我前行的動(dòng)力!如需轉(zhuǎn)載,請私信我,謝謝!