第4章 语句

4.1 表达式语句

C并不存在专门的“赋值语句”,赋值就是一种操作,就像加法和减法一样,所以赋值就在表达式内进行。
你只要在表达式后面加上一个分号,就可以把表达式转变成语句。所以下面这两个表达式实际上是表达式语句,而不是赋值语句。

x = y + 3;
ch = getchar();

警告:理解这点非常重要,因为像下面这样的语句也是完全合法的:

y + 3;

4.2 代码块

代码块就是位于一对花括号之内的可选的声明和语句列表。

{
    declarations;
    statements;
}

4.3 goto语句

goto error;

error:
    printf("error!\n");

第5章 操作符和表达式

C提供了所有你希望编程语言应该拥有的操作符。它甚至提供了一些你意想不到的操作符。事实上,C被许多人诟病的一个缺点就是它品种繁多的操作符。C的这个特点使它很难精通。
另一方面。C的许多操作符具有其他语言的操作符无可抗衡的价值,这也是C适用于开发范围极广的应用程序的原因之一。

5.1 操作符

5.1.1 算数操作符

C提供了所有常用的算数操作符: + - * / %

5.1.2 移位操作符

移位操作只是简单地把一个值的位向左移动或者向右移动,在左移位中,值最左边的几位被丢弃,右边多出来的几个空位则有0补齐,右移同理。
左移位操作符为<<,右移位操作符为>>。移位的操作数必须都必须是整数类型。
下图是一个左移位的例子,它在一个8位的值上进行左移3位的操作,以二进制形式显示。

警告:
注意类似这种形式的移位:
a << -5;
左移-5位表示什么呢?是表示右移5位呢?还是根本不移位?在某台机器上,这个表达式实际上执行左移27位的操作。。。

5.1.3 位操作符

位操作符有这3种: &(与)、|(或)、^(异或)。
这些操作符以图表的形式总结如下:

位的操纵
可以使用移位操作符和位操作符来操纵一个整型值的单个位。
设定value为一整型值,这个例子把制定的位设置为1。

value = value | (1<<bit_number);

下面的例子将指定的位清零。

value = value & ~(1<<bit_number);

5.1.4 赋值操作符

赋值表达式的值就是左操作数的新值,如下面的语句所示:

a = x = y + 3;
赋值操作符的结合性(求值的顺序)是从右到左,所以这个表达式相当于:
a = (x = y + 3);
上面语句相当于

x= y+3;
a = x;

复合赋值符

还有一种复合赋值的形式:

+= -= *= /= %=
<<= >>= &= ^= |=

5.1.5 单目操作符

C具有一些单目操作符,就是只接受一个操作数的操作符。它们是

! ++ - & sizeof
~ -- + + (类型)

  • !操作符对它的操作数执行逻辑反操作;如果操作数为真,其结果为假,如果操作数为假,其结果为真。和关系操作符一样,这个操作符实际上产生一个整型结果,0或1。
  • ~操作符对整型类型的操作数进行求补操作,操作数中原先为1的位变为0,所有原先为0的位变为1,注意与!操作符的区别。
  • -操作符产生操作符的负值。
  • +操作符产生操作值的值;换句话说,它什么也不干。之所以提供这个操作符,是为了与-操作符组成对称的一对。
  • &操作符产生它的操作数的地址,被称为取地址符。
  • *是间接访问操作符,它与指针一起使用,用于访问指针所指向的值。
  • C语言中,sizeof是操作符,又是C关键字,不是函数,它用来计算它的操作数的类型长度,以字节为单位表示,它的结合性是从右到左。
    sizeof的操作数可以不加括号,也是合法的:

    sizeof a //合法
    sizeof操作符可以是表达式,判断表达式的长度并不需要对表达式进行求值,所以sizeof(a = b + 1)并没有向a赋任何值。

    int a=0;
    int b=1;
    cout << sizeof a << endl;
    cout << sizeof(a = b+1) << endl;
    cout << a << endl;    
  • (类型)操作符被称为强制类型转换(cast),它用于显式地把表达式的值转换为另外的类型。例如,为了获得整型变量a对应的浮点数值,你可以这样写:

    (float)a;

  • 最后我们讨论增值操作符++和减值操作符--。这两个操作符都有两个变型,分别为前缀形式和后缀形式。前缀形式的++操作符出现在操作符的前面,操作数的值被增加,而表达式的值就是操作数增加后的值。
    而后缀形式的++操作符出现在操作符的后面,操作数的值仍被增加,但是表达式的值为操作数增加前的值。demo例子如下:
    int a,b,c,d;

    a = b = 10;
    c = ++a;    //a增加到11,c得到的值为11
    d = b++;    //b增加到11,但d得到的值仍为10 

