C++ Primer 筆記+習題解答(二)

今天是第二篇筆記了,主要記錄一下比較有意思的知識點,做不到面面兼顧。

有錯誤 請指正 謝謝

1.引言:

    任何計算機語言都有一組公共的語法特徵,不通語言的主要區別在於語法特徵的實現細節。

2.基本特徵:

    一般泛指內置數據類型。大多數C++語言通過兩種方式擴充語言:1是自定義數據類型2是把常用的封裝成庫提供給使用者。

3.一句老話:

    C++對象類型決定了其能進行的操作。一個表達式是否合合法取決於參與其中的數據類型。如C++之類的靜態類型語言會在編譯器進行類型檢測,諸如python之類的語言是在運行期進行檢查。關於編譯和運行時期的概念按照其字面意思進行理解,大致是正確的。

4.一個例子:

i=i+j;//這個語句的具體含義取決於i和j的數據類型。i可能是一個類的對象
      //也可能只是常見的內置數據類型。

5. 基本內置類型:

   分爲兩類,1是算術類型(arithmetic type),2是空類型(void type)

    算術類型:字符類型,布爾型,整形,浮點型。

    void 類型:不對應具體的值。常見的就是函數的返回值。

6.C++幾種字符類型:

     拓展字符類型,基本字符類型。

7.字節:

    計算機尋址的最小內存快。通常是8位一個字節。位:bit; 字節:byte。

8.存儲的基本單位:

    字(word)一般由4個或者8個字節組成。有點類似吞吐量的意思。內存中每個自己與一個地址關聯起來。可以通過地址訪問相應的自己。爲了賦予地址明確的含義,必須知道存儲的內容的數據類型。數據類型決定了數據所佔用的字節數,以及如何解釋這些自己的內容。在深度探索C++對象模型也提到類似的問題。

9.重點關注C++類型的概念。

10.符號:

    無符號和帶符號類型.

    除去布爾型和拓展字符型,其他整形可以劃分爲有符號型和無符號型。

   有符號可以表示0,正數,負數。無符號就只能表示正數,0.

11.在相應的類型前加上unsigned 或者signed 可以得到對應的符號類型。

12.其中字符型比較特殊:

    分爲三種:char ,signed char, unsigned char ,但是對外表現只 有兩種有符號型和無符號型。也就是說char 和 signed char 是不一樣的,具體的行爲是由編譯器決定的。

13.無符號類型的所有比特位都用來存儲數值。有符號類型最高位是表示正負。

    如:unsigned char 類型的表示範圍:理論上爲了對稱範圍是-127到127.實際現代計算機通常是-128到127.

14.類型轉換:

    從一種類型轉換爲另一相關類型。主要是相關。

    unsigned char c = -1; 因爲-1字面值默認是int 的,所以會進行轉換。這個地方涉及到二進制補碼的知識,也就是計算機如何存儲負數的。

    最後輸出c的值是255。關於255是如何得來的,是-1轉換後的值除以表示的最大範圍所得到的餘數。

舉個例子:

上面提到:-1的默認類型是int型的。那麼-1在計算機中如何存儲呢?是這樣搞的。
首先:最高位是1表示負數。那麼-1表示成:1000 0001 ;然而工作並沒結束。
除去最高位之外,其餘進行求反,得到反碼。即:1111 1110 ;得到反碼。
最後一步:求補碼。對反碼+1;即可以得到: 1111 1111 ;
補碼對應的10進制數字是:255 .那麼255%256=255,故上面的c值是255. 
如果轉換不了,或者超出範圍就會成爲未定義行爲或者截斷成爲異常值。

unsigned char c=256.//轉不了了。
不要混用帶符號和不帶符號的類型。
以0開頭的數字是八進制,0x開頭表示16進制。

15.浮點型的默認字面值是double。

     整形的字面值一般是最小容納類型(不包含short)也就是說int能容納下的數字,那麼編譯器就會默認你是int了。若int放不下,會自動上調到合適的類型。

16.字符和字符串字面值:

    符串字面值實際上是由字符字面值構成的數據。編譯器會在最後一位加一個空字符('\0') 標識結束。

所以字符串的實際長度比內容多1.

