本 Wiki 开启了 HTTPS。但由于同 IP 的 Blog 也开启了 HTTPS,因此本站必须要支持 SNI 的浏览器才能浏览。为了兼容一部分浏览器,本站保留了 HTTP 作为兼容。如果您的浏览器支持 SNI,请尽量通过 HTTPS 访问本站,谢谢!
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录前一修订版后一修订版 | 前一修订版 | ||
cs:programming:cpp:courses:cpp_basic_deep:chpt_4 [2024/09/23 12:23] – [逻辑与关系操作符] codinghare | cs:programming:cpp:courses:cpp_basic_deep:chpt_4 [2024/10/03 06:03] (当前版本) – [sizeof] codinghare | ||
---|---|---|---|
行 135: | 行 135: | ||
</ | </ | ||
<WRAP center round info 100%> | <WRAP center round info 100%> | ||
- | entity: unparenthesized id-expression or an unparenthesized class member access expression. | + | Entity: **unparenthesized id-expression** or an **unparenthesized class member access expression**. |
</ | </ | ||
===类型转换=== | ===类型转换=== | ||
行 269: | 行 269: | ||
* 优先级:与高于或 | * 优先级:与高于或 | ||
===位操作符=== | ===位操作符=== | ||
+ | * '' | ||
+ | * 接受右值,返回右值 | ||
+ | * 除取反,都是左结合 | ||
+ | * 计算过程中可能会存在整型提升 | ||
+ | * 不存在短路逻辑 | ||
+ | <code cpp> | ||
+ | char x = 3; // 00000011 | ||
+ | ~x; // -4, 11111100 | ||
+ | char y = 5; // 00000101 | ||
+ | x & y; // 00000001 | ||
+ | x | y; // 000000111 | ||
+ | x ^ y; // 00000110 | ||
+ | </ | ||
+ | ==左移右移== | ||
+ | * 缺出来的位置用 0 补全 | ||
+ | * 一定条件下可以替代乘/ | ||
+ | <code cpp> | ||
+ | char x = 3; //00000011 | ||
+ | x >> 1; // 00000001 | ||
+ | char y = -4; // 11111100 | ||
+ | // 与输出操作符合并使用时,需要使用括号进行重载 | ||
+ | std::cout << (y << 1); // 11111000 | ||
+ | </ | ||
+ | ==整型提升会根据符号位来填充== | ||
+ | <code cpp> | ||
+ | unsigned char x = 3; | ||
+ | // char 到 int 的提升 | ||
+ | // unsigned 会按位进行 0 的补全 | ||
+ | unsigned char z = 0xff // 11111111 | ||
+ | // 0000...00011111111 总共32位 | ||
+ | auto y = ~x; // 结果为 256 | ||
+ | //signed 的提升会按照符号位来进行补全的提升,这里是 1 | ||
+ | signed char z = 3; | ||
+ | // 提升过后的值为 11111......111111 | ||
+ | // 求反后的结果是 00000....000000 | ||
+ | y = ~z; // 结果是 0 | ||
+ | </ | ||
+ | ===赋值操作符=== | ||
+ | * 赋值操作符左边为**可修改的**左值,右边为可转换为左边类型的右值。 | ||
+ | * 赋值操作符为右结合(先评估等号右边) | ||
+ | * 求值结果为左算子 | ||
+ | * 可以引入大括号防止 narrowing converstion | ||
+ | <code cpp> | ||
+ | short x; | ||
+ | // error, can't store a unsigned int to short | ||
+ | x = {0x80000003}; | ||
+ | // 无精度损失的转换不会被阻止 | ||
+ | x = {3}; | ||
+ | // 只要存在 norrowing conversion 的可能,编译器就不会通过 | ||
+ | // y 可能会被修改导致 norrowing conversion | ||
+ | int y = 3; | ||
+ | x = {y}; | ||
+ | // 使用编译器期 const 确保 y 不会被修改 | ||
+ | constexpr int y = 3; | ||
+ | x = {y}; | ||
+ | </ | ||
+ | * 赋值操作符的优先级非常低 | ||
+ | ==交换两个数== | ||
+ | <code cpp> | ||
+ | // bitwise xor | ||
+ | int x = 2; | ||
+ | int y = 3; | ||
+ | // x = 2^3 | y = 3 | ||
+ | x^=y; | ||
+ | // 任何数与 0 xor 结果都是其本身 | ||
+ | // x = 2^3 | y = 3^2^3 = 2^3^3 = 2^0 = 2 | ||
+ | y^=x; | ||
+ | // x =2^3^2 = 3 | y = 2 | ||
+ | x^=y; // 最后结果 x = 3, y =2 | ||
+ | </ | ||
+ | ===自增 / 自减操作符=== | ||
+ | * 后缀 '' | ||
+ | * 前缀 '' | ||
+ | * 前缀返回**左值**,后缀返回**右值** | ||
+ | <WRAP center round box 100%> | ||
+ | * 后缀在返回的时候当前变量已经更新,因此只能返回一个历史内容,属于临时变量,因此是右值 | ||
+ | * 前缀可以视作 x = x + 1; 得到是左值。该左值还可以接着放到等号左边,因此 ++(++x) 也是合法的。 | ||
+ | * 推荐使用前缀:后缀会创造**临时变量**并返回,效率较低。 | ||
+ | * 后缀一般用于需要利用返回值的时候,比如运算符重载时(类)可能会用到 | ||
+ | </ | ||
+ | ===其他操作符=== | ||
+ | ==成员访问操作符== | ||
+ | * '' | ||
+ | * 实质是 '' | ||
+ | <code cpp> | ||
+ | struct Str { int x }; | ||
+ | int main() | ||
+ | { | ||
+ | Str a; | ||
+ | // a 是左值,返回左值 | ||
+ | a.x; | ||
+ | // Str() 是 右值, 返回值为右值引用 | ||
+ | Str().x; | ||
+ | // | ||
+ | ptr-> | ||
+ | } | ||
+ | </ | ||
+ | ==三元条件操作符== | ||
+ | <code cpp> | ||
+ | // 只会求值一个分支 | ||
+ | true ? 3:5; | ||
+ | // 条件表达式返回的类型必须相同 | ||
+ | ture ? 1: " | ||
+ | // 都是左值,则返回左值,否则返回右值 | ||
+ | int x = 0; | ||
+ | false ? 1 : x; // 返回右值 | ||
+ | // 右结合 | ||
+ | // 先判断 score == 0 | ||
+ | int score = 100; | ||
+ | int res = (score > 0) ? 1: (score == 0) ? 0:-1; | ||
+ | </ | ||
+ | ==逗号操作符== | ||
+ | * 典型应用: | ||
+ | * for 循环中可以写出较为复杂的语句 | ||
+ | * 元编程:折叠表达式,包展开 | ||
+ | * 函数的参数表达式不是逗号操作符,参数列表求值顺序不定 | ||
+ | <code cpp> | ||
+ | // 确保操作数从左向右求值 | ||
+ | // 求值结果为右算子 | ||
+ | 2, 3; // result is 3 | ||
+ | // 左结合 | ||
+ | // (2, 3) , 4 | ||
+ | 2, 3, 4; | ||
+ | </ | ||
+ | ==sizeof== | ||
+ | * 返回类型 / 对象 / 表达式返回值占用的字节数 | ||
+ | <code cpp> | ||
+ | int x; | ||
+ | // 推荐统一使用带括号的形式 | ||
+ | sizeof(int); | ||
+ | sizeof(x); | ||
+ | // 对表达式评估时,不会真正执行求值 | ||
+ | int* ptr = nullptr; | ||
+ | // 等价 sizeof(int) | ||
+ | sizeof(*ptr); | ||
+ | </ | ||
+ | ==域操作符== | ||
+ | 用于访问域内的变量 | ||
+ | <code cpp> | ||
+ | int = x; | ||
+ | namspace ABC | ||
+ | { | ||
+ | int x; | ||
+ | } | ||
+ | int main() | ||
+ | { | ||
+ | int x; | ||
+ | int y = x; // local | ||
+ | int y = ::x; // global | ||
+ | int y = ABC::x // ABC | ||
+ | } | ||
+ | </ | ||
+ | ===C++17表达式求值顺序=== | ||
+ | * 之前的限定求值:逗号,三元条件,逻辑与 / 或(短路) | ||
+ | * C++17 新引入的限定 | ||
+ | <code cpp> | ||
+ | // 先求 e1,再求 e2 | ||
+ | e1[e2]; | ||
+ | e1.e2; | ||
+ | e1.*e2; | ||
+ | e1-> | ||
+ | e1<< | ||
+ | e1>> | ||
+ | e2=e1 / e2+=e1/ e2*=e1; | ||
+ | </ | ||
+ | * newType(e) 会先分配内存再求值 |