C++中的陷阱(一)

导言

程序设计语言中充满陷阱,一不小心就会掉入其中万劫不复,之所以有陷阱,是因为语言的设计细节不符合程序员的直觉

所以你会发现,语言越高级越注重顺从程序员的直觉。

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型

 

posted @ 2014-03-31 11:16  沐恩赖特  阅读(2142)  评论(0编辑  收藏  举报