字符串字面值的書寫格式:特殊的一種,當兩個相鄰字符串之只有空格,縮進,換行符構成,那麼可以分開書寫。

<span style="font-size:18px;">如cout<<"This is a sample"</span>
      <<" oh ! ";
兩類不可以直接使用的字符:1是不可打印字符,如退格,換行等。2是特殊含義的字符,比如反斜線,問號等。

那麼要使用此類字符要加上反斜線。比如常見的‘\n’表示換行。

上面這些可以稱爲轉義序列。

17.泛化轉義序列:

    比如你可以這樣打印字母A: \x41;不建議使用。其次就是x表示16進制。

18.通過添加前綴後綴可以指定字面值類型。

如:double db1=2L;
還有許多前綴和後綴,不一一介紹。

19.內置類型採用列表初始化的時候,不能存在截斷的風險,否則會報錯。

20.聲明和定義的區別:

int i; //定義。
extern int i ;//聲明。
extren int i=0;//定義。
任何存在初始化行爲的聲明也是定義。

21.爲什麼支持分離式編譯,所以要嚴格區分定義和聲明,多次聲明沒問題,但只能一次定義。

22.複合類型的理解:

     基於其他類型定義出來的類型。比如引用和指針。

23.常量表達式:

     不會改變值,在編譯期就可以計算出結果。

24.constexpr:

     聲明定義常量,並且只能用常量初始化。

constexpr int i=10;
一個陷進:

const int *p=nullptr;
constexpr int *p=nullptr; 
這兩個一樣嘛?不同。第一個是指向常量的指針,第二個是指針常量。
類型別名:typedef 和 using 聲明別名。

如: using money=double ;
     typedef double money;
兩者表達的意思一樣。
一個陷進:

typedef char *pstring;
const pstring p;
const char* p;
兩個等價嘛?不等價。所以不要簡單替換理解。第一個const修飾的是char*整體。也就是說p是指針常量。
第二個是指向常量的指針。
對於複雜聲明,建議從右向左讀。首先找變量名。區分變量名和標識符的關係。標識符包含變量名。

26.auto 和 decltype 類型說明符。

auto 可以用表達式的類型去初始化一個auto 類型。

如 int v1=2;
   int v2=3;
   auto x=v1+v2;  //編譯器可以推斷出x的類型。這個地方有了初始化操作,不想初始化可以使用decltype.
   decltypde(v1) x; //x的類型是int .這個地方並不需要吃初始化操作。

27.頭文件衛士:

ifndef    xxx
#define xxx
#include<string>
class a{};
#ennif
防止多次包含。

28.習題解答:

2.1

<span style="font-size:24px;">區別:
1.int long ,long long ,short 的區別。
最主要的區別就是所佔的字節數是不同,也等價於容納數的範圍不同。long long 最大。
2.無符號類型和有符號類型的區別:
無符合的只能表示正數,有符號的可以表示全部。
3.float 和double 的區別:
精度不同,推薦使用double。
</span>
2.2
<span style="font-size:24px;">利率選用 unsigned double ;本金選用 unsigned double; 付款也應該選用 unsigned double.</span>
2.3
<span style="font-size:24px;">#include <iostream>
using namespace  std;
int main(){
	unsigned u = 10, u2 = 42;
	cout << "u2 - u = "<<u2-u <<" type is "<<typeid(u2-u).name()<< endl;  //32;
	cout <<"u - u2 = "<<u-u2<<" type is " <<typeid(u-u2).name()<< endl;  //4294967264
	int i = 10, i2 = 42;
	cout << "i2 - i  = "<<i2-i<<" type is "<<typeid(i2-i).name() <<endl;    //32;
	cout <<"i - i2 = "<<i-i2<<" type is "<<typeid(i-i2).name()<< endl;	 //-32;	
	cout << "i - u = " << i - u <<" type is "<< typeid(i - u).name() <<endl;		 //0
	cout <<"u - i = "<<u-i<< " type is "<<typeid(u-i).name()<<endl;		 //0	
	system("pause");
	return 0;
}</span>
2.4

請看2.3

2.5

