C++表达式与运算符深度解析
立即解锁
发布时间: 2025-08-16 01:07:29 阅读量: 12 订阅数: 67 


C++编程语言精要与实践指南
### C++ 表达式与运算符深度解析
#### 1. 命令行参数与输入流
在编程中,`istringstream` 是一种特殊的输入流,它可以从字符串参数中读取数据。当读取到字符串末尾时,它会像其他输入流一样失效。使用 `istringstream` 时,需要包含 `<sstream>` 头文件。
修改 `main()` 函数以接受多个命令行参数并不困难,但在某些情况下并非必要,因为可以将多个表达式作为一个参数传递。例如:
```cpp
dc "rate=1.1934;150/rate;19.75/rate;217/rate"
```
这里使用引号是因为在 UNIX 系统中,`;` 是命令分隔符,不同系统在程序启动时传递参数的约定可能不同。
在输入例程中,为了灵活使用不同的输入源,将所有输入例程从使用 `cin` 改为使用 `*input` 并不是一个优雅的做法。如果从一开始就引入类似 `input` 的机制,可能就可以避免这种修改。实际上,输入源应该作为计算器模块的一个参数。当前计算器示例的根本问题在于,所谓的“计算器”只是一组函数和数据的集合,没有一个明确表示计算器的模块或对象。
#### 2. 代码风格
对于不熟悉关联数组的程序员来说,使用标准库的 `map` 作为符号表可能看起来像是作弊,但实际上标准库和其他库就是为了被使用而设计的。通常,库在设计和实现上比程序员为单个程序手工编写的代码更加精心。
观察计算器的代码,尤其是第一个版本,可以发现其中很少有传统的 C 风格的底层代码。许多传统的复杂细节已经被标准库类(如 `ostream`、`string` 和 `map`)的使用所取代。同时,代码中算术运算、循环和赋值操作相对较少,这在不直接操作硬件或实现底层抽象的代码中是合理的。
#### 3. 运算符总结
C++ 中的运算符种类繁多,下面是一些常见运算符的总结:
| 运算符分类 | 运算符 | 示例 |
| --- | --- | --- |
| 作用域解析 | `class_name :: member`、`namespace_name :: member`、`:: name`、`:: qualified-name` | `MyClass::myMember` |
| 成员选择 | `object . member`、`pointer -> member` | `obj.member`、`ptr->member` |
| 下标操作 | `pointer [ expr ]` | `arr[5]` |
| 函数调用 | `expr ( expr_list )` | `func(arg1, arg2)` |
| 值构造 | `type ( expr_list )` | `int(3.14)` |
| 后置递增/递减 | `lvalue ++`、`lvalue --` | `i++`、`i--` |
| 类型识别 | `typeid ( type )`、`typeid ( expr )` | `typeid(int)`、`typeid(obj)` |
| 类型转换 | `dynamic_cast < type > ( expr )`、`static_cast < type > ( expr )`、`reinterpret_cast < type > ( expr )`、`const_cast < type > ( expr )` | `dynamic_cast<Base*>(derivedPtr)` |
| 大小操作 | `sizeof expr`、`sizeof ( type )` | `sizeof(int)` |
| 前置递增/递减 | `++ lvalue`、`-- lvalue` | `++i`、`--i` |
| 位操作 | `~ expr`、`& expr`、`^ expr`、`| expr`、`<< expr`、`>> expr` | `~num`、`num1 & num2` |
| 逻辑操作 | `&& expr`、`|| expr`、`! expr` | `a && b` |
| 赋值操作 | `lvalue = expr`、`lvalue *= expr`、`lvalue /= expr` 等 | `i = 5`、`i *= 2` |
| 条件表达式 | `expr ? expr : expr` | `a > b ? a : b` |
| 异常抛出 | `throw expr` | `throw MyException()` |
| 逗号操作 | `expr , expr` | `a = (b, c)` |
运算符的优先级和结合性规则反映了最常见的使用方式。例如,`if (i<=0 || max<i)` 等价于 `if ( (i<=0) || (max<i) )`,而不是 `if (i <= (0||max) < i)`。在使用运算符时,如果对规则有疑问,应该使用括号来明确分组。
下面是运算符优先级和结合性的简单示意图:
```mermaid
graph LR
A[高优先级] --> B[一元运算符、赋值运算符(右结合)]
B --> C[其他运算符(左结合)]
C --> D[低优先级]
```
#### 4. 运算符结果
算术运算符的结果类型由“通常的算术转换”规则决定,目标是产生“最大”操作数类型的结果。例如,如果二元运算符有一个浮点操作数,则使用浮点运算,结果为浮点值;如果有一个 `long` 操作数,则使用长整数运算,结果为 `long`。小于 `int` 的操作数(如 `bool` 和 `char`)在应用运算符之前会转换为 `int`。
关系运算符(如 `==`、`<=` 等)产生布尔结果。用户定义运算符的含义和结果类型由其声明决定。
在逻辑可行的情况下,接受左值操作数的运算符的结果是一个表示该左值操作数的左值。例如:
```cpp
void f(int x, int y)
{
int j = x = y;
int* p = &++x;
int* q = &(x++);
int* pp = &(x>y?x:y);
}
```
如果 `?:` 的第二和第三个操作数都是左值且类型相同,则结果是该类型的左值。这种保留左值的方式在使用运算符时提供了更大的灵活性,特别是在编写需要统一高效处理内置类型和用户定义类型的代码时。
`sizeof` 的结果是一个无符号整数类型 `size_t`,指针减法的结果是一个有符号整数类型 `ptrdiff_t`。实现通常不会检查算术溢出,例如:
```cpp
void f()
{
int i = 1;
while (0 < i) i++;
cout << "i has become negative!" << i << '\n';
}
```
这段代码最终会尝试将 `i` 增加到超过最大整数,此时结果是未定义的,通常值会“回绕”为负数。
#### 5. 表达式求值顺序
表达式中子表达式的求值顺序是未定义的,不能假设表达式是从左到右求值的。例如:
```cpp
int x = f(2)+g(3);
```
这里无法确定 `f()` 和 `g()` 哪个先被调用。
不过,逗号运算符 `,`、逻辑与运算符 `&&` 和逻辑或运算符 `||` 保证其左操作数先于右操作数求值。对于内置类型,`&&` 的右操作数只有在其左操作数为 `true` 时才会求值,`||` 的右操作数只有在其左操作数为 `false` 时才会求值,这被称为短路求值。
需要注意的是,逗号运算符 `,` 与函数调用中用于分隔参数的逗号在逻辑上是不同的。例如:
```cpp
f1(v[i] ,i++);
f2( (v[i] ,i++) );
```
`f1` 调用有两个参数,参数表达式的求值顺序未定义;`f2` 调用有一个参数,逗号表达式 `(v[i] ,i++)` 等价于 `i++`。
可以使用括号来强制分组,例如 `a*b/c` 表示 `(a*b)/c`,要得到 `a*(b/c)` 必须使用括号。在许多浮点计算中,`a*(b/c)` 和 `(a*b)/c` 有显著差异,编译器会按照代码的书写方式求值。
#### 6. 运算符优先级
运算符的优先级和结合性规
0
0
复制全文
相关推荐









