快樂學(xué)習(xí)C++第一天:變量和基本類型

寫在前面

今天給大家講述的是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)載,請私信我,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容