<span style="font-size:24px;">//(a) 'a ; L'a' ; "a" ;L"a"					從左到右類型依次是:char ,wchar_t ,常量字符串,寬字符常量字符串。
//(b) 10 ;10u ;10L ;10uL ;012; 0xC;	依次是 int,unsigned int ,long int  ,unsigned long int ,8進制 int,16進制 int。
//(c)  3.14 ;3.14f ;3.14L						依次是 double ,float, double
//(d)  10 ;10u ;10. ;10e-2;                    int ,unsigned int ,double ,double ,

#include <iostream>
using namespace std;
int main(){
	cout << typeid(10e-2).name() << endl;//驗證方法typeid().name() 函數。
	system("pause");
	return 0;
}</span>
2.6
<span style="font-size:24px;">有區別:
第一組: int month=9,day=7;  9和7 都是10進制整數。
第二組: int month=09,day=07; 09是什麼鬼.不是八進制也不算十進制 07 是8進制整數。
</span>
2.7
<span style="font-size:24px;">#include <iostream>
using namespace std;
int main(){
	cout << "Who goes with F\145rgus?\012";  //145是三個八進制數字。\12換行、
	cout << typeid("Who goes with F\145rgus?\012").name() << endl;
	cout << 3.14e1L<<endl;  // long double 
	cout << typeid(3.14e1L).name() << endl;
	cout << 3.14f << endl;//float
	cout << typeid(3.14f).name() << endl;
	cout << 3.14L << endl;
	cout << typeid(3.14L).name() << endl;
	system("pause");
	return 0;
}</span>
2.8
<span style="font-size:24px;">#include <iostream>
using namespace std;
int main(){
	cout << "\062\x4D\012";
	cout << "\062\x9\x4D\012";
	system("pause");
	return 0;
}
//全部用ASCII碼錶示的。
</span>
2.9
<span style="font-size:24px;">a. 錯誤:int input_value; cin>>input_value;
b. 錯誤:int i={3.13} 存在信息丟失風險。C++Primer 定義爲錯誤,vs2013可以通過
c. 錯誤:double salary=wage=99.99; wage 未定義。
d. 正確:int i=3.13;  會有截斷。
</span>
2.10
<span style="font-size:24px;">global_str empty string;
global_int 0;
local_str 未初始化
local_int 未初始化</span>
2.11
<span style="font-size:24px;">extern int ix=1024 /definition 
int iy //definition 
extern int iz //declaration</span>
2.12
<span style="font-size:24px;">(a),(c),(d)
</span>
2.13
<span style="font-size:24px;">j=100</span>
2.14
<span style="font-size:24px;">不合法。i只存活於循環體內。循環體外無法打印i.</span>
2.15
<span style="font-size:24px;">b,d 不合法。</span>
2.16
<span style="font-size:24px;">不考慮截斷問題,全部可以通過編譯。vs2013+win 7 
</span>
2.17
<span style="font-size:24px;">output: 10  10 ,兩個10之間有一個空格</span>
2.18
<span style="font-size:24px;">#include <iostream>
using namespace std;
int main(){
	int v1=3,v2=4;
	const int *p = &v1;  //指向常量的指針
	//*p = 3;   不合法
	p = nullptr;  //合法
	int * const ptr = &v2;//常量指針
	//ptr = nullptr;  不合法
	*ptr = v1; //合法
	system("pause");
	return 0;
}</span>
2.19
<span style="font-size:24px;">區別:1.無空引用,聲明即綁定,但可以有空指針。
      2.指針是對象,佔據內存的行爲,引用是變量的別名。
</span>
2.20
<span style="font-size:24px;">通過指針間接修改i的值,i的值爲以前值的平方積。</span>
2.21
<span style="font-size:24px;">有。類型不一致。</span>
2.22
<span style="font-size:24px;">1.p指針不爲空,則執行if裏面的語句。
2.*P的值不等於0,就執行if裏面的語句。</span>
2.23
<span style="font-size:24px;">根據上下文觀察吧。或者輸出*p,看看是不是垃圾值。</span>
2.24
<span style="font-size:24px;">因爲 p是 void* 型的。可以存儲任何變量的地址。
而lp 的類型是long int ,同i 的類型不一致。
理論上是可以接受的,但是實際情況定義爲錯誤比較好。</span>
2.25
<span style="font-size:24px;">a)ip 是指針。i  是int ,r是i的引用
b)i 是int,ip是空指針
c)ip 是指針,ip2是int 。</span>
2.26
<span style="font-size:24px;">a,d不合法。const 量必須初始化並且不能改變值。</span>
2.27
<span style="font-size:24px;">b,d,e,g</span>

