"C语言-语句,操作符和表达式"

  "C语言基础复习二"

Posted by Xu on January 8, 2018

C语言复习-语句,操作符和表达式

复习结构图: C_expression

空语句:

本身只包含一个分号,用在语法要求出现一条完整语句,但不需要执行任务的地方,如for语句。

表达式语句:

C不存在专门的”赋值语句”,任何赋值都在表达式中进行。

如:x = y + 3; 为表达式语句而不是赋值语句。

所以像: y + 3;这种语句也是合法的,只不过没有使用赋值操作符。

控制流语句

  • if(ex) else
  • while
  • break和continue:只对最内层循环起作用
  • for
  • do while(ex);
  • switch(ex) {//其中ex的值只能是整数

    case: ex;//case只是一个语句列表的进入点,并不把语句列表划分为几个部分,所以当匹配进入case后的表达式,其后的所有case表达式都要被执行。为了把语句列表划分为几个部分需要使用break语句来划分。

    default :ex;//进入switch,会先匹配case,若case均没有匹配成功则执行default,但进入default同样是一个进入点,后面的case一样会被穿透并执行(没有break的情况),所以default一般放在最后。

    }

    switch语句中的continue没有任何效果,除非位于某个循环内部。为了使同一组语句在两个或多个不同的表达式值时都能够执行,可以使用如下代码风格:

    switch(expression){
    case 1:
    case 2:
    case 3:  statement-list break;    
    }
  • goto 语句标签;//语句标签就是标识符后面跟一个冒号

操作符

  • 算术操作符:+,-,*,/,%(/当两个操作数都是整数时,执行整除运算,%取模运算,只接受整型操作数)
  • 移位操作符:»,«(右移位时,左边移入新位有两种解决方案:逻辑移位(0)和算术移位(由最左边位的值确定)),两边操作数必须为整型
  • 位操作符:&(与),|(或),^(异或),利用位操作符做一些简单的位操作,要求操作数为整数

    1.把指定位设置为1: value=value | 1«bit_index(表示指定位)

    2.将指定位清0:value = value & ~(1«bit_index(表示指定位))

    3.对指定位进行测试:test = value & 1«bit_index(指定位为1,test为非零之,否者为零值)

    上述三个操作可以用于对位图的操作,或对一些标志位的操作

  • 赋值:赋值=也是一个操作符,赋值语句为表达式,赋值表达式的值为=的左值,下面是合法的 :x=y=z+1

    当把整型值赋值给一个字符变量时,会被截断,分析下面的错误:

  char ch;
  while((ch=getchar())!=EOF)
  //该段代码在有符号字符集上工作时,只有遇到\377字节才会结束循环,在无符号字符集上工作时,循环无法结束
  //因为getchar()返回一个整型值,即输入字符的ASCLL码,赋值给一个ch字符变量会被截断,当和EOF(-1,文本文件的末尾标志,因为文本文件中的内容的ascll码都为正数)比较时,ch进行提升后和-1进行比较。
  • 复合赋值符:+=,-=,*=,/=,%=,«=,»=,&=,^=,|=
     a[f(x)] = a[f(x)] + 1 
     和 
     a[f(x)] += 1
     的区别:
       
     //第一个左边的f(x)是在计算下标,需要将f(x)求值两次
     //第二个只需要一次求值就够了
     //当f(x)的执行没有“副作用“的时候,两式含义相同
     //当f(x)的有“副作用“的时候,即参数相同,但第一次调用函数和第二次调用函数结果有区别,则两式具有区别
    
  • 单目操作符: !,++,-,+,&,*,sizeof,~,–,(类型)

    对于 ++ 和 – 的一个很重要的理解就是,前缀和后缀的增值(减值)操作符都复制了一份变量值的拷贝来作为表达式的值和周围操作符作用,这个拷贝(ex:a++)也就是相当于是一个值,而不是变量本身,所以下面这种使用方法是错误的:

    ++a = 10;//因为++a是一个值而不是变量,无法对它进行复制

    1.前缀:在增值(减值)之后复制作为表达式的值

    2.后缀:在增值(减值)之前复制作为表达式的值

  • 关系操作符:> ,>=,<,>=,!=,== 表达式的返回结果不是0就是1
  • 逻辑操作符:&&,|| 短路求值
  • 条件操作符:c 可减少if语句的冗余代码
  • 逗号操作符:ex1,ex2,ex3 自左向右逐个求值,而整个表达式的值就是最后那个表达式的值。
  • 下标引用、函数调用和结构成员:[],fun(arg),(->,s.a)

布尔值

零为假,任何非零值皆为真

左值和右值

左值就是出现在赋值操作符左边的东西,右值则是出现在右边的东西(左值意味着一个位置,右值意味着一个值)

左值应该指向一个存储值的具体位置,而不能是一个字面值:

  • 变量
  • 也可能是表达式:a[index],*p,s.a等要有一个表达式存值具体的位置
  • a+5这种表达式就不能作为左值,因为他的值并不是存放在一个具体的位置,表达式求值后保存在一个未知地址,第二次求值存放的地址有有所不同,所以不能作为左值

表达式求值

隐式类型转换

char a,b,c;
...
a = b+c;//其中b,c需要先提升为整型再进行计算,赋值给a时会被截断

这种由于操作符对操作数的限制会引发隐式类型转换。比如位操作符和移位操作符都要求操作数为整数。这就可能引起得不到我们想要的结果。

a = (~a^b«1)»1;这个表达式通过隐式类型转换后(因为求补和左移操作)和只用8位运算得到的结果就有所不同。

算术转换

当两个操作数不同时,往往需要先转换成同一操作数类型才能进行运算,转换的优先级(尽量补位转换)如下:

  1. long double
  2. double
  3. float
  4. unsigned long int
  5. long int
  6. unsigned int
  7. int

操作符的属性

表达式的求值顺序由三个因素决定:

  • 操作符的优先级:两个相邻操作符哪个先执行哪个后执行取决于它们的优先级
  • 操作符的结合性:确定是从左到右执行还是从右到左执行
  • 操作符是否控制执行的顺序:最后有四个操作符,可以控制执行顺序(&&,   ,?:,’,’)

优先级和求值顺序

两个相邻操作符的执行顺序由优先级决定,如果优先级相同,执行顺序由它们结合性决定。

操作符中的优先级只决定表达式各个组成部分在求值过程中如何进行聚组,而求值的顺序由编译器自由决定