分类: C/C++
2007-09-10 17:00:05
例如,有人报告如下代码:
int a = 123, b = 7654; a ^= b ^= a ^= b;
在上一个和下一个序列点之间, 一个对象所保存的值至多只能被表达式的计算修改一次。而且前一个值只能用于决定将要保存的值。
第二句话比较费解。它说在一个表达式中如果某个对象需要写入, 则在同一表达式中对该对象的访问应该只局限于直接用于计算将要写入的值。这条规则有效地限制了只有能确保在修改之前才访问变量的表达式为合法。例如 i = i+1 合法, 而 a[i] = i++ 则非法.
21.14 怎样不用临时变量而交换两个值?
一个标准而古老的汇编程序员的技巧是:
a ^= b; b ^= a; a ^= b;
但是这样的代码在现代高级程序设计语言中没什么用处。临时变量基本上是自由使用的, 一般上的三个赋值是:
int t = a; a = b; b = t;
这不只对读者更清晰, 更有可能被编译器辨别出来而变成最有效的代码 (例如有可能使用 EXCH 指令)。后面的代码明显的可以用于指针和浮点值, 而不象 XOR 技巧只能用于整型
11.2 怎么写一个一般用途的宏交换两个值?
对于这个问题没有什么好的答案。如果这两个值是整数, 可以使用异或的技术, 但是这对浮点值或指针却不行, 对同一个值也无能为力。 (参见问题 和 。) 如果希望这个宏用于任何类型 (通常的目标), 那么它不能使用临时变量, 因为不知道需要什么类型的临时变量 (即使知道也难以找出一个名字), 而且标准 C 也没有提供 typeof 操作符。
最好的全面解决方案可能就是忘掉宏这回事, 除非你还准备把类型作为第三个参数传入。
4.5 我可否用括号来强制执行我所需要的计算顺序?
一般来讲, 不行。运算符优先级和括弧只能赋予表达是计算部分的顺序. 在如下的代码中
f() + g() * h()
尽管我们知道乘法运算在加法之前, 但这并不能说明这三个函数哪个会被首先调用。
如果你需要确保子表达式的计算顺序, 你可能需要使用明确的临时变量和独立的语句。
4.9 ++i 和 i++ 有什么区别?
如果你的 C 语言书没有说明它们的区别, 那么买一本好的。简单而言: ++i 在 i 存储的值上增加一并向使用它的表达式 ``返回" 新的, 增加后的值; 而 i++ 对 i 增加一, 但返回原来的是未增加的值。
4.10 如果我不使用表达式的值, 我应该用 ++i 或 i++ 来自增一个变量吗?
由于这两种格式区别仅在于生成的值, 所以在仅使用它们的副作用时, 二者完全一样。但是, 在 C++ 中, 前缀方式却是首选。
4.11 为什么如下的代码 int a = 100, b = 100; long int c = a * b; 不能工作?
根据 C 的内部类型转换规则, 乘法是用 int 进行的, 而其结果可能在转换为 long 型并赋给左边的 c 之前溢出或被截短。可以使用明确的类型转换, 强迫乘法以 long 型进行:
long int c = (long int)a * b;
注意, (long int)(a * b) 不能达到需要的效果。
当两个整数做除法而结果赋与一个浮点变量时, 也有可能有同样类型的问题, 解决方法也是类似的。
4.12 我需要根据条件把一个复杂的表达式赋值给两个变量中的一个。可以用下边这样的代码吗? ((condition) ? a : b) = complicated_expression; 不能。
? : 操作符, 跟多数操作符一样, 生成一个值, 而不能被赋值。换言之, ? : 不能生成一个 ``左值"。如果你真的需要, 你可以试试下面这样的代码:
*((condition) ? &a : &b) = complicated_expression;
尽管这毫无优雅可言。