• 坑人的运算符


    一、运算符优先级

    前两天我在园子的首页看到一篇随笔,随笔地址已经找不到了(着实有点抱歉),不过其中的一个代码片段还是记得一二,大概如下:

                Thread t = null;
                string message = "I'm ..." + t == null ? "And ..." : "";
                Console.WriteLine(message);
    

    代码很少,但我想有不少人会中招,最关键的问题就是运算符的优先级了。这里是 C#参考 运算符,根据这份优先级顺序,我总结出以下规律: 

    最高:.(点) ()  []  i++  i-- 

    其次:单目运算符(i++ 高于 ++i)

    然后:先算乘除余、后算加减、最后位移、比大小(大于小于 高于 相等和不等)

    接下来:Not And Or ( 即 ! , && , || )

    最后:三目 、赋值 和 Lambda

    这份优先级顺序已经包含了大部分运算符,只要不是遇到很变态的问题,我想应该可以搞定。

    二、编译器的贪心原则

    该话题源于《Java深入解析——透析Java本质的36个话题》。(虽然源于Java话题,但同样适用于C#、已验证)

    三个加号该如何运算呢? 代码片段如下:

                int i = 10;
                int j = 20;
                int k = i +++ j; // 代码未格式化,但可以正常编译运行
                Console.WriteLine(k); // 输出结果是多少呢? 是 30 还是 31?
    

    我们先不看答案,先把代码格式化一下、看看结果:  

    int k = i++ + j; // 格式化后的结果,为何不格式化成 i + ++j; 呢? 答案是:编译器的贪心原则
    

    贪心原则:编译器在分析符号的时候会尽可能多的结合有效的符号。  

    因为“+” 和 “++”都是有效的符号,但是“+++”不是有效的符号,因此,将表达式解析为了:i++ + j。

    再举个例子:a--b,本意为a - 负b。但是编译器解析为了 a-- b,最终编译不通过。所以代码应该这样写:a - -b; 

    三、 i++你真的理解了吗?

    这个例子我第一次看到、是在一份笔试试卷上,如果你能说出下面这个代码片段的正确答案,那这部分就没有必要看了,如果不能也许你走入了一个“误区”。

                int i = 0;
                i = i++;
                Console.WriteLine(i); // 1 or 0?
    

    我在记忆 i++和++i 的时候,不是记忆“先加”还是“后加”的,而是直接记住“运算结果”。

    i++的运算结果:表达式的值等于 i    ,i的值自加1.

    ++i的运算结果:表达式的值等于 i+1,i的值自加1.

    也就是说:

    i = i++; // 右侧表达式的结果等于i本身,然后执行赋值运算符赋值,最后得到的i的结果还是 i本身.

    补充:

    一楼评论里给出了疑问,对于该疑问我最简单的答案是:误区——i++的运算结果:表达式的值等于 i    ,i的值自加1。注意这里说的是运算结果,

    表达式的值就等于i了,之后没有再进行自加1,而i的值进行了自加1。

    让我们看一下IL代码吧,看完之后也许一切疑问都将烟消云散:

    .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint
      // 代码大小       17 (0x11)
      .maxstack  3
      .locals init ([0] int32 i)
      IL_0000:  nop
      IL_0001:  ldc.i4.0 // 将字面常量 0 入栈                                            栈结果:{0}
      IL_0002:  stloc.0  // 将栈顶值赋值给第0个局部变量(即 i),栈顶值出栈                    栈结果: {}, i = 0
      IL_0003:  ldloc.0  // 将第0个局部变量的值入栈 (即 0 入栈)                            栈结果:{0}
      IL_0004:  dup      // 复制栈顶值                            栈结果:{0、0}
      IL_0005:  ldc.i4.1 // 将字面常量 1 入栈                                            栈结果:{0、0、1}
      IL_0006:  add      // 将栈顶的两个值相加,栈顶的两个值出栈,计算结果入栈              栈结果:{0、1}
      IL_0007:  stloc.0  // 将栈顶值赋值给第0个局部变量(即 i),栈顶值出栈                   栈结果:{0}, i = 1 到此,完成 i++ 操作
      IL_0008:  stloc.0  // 将栈顶值赋值给第0个局部变量(即 i),栈顶值出栈                   栈结果:{}, i = 0  到此,完成 = 赋值操作 由此可以看出最终执行赋值操作的值其实就是最先入栈的 i 的值。
      IL_0009:  ldloc.0  
      IL_000a:  call       void [mscorlib]System.Console::WriteLine(int32)
      IL_000f:  nop
      IL_0010:  ret
    } // end of method Program::Main
    

    对于i++的问题,我觉得还是记运算结果为好,就像上面所总结的那样。

    四、零除以零等于什么?


    有人也许会说:“小学生都知道啊,0不能做除数”,如果是这样那恭喜你,答对了。但是只能得6分(满分10分)。为啥呢?看代码吧!

            static void Main(string[] args)
            {
                float x = 0;
                float result = x / x;
                Console.WriteLine(result); // 输出结果:非数字
    
                Console.Read();
            }
    

    此话题只是个引子,更多信息请参见:Single.NaN

    坑人的运算符,确实很坑人啊。

  • 相关阅读:
    Hdu1711 Number Sequence--Kmp模板题
    Trie入门--Poj3630 Phone List,查单词,HDU1251 统计前缀,PKU2503 Babelfish
    高次幂的组合数表示形式
    BZOJ1697 [Usaco2007 Feb] Cow Sorting牛排序
    1025 [SCOI2009]游戏(置换群,DP)
    Poj1721 Cards
    [Poi2003]Shuffle
    poj 3128 Leonardo's Notebook(置换的幂)
    POJ3734 Block母函数入门
    重心拉格朗日插值法
  • 原文地址:https://www.cnblogs.com/08shiyan/p/3944531.html
Copyright © 2020-2023  润新知