C++ Primer阅读笔记--表达式和运算符的使用

1--左值和右值

        C++ 的表达式有右值(rvalue, are-value)和左值(lvalue, ell-value)两个形式;当一个对象被用作右值时,使用的是对象的值(内容);当对象被用作左值时,使用的是对象的身份(在内存中的位置);

        使用关键字 decltype 作用于左值和右值时,返回的类型不同:

        当表达式的求值结果是左值时,decltype 作用于该表达式(不是变量)将得到一个引用类型;

        当表达式的求值结果是右值时,decltype 作用于该表达式将得到与表达式相同的类型;

int *p;

decltype(*p) // int &

decltype(&p) // int **

// *解引用返回的是左值,所以 decltype(*p) 返回的是引用类型
// &取地址符返回的是右值,所以 decltype(&p) 返回的类型与 &p 相同,
// 即指向 p 的指针,而 p 的类型是int *,所以最终返回的是int **,指向整型指针的指针

2--除法和取余

        C++11 新标准规定商一律向 0 取整(直接切除小数部分);

        除去 -m 导致溢出的特殊情况,(-m)/n 和 m/(-n) 都等于-(m/n)m%(-n)等于m%n(-m)%n等于-(m%n); 

21 % 6; // 2
21 % 7; // 0
-21 % -8; // -5 // -21%-8 == -(21%-8) == -(21%8) == -5
21 % -5; // 1

21 / 6; // 3
21 / 7; // 3
-21 / -8; // 2
21 / -5; // -4

3--递增和递减运算符

        递增和递减运算符有两种形式:前置版本和后置版本;

        前置版本首先将运算对象加1 (减1),然后将改变后的对象作为求值结果进行返回;后置版本同样将运算对象加1 (减1),但会将运算对象改变前的值的副本作为求值结果进行返回;

        递增和递减运算符都必须作用于左值运算对象,前置版本将对象作为左值进行返回,后置版本则将对象原始值的副本作为右值返回;

4--条件运算符

cond ? expr1, expr2;

        首先求 cond 的值,如果条件为真,对 expr1 求值并返回该值,否则对 expr2 求值并返回该值;

        当条件运算符的两个表达式都是左值或能转换成同一种左值类型时,运算结果是左值,否则运算的结果是右值;

        允许在条件运算符的内部嵌套另一个条件运算符,即条件表达式可以作为另一个条件运算符的 cond 或 expr;

cond1 ? exprA : (cond2) ? exprB : exprC;

5--位或运算符和逻辑运算符

位或运算符:逐位进行相应的逻辑操作

        &(位与)、|(位或)、^(位异或)、~(位求反);

逻辑运算符:

        &&(逻辑与)、||(逻辑或)、!(逻辑非);

6--sizeof运算符

        sizeof 运算符返回一条表达式或一个类型名字所占的字节数,其值是一个 size_t 类型的常量表达式;

        sizeof 运算符的结果部分地依赖于其作用的类型:

① 对 char 或类型为 char 的表达式执行 sizeof 运算,其结果为 1;

② 对引用类型执行 sizeof 运算得到被引用对象所占空间的大小;

③ 对指针执行 sizeof 运算得到指针本身所占的空间大小;

④ 对解引用指针执行 sizeof 运算得到指针指向的对象所占的空间大小,指针无需有效;

⑤ 对数组执行 sizeof 运算得到整个数组所占的空间大小,等价于对数组中所有的元素各执行一次 sizeof 运算并将所得结果求和;

⑥ 对 string 对象或 vector 对象执行 sizeof 运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间;

7--类型转换

        命名的强制类型转换:cast_name<type>(expression); 其中 type 是转换的目标类型,expression 是待转换的值,cast_name 是static_cast、dynamic_cast、const_cast 和 reinterpret_cast 中的一种;

        任何具有明确定义的类型转换,只要不包含底层 const (指针的指向可以改变,指针指向的值不能修改,类似于指向常量的指针),都可以使用 static_cast;当需要把一个较大的算术类型赋值给较小的类型时,static_cast 非常有用,即告诉编译器这个操作我们知道会损失精度但不在乎精度的损失,从而可以关闭警告信息;​​​​​​​

double A = static_cast<double>(B) / C;
// 对 B 进行强制类型转换以便执行浮点数除法;

        const_cast 只能改变运算对象的底层 const (指针的指向可以改变,指针指向的值不能修改,类似于指向常量的指针),对于将常量对象转换成非常量对象的行为,一般称其为 “去掉const性质”;

        一旦去掉某个对象的 const 性质,编译器就不会阻止对该对象进行写操作;​​​​​​​

#include "iostream"

int main(int argc, char* argv[]){
    const int a = 30;
    const int *p = &a;
    int *q = const_cast<int*>(p);
    *q = 10;

    std::cout << "a: " << a << std::endl;
    std::cout << "*p: " << *p << std::endl;
    std::cout << "*q: " << *q << std::endl;

    std::cout << "&a: " << &a << std::endl;
    std::cout << "p: " << p << std::endl;
    std::cout << "q: " << q << std::endl;

    return 0;
}

        当使用 const 声明一个常量时,在 C++ 中会把这个常量写到 ROM(磁盘)中,并且这个 ROM 地址会映射到内存,指向的地址不变。

        当使用 const_cast 去掉 const 限定后,将地址赋给另外的变量指针;当改变变量指针的值时,实际上是改变了内存地址中存的数据,但并没有改变在外存(磁盘)中的数据。因此当读取变量指针的值时,其实是取内存中的数据,当使用原常量名时取的是外存中的数据;

猜你喜欢

转载自blog.csdn.net/weixin_43863869/article/details/130295934