2.28

<span style="font-size:24px;">不合法的:a,b,d,e.不合法原因因爲常量只能初始化,不能賦值。</span>
2.29

<span style="font-size:24px;">合法:a,b,c</span>
2.30
<span style="font-size:24px;">頂層const: v2,  靠右的是頂層const.
底層const: p2,  靠左的是底層const.
</span>
2.31

<span style="font-size:24px;">全部合法。賦值操作時,可以忽略const.但是涉及到const作爲左值時,也就是值將被修改,那麼要關注const.
</span>
2.32

<span style="font-size:24px;">不合法:修改方案:int null=0,*p=&null;</span>
2.33

<span style="font-size:24px;">42, 42 ,42 ,報錯,報錯,42.</span>
2.34

2.35

<span style="font-size:24px;">j : int; k:int;p:int const *; j2: int ; k2: int .
#include <iostream>
using namespace std;
int main(){
    const int i = 42;
    const auto j = i;
    const auto &k = i;
    auto *p = &i;
    const auto j2 = i, &k2 = i;
    cout << typeid(k2).name();  //用typdeid函數進行驗證。
    system("pause");
    return 0;
}
平臺不同,可能有差異。測試平臺:vs2013+win 7 
</span>
 2.36

<span style="font-size:24px;">a:int ; b: int ; c: int ; d: int ;
結果:a=4,b=4,c=4,d=4;
#include <iostream>
using namespace std;
int main(){
    int a = 3, b = 4;
    decltype(a) c = a;
    decltype((b)) d = a;
    ++c;
    ++d;
    cout << "a= " << a << endl;
    cout << "b= " << b << endl;
    cout << "c= " << c << endl;
    cout << "d= " << d << endl;
    cout << "d's type is :" << typeid(d).name();
    system("pause");
    return 0;
}
</span>
2.37

<span style="font-size:24px;">#include <iostream>
using namespace std;
int main(){
	int a = 3, b = 4;
	decltype(a) c = a;
	decltype(a=b) d = a;
	cout << "d's type is :" << typeid(d).name();
	system("pause");
	return 0;
}
//由於int& 類型屬於符合類型,而且類似int, 所以編譯器可能會d 劃爲 int 型。如果你的結果是int& ,不用奇怪。
</span>
2.38

<span style="font-size:24px;">區別:auto 類型推斷時肯定要初始化;而decltype 不需要,除非必要。
關於int& 和 int ,我的編譯器不區分,所以不做測試。</span>
2.39

<span style="font-size:24px;">#include <iostream>
using namespace std;
struct Foo{ }
int main(){
	system("pause");
	return 0;
}
// error C2628: “Foo”後面接“int”是非法的(是否忘記了“;”?)
//error C3874 : “main”的返回類型應爲“int”而非“Foo”
//error C2440 : “return” : 無法從“int”轉換爲“Foo”</span>
2.40

<span style="font-size:24px;">#include <iostream>
using namespace std;
#include <string>
struct Foo{ 
	string bookNo;
	unsigned int units_sold;  //可能一開始寫的時候注意不到無符號數的使用。
	double revenue;
};
int main(){
	system("pause");
	return 0;
}</span>
2.41

2.42

<span style="font-size:24px;">#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <iostream>
#include <string>
struct Sales_data{
	std::string bookNo;
	unsigned int units_sold;
	double revenue;
};
#endif // !SALES_DATA_H
重點是學會使用header gurd !!!最好都要加上。不建議包含命名空間,詳情見下一節。
</span>


End

爲什麼寫的這麼零碎?因爲我寫的不好,因爲書本纔是王道。我只能寫一點不常見的東西,不然無異於抄書。習題解答僅供參考,其實不用關心答案正確與否,會了就會了,不會的自己應該很清楚。

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