《从陷阱中学习C/C++》读书笔记
1、运算符优先级很容易引起问题,如a = 4<<1+1,由于<<的优先级低于+,故其执行过程为 a = 4<<(1+1);
常见的运算符优先关系:(具体参照博客)
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
-- |
() |
圆括号 |
(表达式)/函数名(形参表) |
-- |
||
. |
成员选择(对象) |
对象.成员名 |
-- |
||
-> |
成员选择(指针) |
对象指针->成员名 |
-- |
||
|
|||||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
~ |
按位取反运算符 |
~表达式 |
|||
++ |
自增运算符 |
++变量名/变量名++ |
|||
-- |
自减运算符 |
--变量名/变量名-- |
|||
* |
取值运算符 |
*指针变量 |
|||
& |
取地址运算符 |
&变量名 |
|||
! |
逻辑非运算符 |
!表达式 |
|||
(类型) |
强制类型转换 |
(数据类型)表达式 |
-- |
||
sizeof |
长度运算符 |
sizeof(表达式) |
-- |
||
|
|||||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
|||
% |
余数(取模) |
整型表达式%整型表达式 |
|||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
|||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
|||
|
|||||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
|||
< |
小于 |
表达式<表达式 |
|||
<= |
小于等于 |
表达式<=表达式 |
|||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
|||
|
|||||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
|
|||||
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目运算符 |
|
|||||
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
-- |
/= |
除后赋值 |
变量/=表达式 |
-- |
||
*= |
乘后赋值 |
变量*=表达式 |
-- |
||
%= |
取模后赋值 |
变量%=表达式 |
-- |
||
+= |
加后赋值 |
变量+=表达式 |
-- |
||
-= |
减后赋值 |
变量-=表达式 |
-- |
||
<<= |
左移后赋值 |
变量<<=表达式 |
-- |
||
>>= |
右移后赋值 |
变量>>=表达式 |
-- |
||
&= |
按位与后赋值 |
变量&=表达式 |
-- |
||
^= |
按位异或后赋值 |
变量^=表达式 |
-- |
||
|= |
按位或后赋值 |
变量|=表达式 |
-- |
||
|
|||||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
--
|
2、c++宏定义时要对每个变量和语句加上括号,在编译之前,将宏带入语句看有没有逻辑错误,
在c++中使用宏定义的函数尽量使用内联函数代替;
使用宏来存储常量的尽量使用const变量代替;
使用宏来缩短长变量名的,可以用引用来代替;
3、给宏定义命名时,要避免宏名称与系统库的定义同名。
4、将char转换为int时,一定要注意char的最高位是0还是1,一般系统中char为为有符号的,在将char转换为int时,先将char转换为unsigned char
char a = 0x9A; int util = (int)(unsigned char)a;
将int转换为char时,如果系统默认char为unsigned char,则下面会出现问题:
char c; while((c = getchar())!=EOF){ //如果char是unsigned char则EOF(-1)会转换为(unsigned char)255 putchar(c); }
解决方法是
int c; //去除int转换为char的强制转化 while((c = getchar())!=EOF){ putchar(c); }
对于变量的强制转换,需要注意类型的截断和扩展。
一定要注意有符号int和无符号int比较的后果:
#include <iostream> #include <vector> using namespace std; int main(){ vector<int> a; a.push_back(1); a.push_back(2); int i = -1; if(i< a.size()) cout<<"True"<<endl; ////由于size()返回的是size_t,其是unsigned int此时i会转换成unsigned int, else cout<<"False"<<endl; return 0; }
在位域结构体中,注意结构体变量类型
#include <iostream> #include <vector> using namespace std; struct data{ int flag : 1; //注意这代表符号位,取值范围为0或-1 int other: 31; }; int status(){ return 1; } int main(){ struct data test; test.flag = 1; //返回值1赋给flag时会出现溢出, cout<<test.flag; }
解决方法是将结构体中的变量变成unsigned int
5、浮点数比较时,一般比较他们的差值在一定范围内
6、关于常量指针和指针常量:
int *const px ; //指真本身是常量,它指向的地址是不可改变的,但地址里的内容可以通过指针改变。
const int *py; // 指针指向的是常量,它不能指向变量,它指向的内容不能改变,不能通过指针来修改它指向的内容,但指针本身不是常量,它自身的值可以改变。
7、关键字extern可以置于变量和函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量或函数时在其他模块中寻找定义。
8、对结构体进行赋值时,建议具体到结构体的成员变量名。
struct rectangle { int length; int width; } struct rectangle rect ={rect.length = 4, rect.width = 2};
9.关于cin>>和getline混用导致的奇怪问题
#include <iostream> #include <vector> using namespace std; int main(){ int num; string str; cin >> str; //读取到连续的字符串后,立即停止,用户按下回车键时,"\n"留在输入流中 cin.ignore(); //解决方法用cin.ignore()清除留在输入流中的换行符 getline(cin,str); //getline遇到换行符结束 cout<<num << " "<<str<<endl; }