5.1.6 关系操作符

C的关系操作符如下:

= < <= != ==
注意关系操作符产生的结果都是一个整型值,而不是布尔值。如果两端的操作数符合操作符指定的关系,表达式的结果为1,否则为0。

5.1.7 逻辑操作符

逻辑操作符有&&和||。

expression1 && expression2
&&操作符的左操作数的值为假,那么右操作值便不再进行求值,因为整个表达式的值肯定是假的。||操作符也一样,当左操作数为真,则右操作数不再求值。

5.1.8 条件操作符

条件操作符是C语言中唯一一个三目操作符,接收三个操作数,用法如下:
expression1 ? expression2 : expression3
首先计算expression1的值,如果为真,那么整个表达式的值为expression2,expression3不再执行;如果为假,那么整个表达式的值为expression3,expression3也不会执行。

5.1.9 逗号操作符

逗号操作符将两个或多个表达式分隔开来,这些表达式自左向右逐个进行求值,整个逗号表达式的值就是最后那个表达式的值。例如:

if( b + 1,c / 2,d > 0 )
如果d的值大于0,那么整个表达式的值为真,否则为假。当然最好不要这么编写,前2个表达式的求值毫无意义,它们的值只是被简单地丢弃。

    a = get_value();
    cout_value(a);
    while(a > 0){ ; }

如果使用逗号操作符,可以将上面的程序改写为:

while(a = get_value(), count_value(a), a > 0) { ; }

5.1.10 下标引用、函数调用和结构成员

  • 下标引用操作符是一对方括号[],下标引用操作符是双目操作符,接收两个操作数,一个数组名和一个索引值。事实上,下标引用并不局限于数组名,也可以用于指针。
    C的下标值总是从零开始,并且不会对下标值进行有效性检查。除了优先级不同,下标引用和间接访问表达式是等价的,这里是它们的等价关系:

    array[下标]
    *( array + (下标) )
    下标引用实际上是以后面这张形式实现的。

  • 函数调用操作符接收一个或多个操作数。它的第一个操作数就是函数名,剩余的操作数就是传递给函数的参数。把函数调用以操作符的方式实现,实际意味着“表达式”可以代替“常量”作为函数名,
    事实也确实如此。在第7章详细讨论函数调用操作符

  • 如果s是个结构变量,那么s.a就访问s中名为a的成员。当是一个结构体指针,就要使用->操作符来访问成员。

5.2 布尔值

C并不具备显式的布尔类型,所以使用整数来代替。其规则是: 零是假,任何非零值皆为真。
C语言中常用#define来定义布尔值:

#define    FALSE    0
#define    TRUE     1

5.3 左值和右值

为了理解有些操作符存在的限制,你必须理解左值(L-value)和右值(R-value)之间的区别。左值就是那些能够出现在赋值符号左边的东西,右值就是那些可以出现在赋值符号右边的东西。
这里有个例子:

a = b + 25;
a是个左值,因为它标识了一个可以存储结果值的地点,b+25是个右值,因为它指定了一个值。
它们不可以互换
b + 25 = a; //error!
因为b+25并未标识一个特定的位置,所以这条赋值语句是非法的。

5.4 表达式求值

表达式的操作数在求值过程中可能需要转换为其他类型。

5.4.1 隐式类型转换

C的整型算数运算总是至少以缺省整数类型的精度来进行的。为了获得这个精度,表达式中的字符型和短整型操作数在使用之前被转换为普通整型。这种转换称为整型提升。

例如,在下面表达式的求值中,

char a,b,c;
a = b + c;
b和c的值被提升为普通整型,然后再执行加法运算。加法运算的结果将被截断,然后再存储在a中。

5.4.2 算数转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作无法进行。下面的层次体系称为寻常算数转换。(越上面的,排名越高)

long  double
double
float
unsigned  long  int
long  int
unsigned  int
int

如果某个操作数的类型在上面这个列表中排名较低,那么它将转换为另一个操作数的类型然后执行操作。

  • 当整型值转换为float型,也有可能损失精度。float型仅要求6位数字的精度。如果将一个超过6位数字的整型值赋给一个float变量时,其结果可能只是该整型值的近似值。

5.4.3 操作符的属性

复杂表达式的求值顺序是由3个因素决定的:操作符的优先级、操作符的结合性以及操作符是否控制执行的顺序。两个相邻的操作符哪个先执行取决于它们的优先级,

