導言 程序設計語言中充滿陷阱,一不小心就會掉入其中萬劫不復,之所以有陷阱,是因為語言的設計細節不符合程序員的直覺 所以你會發現,語言越高級越注重順從程序員的直覺。 c++也有許多陷阱,所謂山不過來,我就過去,因此將c++中易錯點、難點集合於此,會不定期更新。 字符串與vector 字符串字面值與標准庫string不是同一種類型 string s("hello"); cout<<s.size()<<endl; //OK cout<<"hello".size()<<endl; //ERROR cout<<s+"world"<<endl; //OK cout<<"hello"+"world"<<endl; //ERROR strlen、sizeof與size()求字符串長度的區別 cout<<strlen("123")<<endl; //返回 3 cout<<sizeof("123")<<endl; //返回 4 string s = "123"; cout<<s.size()<<endl; //返回 3 標准string庫中的getline函數返回時會丟棄換行符 const iterator與const_iterator的區別 vector<int>::const_iterator //不能改變指向的值,自身的值可以改變 const vector<int>::iterator //可以改變指向的值,自身的值不能改變 const vector<int>::const_iterator //自身的值和指向的值都是只讀的 任何改變vector長度的操作都會使已存在的迭代器失效。如:在調用push_back之後,就不能再信賴指向vector的迭代器了 vector<int> ivec; ivec.push_back(10); vector<int>::iterator it = ivec.begin(); cout<<*it<<endl; ivec.push_back(9); cout<<*it<<endl; //迭代器已經失效 數組與指針 字符數組除了可以用花括號在定義時初始化外,還可以用字符串字面值初始化,但謹記字符串字面值包含一個額外的空字符 char c1[] = {'h','e','l','l','o'}; char c2[] = "hello"; cout<<sizeof(c1)/sizeof(char)<<endl; //長度是5 cout<<sizeof(c2)/sizeof(char)<<endl; //長度是6 一個數組不能用另一個數組初始化,也不能將一個數組賦值給另一個數組 int a[3] = {1,2,3}; int b[3][3] = {{1,2,3},{1,2,3},{1,2,3}}; //right int c[3][3] = {a,a,a}; //error 若指針保存0值,表明它不指向任何對象。但是把int型變量賦值給指針是非法的,盡管此int型變量的值可能為0 int a = 0; int *p1 = 0; //right int *p2 = a; //error typedef string *pstring; const pstring cstr; cstr的類型是 string * const 還是 const string * ? 答:是string *const cstr,而非 const string *cstr。容易產生誤解的原因是const限定符既可以放在類型前也可以放在類型後,const pstring cstr等價於pstring const cstr。遇到此類問題時,把const放在類型之後來理解。 區分:int *ip[4] 和 int (*ip)[4] 第一個表示一個數組,元素是int指針 第二個表示一個指針,指向int數組,遇到此類問題時,由內向外讀。 運算符 除法/和求模% 若兩個操作數是正數,則除法的結果是正數,求模的結果也是正數 若兩個操作數是負數,則除法的結果是正數,求模的結果是負數 若只有一個操作數是負數,則除法和求模的結果取決於機器,除法可以確定結果是負數 邏輯與和邏輯或操作符總是先計算其左操作數,然後再計算其右操作數,只有在僅靠左操作數的值無法確定該邏輯表達式的結果時,才會求解其右操作數 區分 if(i<j<k) 和 if(i<j && j<k) 第一個i<j或者為0或者為1,只要k大於1,表達式就為true 第二個必須i<j且j<k表達式才為true 區分 if(val) 和 if(val == true) 第一個只要val非零則表達式為true,val為0則表達式為false 第二個只有val為1表達式為true,val非1則表達式為false int val = 2; if(val==true){ //不會進入if cout<<"val==true"<<endl; } 多個賦值操作符中,各對象必須具有相同的數據類型,或者具有可轉換為同一類型的數據類型。 int ival; int *pval; ival = pval = 0; //error 盡管ival和pval都可以賦值為0 string s1,s2; s1 = s2 = "OK" //ok 如果指針指向不是用new分配的內存地址,則在該指針上使用delete是不合法的。 通常編譯器不能斷定一個指針是否指向動態對象,因此盡管這樣做是錯誤的,但在大部分編譯器上仍能運行通過,但是會產生運行時錯誤。 整形提升 對於所有比int小的整形(char, signed char, unsigned char, short, unsigned short),如過該類型所有可能值都包含在int中,他們會被提升為int型,否則,他們將被提升為unsigned int。 對於包含signed和unsigned int型的表達式,表達式中的signed型整數會被轉換為unsigned型。 int i = -5; unsigned int ii = 1; cout<<(i>ii)<<endl; //輸出1,非常有趣的結果 原因是int型的i轉換為unsigned int型 short i = -5; unsigned short ii = 1; cout<<(i>ii)<<endl; //輸出0 比較時short和unsigned short都提升為int型