这篇笔记的内容是表达式。
一、表达式的基本概念 1.基本概念值得注意的是函数也是一种运算符。符号的运算符类型是编译器根据上下文来决定的。
常见的运算符的作用有:
组合运算、运算对象转换、重载运算符、左值和右值。
其中有一个值得注意的是:decltype在使用指针类型时
如下,即decltype中放指针时需要注意,其赋值的是引用,而不是int。
int a=1,* p=&a; decltype(*p) delc1=*p; decltype(p) delc2 = p; decltype(&p) delc2 = &(p);二、算术运算符
这个好像没啥好说的。
三、逻辑与关系运算符这个好像也没啥好说的。
四、赋值运算符这个好像也没啥好说的。
五、递增递减运算符这个好像也没啥好说的。
六、成员访问运算符这里主要得注意一个点。对象指针要用点来调用函数的话要加上括号。
七、条件运算符这个还是蛮有用的,可以精简代码量。条件运算符也称三目运算符。
1.条件运算符嵌套值得注意的是不要嵌套太多层,要不然可读性降低了。
2.输出表达式中使用条件运算符以上语句执行的结果:
八、位运算符1.移位运算符 2.位求反运算符 3.位与、位或、位异或运算符
4.位运算符使用
关于1UL<<27
0UL 表示无符号长整型 0 1UL 表示无符号长整型 1 1UL<<27 表示输入一个仅27位为1的无符号数5.移位运算符(又称IO运算符)满足左结合律
主要是注意优先级,尤其在打印的时候很容易出问题。
九、sizeof运算符需要注意的是sizeof一个数组会返回数组的大小,而sizeof一个string或者vector返回的都是固定值。这也说明了string和vector的本质是封装的对象而不是基本数据类型。
另外一个值得注意的就是sizeof的返回值是size_t类型。size_t类型是一个无符号整型数。
在32位平台运行的话:
在64位平台运行的话:
最终总结即size_t的大小是由运行的目标平台所决定的,其代表目标平台下最大可能的数组尺寸。关于size_t可进一步参考:size_t 这个类型的意义是什么? - 知乎
十、逗号运算符这个似乎也没啥好说的。
十一、类型转换 1.隐式转换1)举例
即注意,ival在实际执行过程中是将3转换为double类型与3.14运算,最终再截断的。
2)隐式转换发生的情况
2.算术转换通俗点解释就是把两种不同数据类型的变量放一块了,最终计算机会怎么处理这两个变量。
1)整型提升
怎么个提升这些都是需要经验积累的
2)无符号类型的运算对象
这个地方还蛮复杂的。 接下来举几个例子把。
//unsigned int与int之间的较量--a>b>0 unsigned int a1 = 2; int b1 = 1; cout << typeid(a1 + b1).name() << endl << a1 + b1<0>b unsigned int a2 = 2; int b2 = -3; cout << typeid(a2 + b2).name() << endl << a2 + b2 << endl; //unsigned int与int之间的较量--a>0>b unsigned int a3 = 2; int b3 = -1; cout << typeid(a3 + b3).name() << endl << a3 + b3 << endl; //unsigned int与long之间的较量,最后是unsinged long比较有意思了 unsigned int a4 = 2; long b4 = -1; cout << typeid(a4 + b4).name() << endl << a4 + b4 << endl; //unsigned int与long long之间的较量 unsigned int a5 = 2; long long b5 = -1; cout << typeid(a5 + b5).name() << endl << a5 + b5 << endl; //unsigned int与double之间的较量 unsigned int a6 = 2; double b6 = -1; cout << typeid(a6 + b6).name() << endl << a6 + b6 << endl; //unsigned int与float之间的较量,值得关注的是虽然float与unsigned int都只有四位, //但是float的级别就是更高 unsigned int a7 = 2; float b7 = -1; cout << typeid(a7 + b7).name() << endl << a7 + b7 << endl;
运行结果:
3)大量例子举例
2.其他的隐式转换关于数组的转换比较恶心,数组名说是指针又和指针有所区别,举一下几个例子:
int ia[10];//sizeof数组名得到的值是整个数组所占空间 cout << typeid(ia).name() << endl << sizeof(ia)<< endl; decltype(ia) ib;//ib直接是数组 cout << typeid(ib).name() << endl << sizeof(ib) << endl; int* ic = ia;//ic还是一个int指针 cout << typeid(ic).name() << endl << sizeof(ic) << endl; int (&id)[10] = ia;//使用引用进行数组赋值 cout << typeid(id).name() << endl << sizeof(id) << endl;
运行结果:
比较值得注意的是,对隐式转换来说,非常量可以转常量,常量不能转为非常量了,这是一条不归路。
3.显式转换即光明正大的强转。结合参考网站进行理解更佳:C++强制类型转换运算符(static_cast、reinterpret_cast、const_cast和dynamic_cast)
1)比较安全的强转
关于左值右值,参考:什么是左值?_百度知道
C语言左值与右值详解
static_cast
static_cast基本上能满足正常的cast需求了。
const_cast
这一块有点混乱,下面举个几例子说明:
//example1
char c = 'a';
const char* pc = &c;
char* p = const_cast(pc);
*p = '1';
cout << c << endl <<*pc<(pc1);
*p1 = '2';
cout << c1 << endl <<*pc1<(&c1);
*p3 = '3';
cout << c1 << endl << *pc1 << endl << *p3 << endl << "**************" << endl;
//强行对&c1直接做强转后的指针也无法改变c1的值
//example4
string s1 = static_cast(&c1);
string s2 = static_cast(pc);//即输出地址,地址不是const的
cout << (string)s1 << endl << (string)s2 << endl;
//string s = const_cast(pc1);//错的,const_cast无法改变属性
运行结果:
最值得注意的是example1和example2,这里面有大坑,详情看里面注释。
reinterpret_cast
reinterpret_cast是功能最强大的cast,同时也是最不安全的,在使用的时候要小心谨慎。
4.旧版本的强制转换从学C语言过来的话还是会更经常用这种,更简单。