如果两者的优先级相同,那么决定顺序由它们的结合性决定。简单地说,结合性就是一串操作符是从左到右还是从右到左依次执行。最后,有4个操作符,它们可以对整个表达式的求值顺序进行控制。
每个操作符的所有属性都列在表5.1所示的优先级表中。
表5.1 操作符优先级

5.4.4 优先级和求值的顺序

两个相邻操作符的执行顺序由它们的优先级决定,如果它们的优先级相同,它们的执行顺序由它们的结合性决定。除次之外,编译器可以自由决定使用任何顺序对表达式进行求值,只要它不违背逗号、&&、||和?操作符所施加的限制。
这里有一个例子:

a + b * c
在上面的表达式中,由于的优先级较高,所以先执行bc,再执行a+(bc)。
下面是一个更为更为有趣的表达式:
a
b + c * d + e * f
如果仅由优先级决定这个表达式的求值顺序,那么所有3个乘法运算将在所有加法运算之前进行。事实上,这个顺序并不是必须的。实际上只要保证每个乘法运算在它相邻的加法运算之前执行即可。
例如,这个表达式可能会以下面的顺序执行:

    a * b
    c * d
    (a*b) + (c*d)
    e * f
    (a*b) + (c*d) + (e*f)    
注意第1个加法运算在最后一个乘法运算之前执行。如果这个表达式按以下的顺序执行,其结果是一样的(  + 的结合性为从左到右<加法运算情况>· ):
    a * b
    c * d
    e * f
    (a*b) + (c*d)
    (a*b) + (c*d) + (e*f)    
+ , - 在做一元运算符时具有右结合性,如:+1,-1(表示正负1)
+ , - 在做二元运算符时是从左向右结合,如1+2-1(表示加减)

第5章 编程练习

  1. 编写一个程序,从标准输入读取字符,并把它们写到标准输出中。所有非字母字符都完全按照它的输入形式输出,字母字符在输出前进行加密。
    加密方法很简单:每个字母被修改为在字母表上距其13个位置(前或后)的字母。例如,A被修改为N,B被修改为O,Z被修改为M,以此类推。注意大小写字母都应该被转换。
    char ch;
    while( (ch = getchar()) != '\0' )
    {
        if( (ch>='A' && ch<='Z') || (ch>='a' && ch<='z') )
        {
            if( (ch<='Z'&&(ch+13)>'Z')  || (ch+13)>'z' )  //因为'a'+13大于'Z',导致进入if分支
                ch -= 13;
            else
                ch += 13;
        }
        putchar(ch);
    }

2.请编写函数 unsigned int reverse_bits(unsigned int value);
这个函数的返回值是把value的二进制数从左到右变换一下后的值。32位机器上,25这个值包含下列各个位:00000000 000000000 00000000 00011001
函数的返回值应该是2,550,136,832,它的二进制是:10011000 00000000 000000000 00000000

    unsigned    int    reverse_bits(unsigned    int    value)
    {
        unsigned int answer = 0;
        unsigned int i;

        for(i = 1;i != 0;i << -1)
        {
            answer <<= 1;
            if(value &= 1)
            {
                answer |= 1;
            }
            value >>= 1;
        }

        return answer;
    }
  1. 请编写函数,使用数组存储一个整型数的二进制位。
    void    setBit(char    bitArr[],    unsigned    int    num)
    {
        unsigned    int    i;
        for(i=0;i<32;i++)
        {
            if(num&1)
                bitArr[32-i]    =    '1';
            else
                bitArr[32-i]    =    '0';    

            num >>= 1;
        }
    }

