C语言编程之运算符

 |   
C  

在C语言中有很多运算符,像求字节数运算符[sizeof],赋值运算符,算术运算符,关系运算符,逻辑运算符,条件运算符,位运算符,指针运算符等。这些运算符在运算时是有优先级的,会最终影响结果。所以了解其优先级以及运算方法是挺重要的(虽然本人觉得这些都可以通过加括号来解决,但是还是要了解一下,以便更快读懂他人程序)。

类型转换

在C语言中,有很多双目运算符(一个运算符有两个操作数),一般情况下两个操作数的数据类型要是一样的,不过当两边类型不一致时就要进行==类型转换==。而类型转换的最基本的原则是==往最高级转换==。其转换的关系如下图:

convert_type

图中纵向代表转换的顺序,int->unsigned->long->double;而两个横向的转换是当表达式中出现了float会当作double来处理,出现了short,char类型都会当作int来处理,也就是说这步是必须的,必须执行的转换。所以我们得到以下的结论:

  1. 表达式的结果类型只有int/unsigned/long/double四种类型。
  2. 表达式的结果类型只要看表达式中数据类型最高的那个。

自增/自减

自增和自减概念类似,这里以自增为例。自增包括前自增和后自增。前自增(++i)是指变量先自增,然后将自增后的结果当作表达式的值;而后自增(i++)是指变量先将值当作表达式的值,然后再自增1。这个在大学学C语言就知道了,这里在提及是为了下面关于自增/自减的两种使用情况。

  1. 自增/自减操作符只能用于变量,不能用于表达式

     1#include<stdio.h>
     2
     3int main(){
     4    int i=1;
     5    printf("i = %d\n",i);
     6    printf("i++ = %d\n",i++);
     7    printf("i = %d\n",i);
     8    printf("++i = %d\n",++i);
     9    rintf("i = %d\n",i);
    10
    11    // error: lvalue required as increment operand
    12    //printf("(-i)++ = %d\n",(-i)++);
    13    //printf("++i++ = %d\n",++i++);
    14
    15    return 0;
    16}
    

    由注释的三行可知:当给表达式进行自增操作的时候,回报左值需要增量操作数。

  2. 在输出中,计算顺序是从右往左

     1#include<stdio.h>
     2
     3int main(){
     4    int i=1;
     5    // 输出多个自增
     6    printf("i = %d\n",i);
     7    printf("i = %d, i++ =%d\n",i,i++);
     8    printf("i = %d\n",i);
     9    printf("i++ = %d, ++i =%d\n",i,++i);
    10    printf("i = %d\n",i);
    11
    12    return 0;
    13}
    

    根据下图输出的截图,我们发现运算的顺序是从右往左的。代码见 附件

    increasment

混合运算

虽然本人觉得这部分绝对是装x的作死的,因为多用()就能解决表达式的可读性,但为了读懂某些装x作死的coder写的code,还是稍微了解一下。

  • 操作符的优先级

    1. 括号级别
      这是C语言中运算级别最高的操作符了。

      operator1

    2. 单目运算符
      第二级别的运算符有两个共同的特征:

      • 它们都是单目运算符(强制类型转换和长度运算符sizeof个人也觉得算单目)
      • 它们都是从右往左运算的(右撇子三剑客之一)。
        operator2
    3. 双目运算符
      第三级别里面的操作符就“群魔乱舞”了。

      1. 算术运算符
        对于算术运算符,可能都知道乘除高于加减,但是还得记得取模%和乘除是同级的。
        operator34
      2. 移位运算符
        有点疑问,见问题&回答1。
        operator5
      3. 关系运算符
        记住一点,大于小于高于等于。
        operator67
      4. 位操作符和逻辑操作符
        位操作符和逻辑操作符算是双目运算符中比较特殊的两个操作符,其特殊之处如下:
        • 按位取反操作符和逻辑非操作符是单目运算符,跻身第二级别
        • 按位操作符优先级高于逻辑操作符
        • 按位与& > 按位异或^ > 按位或|; 逻辑与&& > 逻辑或||

      operator8-12

    4. 三目运算符
      这也是从右往左运算的操作符(右撇子三剑客之一)

      operator13

    5. 赋值家族运算符 这是除了不常用的逗号运算符以外优先基本最低的运算符了,而且也是最后一个从右往左运算的操作(右撇子三剑客之一)。

      operator14

    6. 逗号运算符 这是优先级别最低的运算符,也是最不常用的运算符。

      operator15

  • 记忆要诀
    上面的优先级还是比较繁琐的,还是下面的基础要诀比较重要:

    • 括号最重要,操作数越多优先级越低(单目>双目>三目)
    • 双目中,算术运算符 > 移位运算符 > 关系运算符 > 位操作符 > 逻辑操作符
    • 三目运算符(条件运算符) > 赋值家族运算符 > 逗号运算符
    • 相同优先级中,按结合顺序计算。只有三种运算符是从右往左的,分别是单目运算符,条件运算符和赋值运算符。
  • 剪刀法切割表达式

    1short s;
    2int i;
    3float f;
    4double d;
    5unsigned u;
    6printf("%d\n",123%s+(i+'@')+i*u-f/d);
    

剪刀法是将表达式中的符号按照优先级由低到高的顺序依次减开,而且除了右撇子三剑客之外都要从右往左开始剪。比如上式表达式中按下面的顺序剪开: 1. u-f 2. )+i 3. s+(
此时表达式将变成四个部分,分别是123%s[int],(i+’@')[int],i*u[unsigned],f/d[double]。最终的结果也是double类型的。

问题&回答

  1. Q:在移位运算符中,我们发现其用法是变量 » 表达式,按理说这样的用法4»1应该会报错的(和自增/自减类似),但是执行代码 printf("%d\n",4>>1); 会获得结果2。
    A: 尚且无解

参考文献

  1. 计算机导论与C语言基础
  2. C语言运算符优先级列表
  3. 运算符优先级
技术茶话会
< 前一篇 后一篇 >