2.1 基本內置類型
算術類型
(1)算術類型分爲:整型(包括字符、布爾)、浮點型
- 算術類型所佔的最小尺寸,如下:
類型 | 最小尺寸 |
---|---|
bool | 未定義 |
char | 8 bit |
int | 16 bit |
long | 32 bit |
float | 6位有效數字 |
double | 10位有效數字 |
其中,一個char的大小和一個機器的字節一樣
- char16_t 是 Unicode 字符(Unicode 是用於表示所有自然語言中字符的標準),另外,還有 char32_t
- C++語言規定,一個int至少和一個short一樣大,一個long long至少和一個long一樣大
*內置類型的機器實現*
- 可尋址的最小內存塊稱爲字節(byte)
- 存儲的基本單元爲字(word),它通常由幾個字節組成
- 大多數計算機將內存中的每個字節與一個地址關聯,爲了賦予內存中某個地址明確的含義,必須首先知道存儲在該地址的數據的類型。類型決定了數據所佔的比特數以及如何解釋這些比特的內容
帶符號類型和無符號類型
(1)除去布爾型和擴展的字符型之外,其他整型可以分爲 signed 和 unsigned 兩種
(2)char實際上會表現爲有或者無符號的,由編譯器決定
(3)在表示範圍內正值和負值的量應該平衡,例如:8比特的signed char 理論上表示-127到127,而大多數計算機將表示範圍定爲-128到127
*選擇類型的建議*
1. 明確知曉數值不爲負,則用無符號類型
2. 一般用int、double進行運算
3. 算術表達式一般不用char或者bool,因爲不同機器可能所帶符號不同
類型轉換
(1)強行賦值轉換情況
初始值 | 賦值給 | 結果 |
---|---|---|
非 bool | bool | 初始值爲0,則false;否則true |
bool | 非bool | 初始值爲false,則0;否則1 |
浮點 | 整數 | 保留整數部分 |
整數 | 浮點 | 小數部分記爲0(整數所佔空間若超浮點容量,可能會損失) |
超出範圍的值 | 無符號 | 初始值對無符號類型表示數值總數取模後的餘數 |
超出範圍的值 | 符號類型 | undefined |
- 避==免無法預知==和==依賴於實現環境==的行爲
(2)含有無符號int又有符號int時,會將有符號int轉換成無符號int
unsigned u=10;
int i=-42;
std::cout<<u+i<<std::endl; // 如果int佔32位,輸出4294967264
- 其中,相加前把-42轉換成無符號數,把==負數轉換成無符號數==類似於直接給無符號數賦負值,結果是等於這個負數加上無符號數的模
(3)死循環的例子
for(unsigned u=10;u>=0;--u)
std::scout<<u<<std::endl;
*切記混用帶符號類型和無符號類型*
練習
1.閱讀代碼(32位)
unsigned u=10,u2=42;
int i=10,i2=42;
std::cout<<u-u2<<std::endl;
std::cout<<"/"<<std:endl
std::cout<<i-u2<<std::endl;
- 輸出結果爲:2^32-32 / 2^32-32
字面值常量
(1)整型:(0開頭表示八進制,0x或0X表示十六進制)
- 20(十進制)
- 024(八進制)
- 0x14(十六進制)
(2)浮點型(小數,或科學計數法(指數部分用E或e標識))
- 3.14159
- 3.14159E0
- 1e0
- 浮點字面值默認爲double,使用其他浮點需要加後綴
(3)字符和字符串字面值
- ‘a’ //字符
- “Hello World” //字符串
- (字符串實際爲常量構成的數組(array),因此字符串的長度比內容多1,爲結尾處的‘\0’)
(4)轉義序列(等等)
功能 | 序列 |
---|---|
換行符號 | \n |
橫向製表符 | \t |
退格符 | \b |
(5)指定字面值的類型
- 字符和字符串——前綴
- 整型和浮點——後綴
(6)布爾字面值和指針字面值
- true和false
- nullptr(指針字面值)
練習
1.利用轉義序列編程,要求先輸出2M,然後轉到新一行。修改程序使其輸入2,然後輸出製表符,再輸出M,最後轉到新一行。
std::cout<<"2M"<<std:endl;
std::cout<<"\n"<<std::endl;
std::cout<<2<<"\t"<<'M'<<std::endl;
2.2 變量
(1)變量定義基本形式
- 類型說明符(type specifier),緊跟一個或多個變量名
- 字符串類型在標準庫std中:std::String
==對象==一般指一塊能存儲數據並具有某種類型的內存空間
(2)初始值
初始化:創建變量時賦予其一個初始值
賦值:把對象當前值擦除,以一個新值來替代
(4)默認初始化
- 定義變量時沒有指定初值,則變量被默認初始化
- 定義在函數體內部的內置類型變量將不被初始化。一個未被初始化的內置類型變量的值是未定義的,如果試圖訪問將引發錯誤
定義於函數體內的內置類型的對象如果沒有被初始化,則其值未定義。
類的對象如果沒有顯式初始化,則值由類確定
==建議初始化每一個內置類型的變量==
變量聲明和定義關係
(1)C++支持分離式編譯,即允許將程序分割爲若干文件,每個文件獨立編譯
(2)聲明(declaration)使得名字爲程序所知
(3)定義(definition)負責創建與名字關聯的實體
==任何聲明並顯式初始化即成爲定義==
C++是一種靜態類型語言,含義是在編譯階段檢查類型(成爲類型檢查)
標識符(字母、數字和下劃線組成,數字不能做開頭)
(1)約定俗成的命名規範
- 標識符要體現實際含義
- 變量名一啊不能用小寫字母
- 用戶自定義的類名一般以大寫字母開頭
- 如果標識符由多個單詞組成,要有明顯區分,如student_loan
(2)自定義標識符注意:
- 不能連續出現兩個下劃線
- 不能下劃線緊連大寫字母開頭
- 函數體外的標識符不能以下劃線開頭
名字的作用域
(1)main定義於所有{}之外,有==全局作用域==
(2)定義於main{}中的名字,值能在main所在塊有效,有==塊作用域==
建議:當你第一次是用變量時,才定義它(便於找到變量的定義,初始化更合理)
(3)優先使用正在作用域內的名字,要區分可以對外層使用(::reused)
2.3 複合類型
引用(reference)
int ival=1024;
int &refVal=ival; //refVal是ival的另一個名字
- 定義引用時,程序把引用和它的初始值綁定在一起,而不是拷貝給引用
- 引用即別名
練習
1.下列那些定義不合法?
int ival=1.01;
int &rval=1.01; //不合法
int &rval2=ival; //不合法
int &rval3 //不合法
2.執行下面代碼,會什麼結果?
int i,&ri=i;
i=5;
ri=10;
std::cout<<i<<" "<<ri<<std::endl;
- 將會輸出:10 10
指針
指針是“指向”另外一種類型的複合類型
與 引用 之不同
==指針本身就是一個對象==
- 允許對指針賦值和拷貝
- 在指針的生命週期內,可以先後指定幾個不同對象
==指針無需在定義時賦初值==
- 與內置類型一樣,指針沒有被初始化,也將擁有一個不確定的值
(1)指針定義:
int *ip1,*ip2; //都是直線int型對象的指針
double dp,*dp2; //dp2是指向double型對象的指針,dp是double型對象
(3)獲取對象的地址
- 指針存放某個對象的地址,要獲取,要使用取地址符(操作符&)
int ival=43;
int *p=&ival;
其中,p存放ival的地址,或者說:p是指向ival的指針
> 因爲引用不是對象,所以不能定義指向引用的指針
(4)指針的值(下列只有第一個能訪問和拷貝)
- 指向一個對象
- 指向==近鄰對象所佔空間的下一個位置==
- 空指針
- 無效指針
(5)獲取指針訪問對象
- 如果指針指向了一個對象,則允許使用解引用符(操作符*)來訪問
關鍵概念:多重含義的符號 * 和 &
(6)空指針
- 空指針的生成辦法(可以用於檢查指針是否爲空)
int *p1 = nullptr;
int *p2 = 0;
int *p3 = NULL; //預處理變量,不屬於命名空間std
- 一般用nullptr,因爲可以nullptr可以轉換成任意其他的指針類型
(7)賦值和指針
- 給指針賦值,就是令它存放一個新的地址
(8)void* 指針
- 一種特殊的指針類型,可以用來存放任意對象的地址(但是我們不值得所指對象的類型)
練習
1.下列代碼的作用
int i=42;
in *p1=&i;
*p1=*p1**p1;
- p1作爲指針變量,存放i的地址,隨後*p1即是i,最後相當於:i=i^2
2.下列定義是否非法?
int i=0;
double* dp=&i; //不合法,double的指針不能存int的地址
int *ip=i; //不合法,int不能賦值給指針
int *p=&i; //合法
3.下列代碼,爲什麼p合法而lp非法?
int i=42;
void *p=&i;
long *lp=&i;
- 因爲void*指針可以存放任意類型指針;long指針不能存放int的地址
理解複合類型的聲明
(1)變量的定義:基本數據類型+一組聲明符
- 雖然基本數據類型只有一個,但是聲明符卻可以不同
int i=1024,*p=&i,&r=i;
- 注意:定義多個變量
int* p,r; // p是指向int的指針,r是int
(2)指向指針的指針
int ival=1024;
int *pi=&ival; // pi指向一個int型數
int **ppi=π //ppi指向一個int型指針
(3)指向指針的引用
int i=42;
int *p; //p是一個int型的指針
int *&r=p; //r是一個對指針p的引用
r = &i; //r引用了一個指針,因此給r賦值&i就是令p指向i
*r = 0; //解引用r得到i,也就是p指向的對象,將i的值改爲0
練習
1.說明下列變量的類型和值
- int* ip , i , &r = i ;
// ip爲int指針,i爲int數,r爲i的引用
- int i,*ip=0;
// i 爲int數,ip爲int型的空指針
- int* ip,ip2;
// ip爲int指針,ip2爲int數
const限定符
有時我們希望定義這樣一種變量,它的值不能改變。
const int bufSize=512;
- const對象必須立即初始化
(2)默認狀態下,const對象僅在文件內有效
- 解決辦法:(聲明和定義都添加extern)
extern const int bufSize = fcn(); //在file.cc定義
extern const int bufSize; //與file.cc中的bufSize是同一個
(3)const的引用——==常量引用是對const的引用==
- 常量的引用,不過是綁定別名給一個const,仍然不能用引用來修改其值
(4)初始化和對const引用
int i=42;
const int &i1=i; //合法
const int &r2=42; // 正確
const int &r3=r1*2; //正確
int &r4=r1*2; //非法
(5)對const的引用可能一個非const對象
int i=42;
int &r1=i; //引用r1綁定對象i
const int &r2=i; //r2也綁定對象i,但是不允許通過r2修改i的值
r1=0; //r1並非常量,i的值修改爲0
r2=0; //錯誤:r2是一個常量引用
指針和const
(1)指向常量的指針不能用於改變其所指對象的值,要想存放常量對象的地址,只能使用指向常量的指針
const double pi=3.14; //pi是常量,值不能改變
double *ptr=&pi; //錯誤,ptr是一個普通指針
const double *cptr; //正確,cptr可以指向一個雙精度常量
tips:所謂指向常量的指針或引用,不過是指針或引用“自以爲是”罷了,它們覺得自己指向了常量,所以自覺地不去改變所指對象的值
(2)const指針
int errNumb=0;
int *const curErr = &errNumb; //curErr將一直指向errNumb
==從右向左讀,依然是讀懂指針等複雜聲明的好辦法==
練習
《C++ Primer》 ,57頁,題目未做,待日後補充
頂層const
指針本身是一個對象,它又可以指向另外一個對象
因此,指針本身是不是常量以及指針所指的是不是一個常量,是兩個獨立問題。
- 頂層const:表示指針本身是一個常量
- 底層const:表示指針所指的對象是一個常量
int i=0;
int *const p1=&i; // 不能改變p1的值,這是一個頂層const
const int ci=42; // 不能改變ci的值,這是一個頂層const
const int *p2=&ci; // 允許改變p2的值,這是一個底層const
const int *const p2=p2; //靠右的const是頂層const,靠左的是底層const
const int &r=ci; // 用於聲明引用的const都是底層const
當執行對象的拷貝操作時,常量是頂層const還是底層const區別明顯,其中,頂層const不受什麼影響
i=ci; //正確:拷貝ci的值,ci是一個頂層const,對此操作無影響
p2=p3; //正確:p2和p3指向的對象類型相同,p3頂層const的部分不影響
練習
1.對於下面的語句,說明其頂層還是底層const?
const int v2=0;
int v1=v2;
int *p1=&v1,&r1=v1;
const int *p2=&v2,const p3=&i,&r2=v2;
2.此題未做
constexpr和常量表達式
是指值不會改變並且在編譯過程就能得到計算結果的表達式
(1)constexpr變量
==一般來說,如果你認定變量是一個常量表達式,那就把它聲明成constexpr類型==
(2)字面值類型
- 算術類型、引用、指針都屬於字面值類型
- 自定義類、io庫、string等不屬於字面值類型,即不能定義爲constexpr
(3)指針和constexpr
- 在constexpr聲明定義一個指針,限定符constexpr僅對指針有效,與指針所指的對象無關
const int *p=nullptr; //p是一個指向整型常量的指針
練習
1.下面代碼是否合法?
“`
int null=0,*p=null;
···