[C和指针] 4-语句、5-操作符和表达式的更多相关文章

  1. C和指针 (pointers on C)——第五章:操作符和表达式

    第五章 操作符和表达式 这一章假设没做过玩过单片机.汇编的话,读起来可能比較吃力,尤其是在移位运算符.位运算符应用上.另外多注意一下左值和右值的理解. 总结: 算术操作符.赋值操作符.关系操作符.条件 ...

  2. 《C和指针》读书笔记——第五章 操作符和表达式

    1.当/操作符的两个操作数都是整数时,它执行整除运算:其他都是执行浮点数除法. 2.逻辑移位:左边移入的位用0填充: 算数移位:左边移入的位用符号位填充: 3.位置1 :value |= 1<& ...

  3. LinQ实战学习笔记(三) 序列,查询操作符,查询表达式,表达式树

    序列 延迟查询执行 查询操作符 查询表达式 表达式树 (一) 序列 先上一段代码, 这段代码使用扩展方法实现下面的要求: 取进程列表,进行过滤(取大于10M的进程) 列表进行排序(按内存占用) 只保留 ...

  4. 《Visual C# 从入门到精通》第一章使用变量、操作符和表达式——读书笔记

    前言: 这个笔记是我个人总结,主要是熟练自己查看<Visual C# 从入门到精通>(第8版)这本书时,懵然起总结的想法,只是总结一些知识点,在工作项目会用得上,但是对毫无C#语言基础的, ...

  5. Java入门基础(变量、操作符与表达式)

    Java入门基础 1. 第一个程序 2.变量(命名.运算.整数/小数/字符串.布尔类型) 3.操作符与表达式(算术/逻辑/关系/赋值/自增/类型转换操作符) HelloWorld! public cl ...

  6. C语言-操作符与表达式

    C语言入门之操作符与表达式 前言 本篇文章主要包括各种操作符的介绍与表达式求值,欢迎各位小伙伴与我一起学习. 一.操作符 分类 算术操作符 移位操作符 位操作符 赋值操作符 单目运算符 关系操作符 逻 ...

  7. Java中的switch语句后面的控制表达式的数据类型

    Java中的switch语句后面的控制表达式的数据类型 1.byte 2.char 3.short 4.int 5.枚举类型 6.Java 7允许java.lang.String类型

  8. mybatis 的动态sql语句是基于OGNL表达式的。

    mybatis 的动态sql语句是基于OGNL表达式的.可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:1. if 语句 (简单的条件判断)2. c ...

  9. 无法将具有语句体的lambda表达式转换为表达式树

    很早就碰到了这个问题,当时也没有深入的研究,趁着空闲,遂把这个问题研究清楚. (一)普通案例 下面从一个普通的案例入手,下面准备两个List集合,都是放在内存里面的(需要模拟到远端执行的时候,我们是通 ...

  10. C++ 指针悬挂和赋值操作符的重载,拷贝构造函数实现

    指针悬挂: 问题:使用new申请的内存内存空间无法访问,也无法释放. 原因:直接对指向new申请的存储空间的指针变量进行赋值修改 后果:失去了原来的地址,原来的空间无法访问也无法释放,造成内存泄漏 还 ...

随机推荐

  1. Reactor Cooling(无源汇有上下界网络流)

    194. Reactor Cooling time limit per test: 0.5 sec. memory limit per test: 65536 KB input: standard o ...

  2. htmlspecialschars与htmlentities的区别

    根据php手册,htmlentities与htmlspecialchars功能几乎是一模一样.唯一的差别就是,对于无效的代码单元序列(通俗讲就是不认识的编码)是否进行编码.htmlentities会进 ...

  3. codevs3164 质因数分解

    题目描述 Description (多数据)给出t个数,求出它的质因子个数. 数据没坑,难度降低. 输入描述 Input Description 第一行 t 之后t行 数据 输出描述 Output D ...

  4. string数据类型操作【四】

    keys *    用于查找所有的key值 exists mykey     #判断该键是否存在,存在返回1,否则返回0. del mykey        删除键(存在就删除返回1,不存在返回为0) ...

  5. [bzoj3160]万径人踪灭_FFT_Manacher

    万径人踪灭 bzoj-3160 题目大意:给定一个ab串.求所有的子序列满足:位置和字符都关于某条对称轴对称而且不连续. 注释:$1\le n\le 10^5$. 想法: 看了大爷的题解,OrzOrz ...

  6. SpringBoot学习day01

    SpringBoot目的在于创建和启动新的基于Spring框架的项目.SpringBoot会选择最合适的Spring子项目和第三方开源库进行整合.大部分SpringBoot应用只需要非常少量的配置就可 ...

  7. MongoDB小结27 - 聚合管道【$project】

    我们有这样的数据 { "_id" : 1, title: "abcdef", isbn: "6969696969", author: { l ...

  8. Thread.run方法是同步方法

    Thread.run方法是同步方法, 线程: package com.stono.thread.page005; public class MyThread extends Thread { @Ove ...

  9. C#中Stack&lt;T&gt;类的使用及部分成员函数的源代码分析

    Stack<T>类 Stack<T> 作为数组来实现. Stack<T> 的容量是 Stack<T> 能够包括的元素数. 当向 Stack<T&g ...

  10. Android入门级编译错误汇总

    1  描写叙述:  项目常常须要引用别人的libraryproject,在选项中add进来后,点击应用或者确定.关闭页面. 回到代码中却发现无法链接,又一次打开properties查看,发现导入的pr ...