问题表达式
目录
- 一. 概述
- 二. 例题
- 1. 例子一
- 2. 例子二
- 3. 例子三
- 4. 例子四
- 三. 分析
- 四. 总结
一. 概述
表达式的求值部分由操作符的优先级决定。但有时会由于编码的不规范,会导致表达式执行顺序混乱,出现问题。以下对于一些情况进行举例。
二. 例题
1. 例子一
a*b + c*d + e*f
例子一在计算的时候,由于*比+的优先级高,只能保证,*的计算是比+早,但是优先级并不
能决定第三个*比第一个+早执行。
所以表达式执行顺序可能是:((a*b) + (c*d)) + (e*f) 或者是:((a*b) + (c*d) + (e*f))
2. 例子二
c + --c
同上,操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得
知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。
假设c为2时,表达式可能是2+1也可能是1+1
3. 例子三
int main()
{int i = 10;i = i-- - --i * ( i = -3 ) * i++ + ++i;printf("i = %d\n", i);return 0;
}
非法表达式程序的结果:
值 | 编译器 |
---|---|
—128 | Tandy 6000 Xenix 3.2 |
—95 | Think C 5.02(Macintosh) |
—86 | IBM PowerPC AIX 3.2.5 |
—85 | Sun Sparc cc(K&C编译器) |
—63 | gcc,HP_UX 9.0,Power C 2.0.0 |
4 | Sun Sparc acc(K&C编译器) |
21 | Turbo C/C++ 4.5 |
22 | FreeBSD 2.1 R |
30 | Dec Alpha OSF1 2.0 |
36 | Dec VAX/VMS |
42 | Microsoft C 5.1 |
这段非法表达式在不同的编译器中,会有不同结果,这是极其危险的。
4. 例子四
#include<stdio.h>
int fun()
{static int count = 1;return ++count;
}
int main()
{int answer;answer = fun() - fun() * fun();printf("%d\n", answer);//输出多少?return 0;
}
因为count在创建的时候用static修饰过,所以每次调用后不会销毁。这时结果依然无法预测的,虽然在大多数的编译器上求得结果都是相同的。但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,再算减法。函数的调用先后顺序无法通过操作符的优先级确定。
所以,可能是2-3*4也可能是4-2*3。
三. 分析
代码如下:
#include <stdio.h>
int main()
{int i = 1;int ret = (++i) + (++i) + (++i);printf("%d\n", ret);printf("%d\n", i);return 0;
}
- 直接f10上反汇编
- 这里的i的地址与ebp-8地址相同,所以这里的意思就是把1放到i里面。
- 下图第一行,将ebp-8的值放到eax中,也就是说eax现在值为1。下图第二行,将eax增加1,现在eax值为2。下图第三行,将eax的值放到ebp-8中,现在ebp-8的值是2,所以i的值是2。由此可见这三行完成的是++i。
此时监听到的值:
- 同上,又完成了一次++i操作。这次用到的寄存器是ecx。
此时监听到的值:
- 同上,又完成了一次++i操作。这次用到的寄存器是edx。
此时监听到的值:
- 将ebp-8的值赋给eax。
此时监听到的值:
- 两次加法
此时监听到的值:
- 将最后的算出的值放到ret中。
此时监听到的值:
- 由此可见,在vs2019编译器中的算法是4+4+4,最后得出12。此处也可对gcc编译器中算法进行一个猜测,即3+3+4。
vs2019运行结果:
gcc运行结果:
四. 总结
我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。
本篇博客为本人学习C语言时的详细笔记,如有错误之处,还望各位指正。
文章为原创,如要转载请注明出处