++i和i++关于[自增运算符]和[顺序点]
又是招聘季,同学有去找工作的,谈到i = 1, a = i++ + i++ + ++i的值是多少的问题
这是个老问题,当初上课的时候的想法是,这种边缘代码,根本不值得花时间。
今天无聊去求是了一下,结论是上述语句在c标准中是【未定义】的。
这里要说明一下c标准中的几个概念:
implementation-defined(实现决定):编译器可以按照自己的方便在候选的几种选择中选择一种方案实现,必须文档说明。
unspecified(未说明):undocumented implementation-defined:同实现决定,可以不在文档注明。
undefined(未定义):标准不对编译器的行为做规定。(当然,最好当然是直接不通过编译,有的编译器会按自己的方式实现)
两个相邻的顺序点之间,同一个内存单元的值只能改变一次。如果一个值被改变,此期间对该值的引用只能用于改变该值(读在写之前)。文首那个表达式就是改变了两次,c+和c++标准作为未定义处理。
1、 && (logical AND), || (logical OR)
2、三元操作符? : 的“?”处
3、full expression完整表达式结束处。完整表达式:不是另一个表达式的字表达式(for中的3个表达式也算完整表达式)。一般的语句都属此情况,分号处都存在顺序点。
4、将要进入函数调用前。即所有参数求值完毕,还未调用函数。(函数参数的求值和压栈顺序本身是实现决定的,见上文)。也就是说f(i++)+g(i++)+f(++i)是well-defined的,因为中间存在顺序点。
5、函数返回,返回值拷贝到调用上下文。(仅c++,wiki如是说,我就懒得考证了。。)
6、定义变量处。如int a = 5;(感觉这和3有重复)
7、定义多个变量时,多个变量间。如int a=5,b=3; (此逗号处也有顺序点,注:wiki中给了c++标准的引用,c未考证)
两个相邻的顺序点之间(暂叫无序段。。),同一个内存单元的值只能改变一次。——若能改变多次,2次改变被分配到不同的核执行,因为无序段中的指令顺序不做规定,可能引起不一致(写写)的情况。
如果一个值被改变,此期间对该值的引用只能用于改变该值(读在写之前)。——同样,这里会出现读写不一致。
总之,这两句话可以看作保证并发情况下逻辑正确性的一种简单约束,当然这种底层地方做复杂的约束时间开销上可能会得不偿失。(另一种意图是交给编译器一定的自由,方便某些设计和做优化)
不过就顺序点的机制看,c语言很难做到指令级并行,因为对c的情形来说,这种顺序点的定义太密集,太僵硬。