《C语言解惑》之 谜题1.3 逻辑操作符和增量操作符
来源:互联网 发布:linux 双网卡静态路由 编辑:程序博客网 时间:2024/05/07 00:06
谜题1.3 逻辑操作符和增量操作符
请问,下面这个程序的输出是什么?
#define PRINT(int) printf("%d/n",int)
main()
{
int x, y, z;
x = 2; y = 1; z = 0;
x = x && y || z; PRINT(x); (1.3.1)
PRINT( x || ! y && z ); (1.3.2)
x = y = 1;
z = x ++ - 1; PRINT(x); PRINT(z); (1.3.3)
z += - x ++ + ++ y; PRINT(x); PRINT(z); (1.3.4) <-------
z = x / ++ x; PRINT(z); (1.3.5)
}
输出:
1 (1.3.1)
1 (1.3.2)
2 (1.3.3)
0
3 (1.3.4)
0
? (1.3.5)
解惑1.3 逻辑操作符和增量操作符
1.3.1
初始值:x=2,y=1,z=0
x = x && y || z
x = (x && y) || z
x = ((x && y) || z)
(x = ((x && y) || z))
按照操作符的优先级和关联规则进行绑定。
(x = ((TRUE && TRUE) || z))
逻辑操作符的求值顺序是从左到右。逻辑操作符的操作数如果是0,则解释为FALSE;如果是非零值,则解释为TRUE。
(x = (TRUE || z))
只有两个操作数都是TRUE,逻辑与(&&)的结果才会是TRUE;其他情况的求值结果都将是FALSE。
(x = (TRUE || 任意值))
只要有一个操作数是TRUE,那么不管另一个操作数是什么,逻辑或(||)的结果都将是TRUE。因此,我们不必再对这个表达式继续求值了。
(x = TRUE)
(x = 1)
1
再谈define:这个程序里的define语句与前一个程序里的define语句有所不同:这个程序里的“PRINT”是一个带参数的宏。在遇到带参数的宏时,C语言的预处理器将分两步进行替换:先把宏定义里的形式参数替换为实际参数,再把宏调用替换为宏定义体。
在这个程序里,“PRINT”宏有一个形式参数int。“PRINT(x)”是使用实际参数“x”进行的“PRINT”调用。在扩展“PRINT”调用的时候,C语言预处理器会先把宏定义里的所有“int”替换为“x”,再把宏调用“PRINT(x)”替换为结果字符串“printf("%d/n", x)”。请注意,形式参数int并不是匹配单词“printf”里的“int”字符的。这是因为宏定义里的形式参数是标识符——具体到这个例子,形式参数int只对标识符int进行匹配和替换。
1.3.2
初始值:x=1,y=1,z=0
x || ! y && z
x || (! y) && z
x || ((! y) && z)
(x || ((! y) && z))
对操作符和操作数进行绑定。
(TRUE || ((! y) && z))
(TRUE || 任意值)
TRUE,即1
按从左到右的顺序求值。
1.3.3
初始值:x=1,y=1
z = x ++ - 1
z = (x ++) – 1
z = ((x ++) - 1)
(z = ((x ++) - 1))
对操作符和操作数进行绑定。
(z = (1 - 1)),此时x=2
(z = 0)
0
出现在操作数右边的“++”操作符是所谓的“后”递增操作符:先在表达式里用该操作数完成有关的计算,再对它进行递增。
1.3.4
初始值:x=2,y=1,z=0
z += - x ++ + ++ y
z += - (x ++) + (++ y)
一元操作符的关联是从右到左,因而“++”操作符将先于一元操作符“-”得到绑定。(事实上,如果先去绑定“-”操作符的话,这个表达式将无法成立。这是因为“++”和“--”操作符都要求以一个变量(更准确地说,以一个“左值”)作为其操作数。“x”是一个左值,但“-x”不是。)
z += (- (x ++)) + (++ y)
z += ((- (x ++)) + (++ y))
(z += ((- (x ++)) + (++ y)))
(z += ((-2) + 2)),此时x=3,y=2
(z += 0)
(z = 0 + 0)
(z = 0)
0
按照从内到外的顺序依次求值。
关于记号:计算机程序的源代码文本是由一系列记号构成的,而编译一个程序的第一项工作就是把那些记号一个一个地分解开。这在绝大多数场合都没有什么困难,但有些字符序列可能会让人感到困惑。比如说,如果上面这个例子里的表达式有一部分根本没有空格——就像下面这样:
x+++++y
那么,这个没有空格的表达式与有空格的表达式还会是等价的吗?
为避免产生二义性,如果一个字符串能够解释为多个操作符,C语言编译器将按照“构成操作符的字符个数越多越好”的原则来作出选择。根据这一原则,“x+++++y”解释为:
x++ ++ + y
而这已经不是一个合法的表达式了。
1.3.5
初始值:x=3,z=0
z = x / ++ x
z = x / (++ x)
z = (x / (++ x))
(z = (x / (++ x)))
如果你还像以前那样按照从内到外的顺序对这个表达式进行求值,即先对x进行递增,然后作为除数、用x作被除数去进行除法计算。问题是:作为被除数的x到底是几?是3还是4?换个问法,被除数到底是递增前的x值,还是递增后的x值?请注意,C语言并没有对这种“副作用”作出明确的规定,而是由C编译器的编写者决定的。这个例子的教训是:如果你无法断定会不会产生副作用,那么就尽量不要写这样的表达式。
小结:
1. 编译器识别表达式遵循“大嘴法”或“贪心法”,即尽可能多的读入有意义的字符,注意空格可以分隔字符
2. z += - x ++ + ++ y :一元操作符的关联是从右到左,因而“++”操作符将先于一元操作符“-”得到绑定。
3. !优先级高于&&的优先级高于||,即!-->&&-->||
当&&左边为False时,不再计算右边,相应地,||左边为True时,不在计算右边。
4.未定义: (z = (x / (++ x)))
- 《C语言解惑》之 谜题1.3 逻辑操作符和增量操作符
- 谜题1.3 逻辑操作符和增量操作符
- 《C语言解惑》之 谜题1.2 赋值操作符
- 《C语言解惑》之 谜题1.4 二进制位操作符
- c语言之操作符
- 关系操作符和逻辑操作符
- 逻辑操作符和位操作符
- 逻辑操作符和关系操作符
- 关系操作符和逻辑操作符
- C语言学习之操作符和表达式
- 杂记之C语言函数strlen和操作符sizeof
- C语言之赋值操作符和表达式
- C语言中的移位操作(逻辑移位和算…
- C语言之sizeof操作符
- C语言之位操作符
- C语言基础之操作符总结
- c语言操作符
- C语言#,##操作符
- 三总选向卡的效果。。
- Flex获取URL中的参数
- sql里的cast和convert
- javascript event 事件解析
- Why is OpenVMS Secure? Worms? Viruses?
- 《C语言解惑》之 谜题1.3 逻辑操作符和增量操作符
- AskHL: Copying the LMF$LICENSE.LDB License Database?
- OpenVMS Web Browsers, Command-line Tools, and the odd RSS Feed Tool
- Virtual Machines and Hypervisors: Introduction
- JavaScript的方法和技巧
- Mixed-Version OpenVMS Cluster and the Cluster Protocol Version
- 关于完成端口的好文章
- 《C语言解惑》之 谜题1.4 二进制位操作符
- 一名25岁的董事长给大学生的18条忠告