表达式
本章摘录要点并给出准确表述与示例,便于复习。注意:现代 C++(自 C++11 起)对求值顺序有更精细的规定,但单个表达式中既修改又使用同一对象仍应避免未定义行为。
4.1 求值顺序与未定义行为
- 如果在同一表达式中对同一对象既有修改操作又有未被序列化(sequenced)使用,可能导致未定义行为。示例(不要写成这样):
cout << i << i++ << endl; // 未定义(依赖于求值顺序)
更安全的写法是将修改和读取分开为不同语句。
4.2 算术运算符与常见注意点
- 加、减、乘、除、取模等。除法在整数间是整数除法。注意溢出和除以 0。
4.3 关系运算符(与空指针检查)
示例:既要判空又要判非空字符串时常见写法:
void test03() {
const char *cp = "hello world";
if (cp && *cp) // cp 非空且首字符非 '\0'
cout << cp << '\n';
}
4.4 赋值运算符
- 赋值运算符的优先级通常低于关系运算符,因此
if (a = b)
会先执行赋值再判断结果是否为零,请谨慎使用。
4.5 递增和递减运算符
- 前置(++i)返回已递增后的左值,后置(i++)返回递增前的值(作为右值);对用户自定义类型而言,前置通常更高效。
示例说明:
cout << *iter++ << '\n'; // 等价于: cout << *iter; ++iter; (先解引用,再将迭代器后移)
4.6 成员访问与求值顺序
- 当一个子表达式会修改对象而另一个子表达式使用该对象时,求值顺序决定行为是否定义。建议避免在单个表达式中同时修改并使用同一对象。
4.7 条件(三元)运算符 ?:
- 三元运算符等价于简短的 if-else,可嵌套使用,但为了可读性,深度嵌套应避免。
示例:
std::string finalgrade = (grade > 90) ? "highGrade" : (grade < 60) ? "fail" : "pass";
4.8 位运算符
- 常见位运算:
&
(按位与)、|
(按位或)、^
(按位异或)、~
(按位取反)、<<
(左移)、>>
(右移)。 - 移位运算的优先级介于算术与关系运算之间;注意对符号位的影响以及移位超过类型位宽会产生未定义行为。
4.9 sizeof 运算符
sizeof
在编译期通常产生常量表达式(对动态大小数组的例外除外)。对数组使用sizeof
可得到整个数组大小(字节数),不是元素个数。
示例:
int b[10];
size_t bytes = sizeof(b); // 10 * sizeof(int)
size_t elems = sizeof(b) / sizeof(b[0]);
4.10 逗号运算符
- 逗号运算符从左到右求值,丢弃左侧结果,返回最右侧表达式的值,常用于较低层面的表达式组合;不要与逗号分隔符混淆(函数参数、初始化列表使用的逗号)。
示例:
int x = (f(), g()); // 会先调用 f(),然后调用 g(),x 取 g() 的返回值
4.11 类型转换(概述)
- 常见规则:整型提升(promotions)、算术转换(usual arithmetic conversions)、以及显式转换(
static_cast
,reinterpret_cast
,const_cast
)。 - 布尔上下文中会将非布尔值转换为
bool
(0 -> false,非 0 -> true)。
简要示例:
int a = 3; unsigned int b = 5u;
auto r = a + b; // usual arithmetic conversions: int -> unsigned int (可能导致符号相关行为)
double d = static_cast<double>(a) / 2; // 更明确的浮点除法
评论区 - 11_Expressions