《C++Primer》讀書筆記(二)C++基礎(上)

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=&pi;  //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;
···

發佈了34 篇原創文章 · 獲贊 5 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章