不要没事挑战i++和++i,比如x = x++;这种货!

先上结论:

  1. x++和x+1不是一回事!
  2. 这个玩意没有优先级!!!
  3. 这种写法,是C标准严格禁止的。和伸手摸电门一样,写这种代码属于做死
  4. 关于这种写法的结果的一切讨论,都是无意义的。

一段代码引出的纠结

先看如下一段代码,猜猜看,输出的是什么?

int a;
a = 0;
a = a++;
System.out.println("a = " + a);

最终输出的是:a = 0

为什么是0而不是1?

查看一下字节码,会发现++操作与+1不一样。

我们平时总是说前缀表达式优先级高,后缀表达式优先级低,其实并不是,这里根本不存在优先级问题,而一定要说的话,++运算符的优先级高于赋值运算符!

在程序运行a++时,a的值首先是赋值给一个拷贝或者说临时变量(按值传递,底层实现),即temp = a(即temp = a = 0),然后a执行自增运算(运算后a的值为1),最后将这个拷贝(此时拷贝的值为0)作为(a++)整体的值赋值给a(赋值后a的值有重新从1变为0),所以最终的a的值输出为0。

即a = a++;语句等价于:

a=(temp=a,a+=1,temp);

或者我们干脆这么理解:

int b = a++;
a = b;

那么x = x++;算什么?

这在c标准里,这种操作称为未定义行为。

C99中:
J.2 Undefined behavior
Between two sequence an object is modified more than once, or is modified and the prior value is read other than to determine the value to be stored (6.5).

大致意为在两个序列点(;或,)中,同一变量被修改超过一次的做法是未定义行为。
C99还规定,未定义行为由编译器自行处理,输出什么结果都可以!输出一个Hello World都是符合标准的。

x++确切的解释

i = i + i++之类问题,根本不是优先级的问题。

简单来说,a = b,和数学课本上的等式完全是两个意思。在计算机领域,它的意思是:先计算出表达式b的值,然后把这个值赋给a。

表达式的定义为:一个单独的字面值,或者一个单独的变量,或者通过算术/逻辑运算甚至函数调用连接起来的表达式——注意,赋值操作不是什么算术/逻辑运算,也不是函数调用。

显然,对于表达式b来说,它的运算符优先级有多复杂都不是问题。

但,因为太重要所以需要再说一遍:请注意,表达式里面不允许出现赋值操作,因为这个操作并不是算术/逻辑运算。

显然,i++的问题在于,虽然i++看起来只有操作符和操作数组合、而且通常作为表达式使用,但其实它的含义是i=i+1——这根本不是一个表达式,而是“计算表达式i+1的值,并将其赋予变量i”:换句话说,这里面额外有一个赋值操作。

事实上,i++本身作为一个c/c++语句,是不可删除的;而 2+3、a&&b、!a之类真正的表达式构成的单独语句则可以在编译时直接删除。原因就是i++另外还隐含了一个赋值操作,从而多了个会影响程序状态的“副作用”。

c/c++里面,类似这个赋值操作的、执行后会影响程序状态的行为,被称为“副作用(side effect)”。

进一步的,c/c++标准里面对这类有表达式外表、但却另有额外语义的“假”表达式叫做“有side effect的表达式”(关于何谓side effect,c/c++标准有专门定义,请尽量参考这个定义,因为我的转述很可能会有某些瑕疵之处,不可轻信),实质上也是强调了它和原始意义上的表达式的不同之处。

但是呢,为了写代码的便利,c/c++系语言提供了一个语法糖,允许程序员将i++用到表达式里面,同时规定其含义为:首先取i的值,用这个值代入表达式,供以后求值用;之后,执行i=i+1(执行i=i+1的确切时机不限,在表达式求值之前还是之后都行,只要执行了就对)。

如此一来,忽略副作用不提的话,i++看起来就像是一个真正的表达式。

但,必须注意,i++毕竟不是一个表达式,它毕竟还有个副作用藏在里面。粗暴的用某种规定允许它掺乎进去,就必然带来很多棘手的问题。

比如说,i=i++,这个语句如何解释?

首先,这显然是一个赋值语句,所以最终i应该存的是等号右侧表达式的值;虽然i++不是表达式,但按照规定,它可以解释为“语句执行前i的取值”;所以,这其实是把语句执行前,i的取值赋给i的一个赋值语句——也就是说,执行后,i的值应该不变。

但,注意i++还有一个赋值动作。即:把语句执行前的i值加一,然后赋值给变量i——所以,执行后,i的值应该增加了1。

显然,两个赋值动作的执行结果出现了矛盾。究竟哪个对呢?

进一步的,i=(i++)+(i++)呢?这里面可有三个针对i的赋值操作啊。

不仅如此,对于函数调用,如max(i++, i++),这又是什么意义呢?

很显然,不是表达式的i++,绝对不能和表达式混淆。

虽然,为了表达简洁,c/c++系列语言允许它在特定场合代替表达式,但这并不等于说,c/c++就认为它和表达式没有差别。相反,c/c++自始至终都认为它是一个赋值操作,只是可以在严格限定的场景替代表达式而已——这个“严格限定”,就是“不允许一个变量在一对序列点之间两次改变其值”(不太严谨的说法)。

只有满足了这个“严格限定”,程序才不会出现“二义”。

换句话说,i++本身是一个有着特定内涵(对i赋值)的指令,并不是单纯的数学表达式。把它当基本数学表达式滥用,得到的复合表达式是没有数学意义(因而也没有现实意义)的。

把它用对,是程序员的责任。

最后给个面试题的例子,看看就好,毕竟真有人考这玩意……

public class Test {
    public static void main(String[] args) {
        test1();
        test2();
        test3();
        test4();
        test5();
        test6();
        test7();
    }

    private static void test1() {
        int a;
        a = 10;
        a = a++;
        System.out.println("test1 a = " + a);//10
    }

    private static void test2() {
        int a, b;
        a = 10;
        a = a + a++;
        System.out.println("test2 a = " + a);//20
    }

    private static void test3() {
        int a;
        a = 10;
        a = a++ + a;
        System.out.println("test3 a = " + a);//21
    }

    private static void test4() {
        int a;
        a = 10;
        a = a++ + ++a;
        System.out.println("test4 a = " + a); //22
    }


    private static void test5() {
        int a, b;
        a = 10;
        b = a + a++;
        System.out.println("test5 a = " + a);//11
        System.out.println("test5 b = " + b);//20
    }

    private static void test6() {

        int a, b;
        a = 10;
        b = a++ + a;
        System.out.println("test6 a = " + a);//11
        System.out.println("test6 b = " + b);//21
    }

    private static void test7() {
        int a, b;
        a = 10;
        b = a++ + ++a;
        System.out.println("test7 a = " + a);//12
        System.out.println("test7 b = " + b);//22
    }
}

参考了作者:invalid s在https://www.zhihu.com/question/23180989/answer/23874381中的描述。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339

推荐阅读更多精彩内容