精华内容
下载资源
问答
  • 第五章、选择结构一、关系运算符和关系表达式考点一、关系运算符和关系表达式二、逻辑运算符和逻辑表达式考点一、逻辑运算符和逻辑表达式三、if语句和用if语句构成的选择结构考点一、if语句的几种形式考点二、if语句...

    一、关系运算符和关系表达式

    考点一、关系运算符和关系表达式

    1、C语言提供了6种关系运算符,如下表:

    关系运算符名称
    <小于
    <=小于或等于
    >大于或等于
    ==等于
    !=不等于

    (1)结合性:自左向右
    (2)优先级:前4种优先级相同,后两种优先级相同,且前四种优先级大于后两种。关系运算符的优先级低于算术运算符,高于赋值运算符。
    (3)由关系运算符连成的表达式称为关系表达式。
    (4)关系运算符的结果是一个整数值——“0或者1”。

    二、逻辑运算符和逻辑表达式

    考点一、逻辑运算符和逻辑表达式

    C语言提供了三种逻辑运算符:
    (1)&&:逻辑与
    (2)||:逻辑或
    (3)!:逻辑非
    逻辑与和逻辑或是双目运算符要有两个操作数,逻辑非是单目运算符要求必须出现在运算对象的左边。

    1、结合性:自左至右

    2、优先级:“!”级别最高,然后是“&&”,最低的是“||”。
    加上之前的优先级来一起比较:“!”>算术运算符>关系运算符>“&&”>“||”>赋值运算符。

    3、逻辑表达式由逻辑运算符和运算对象组成,其中参与逻辑运算的对象可以是一个具体的值,还可以是C语言中任何合法的表达式。逻辑表达式的运算结果为1(真)或者为0(假)。

    三、if语句和用if语句构成的选择结构

    考点一、if语句的几种形式

    1、if(表达式)语句。例如:

    if(a>b)
    	printf("你好!");
    

    2、if(表达式)语句一 …else 语句二
    例如:

    if(a>b)
    	printf("你好!");
    else
    	printf("不好!");
    

    3、if(表达式1)语句一
    else if(表达式2)语句二

    else(表达式m)语句m
    else 语句n
    例如:

    if(-30<a<=10)
    	printf("很冷的");
    else if(10<a<=25)
    	printf("舒适的温度");
    else if(25<a<=35)
    	printf("炎热的");
    else 
    	printf("难以生存的");
    

    注意:else不能独立成为一条语句,只是if的一部分,不允许单独出现在程序中。

    考点二、if语句的嵌套

    在if语句中又包含一个或多个if语句结构,称为if语句的嵌套。
    例如:

    if(a>0){
    	if(a>10)
    		printf("nihao");
    	else
    		printf("buhao");
    }
    else
    	printf("feichangbuhao");
    

    考点三、由条件运算符构成的选择结构

    条件运算符(?:)
    下面语句可以用条件运算符代替:min=(x<y)?x:y 。

    if(x<y)
    	min=x;
    else
    	min=y;
    

    优先级:条件运算符高于赋值运算符,但低于逻辑运算符,关系运算符和算术运算符。

    四、switch语句

    考点一、switch语句

    switch语句是c语言提供的多分支选择语句,用来实现多分支选择结构,它的一般形式如下:
    switch(表达式){
    case 常量表达式1:语句1
    case 常量表达式2:语句2
    case 常量表达式3:语句3

    case 常量表达式n:语句n
    default:语句n+1
    }
    例如:

    switch(4){
    	case 0 : printf("0"); break;
    	case 1 : printf("1"); break;
    	case 2 : printf("2"); break;
    	case 3 : printf("3"); break;
    	default :  printf("-1"); break;
    }
    

    运行结果为-1。

    展开全文
  • 第4章 表达式和赋值

    千次阅读 2005-03-16 10:13:00
    第4章 表达式和赋值本章描述如何在C语言中构造表达式和对其赋值。常量、标识符、字符串函数调用都是在表达式中操作的操作数。C语言具有所有常用的语言运算符。本章讨论这些运算符以及对C或Microsoft是唯一的运算符...

    第4章 表达式和赋值

    本章描述如何在C语言中构造表达式和对其赋值。常量、标识符、字符串和函数调用都是在表达式中操作的操作数。C语言具有所有常用的语言运算符。本章讨论这些运算符以及对C或Microsoft是唯一的运算符。讨论的主题包括:

    *操作数和表达式

    *运算符

    *类型转换


    操作数和表达式

    一个“操作数”是一个运算符作用的一个实体。一个“表达式”是执行这些动作的任何组合的运算符和操作数序列:

    *计算一个值

    *设计一个对象或函数

    *产生副作用在C中的操作数包括常量、标识符、字符串、函数调用、下标表达式、成员选择表达式和通过运算符组合操作数或在圆括号中包括操作数而形成的复杂表达式。这些操作数的语法在下一节“基本表达式”中给出。

    基本表达式

    表达式中的操作数称为“基本表达式”。语法基本表达式:

    标识符

    常量

    字符串文字

    (表达式)表达式:

    赋值表达式

    表达式,

    赋值表达式

    基本表达式中的标识符

    标识符有整数、float、enum、struct、union、数组、指针或函数类型。一个标识符是一个基本表达式,它被说明用于设计一个对象(这种情况下它是一个l值)或一个函数(这种情况下它是一个函数指示符)。有关l值的定义参见“L值和R值表达式”。

    由一个数组标识符表示的指针值不是变量,因此,一个数组标识符不能形成一个赋值运算的左边操作数。因此不是一个可修改的l值。

    一个作为函数说明的标识符表示一个其值是该函数地址的指针。该指针地址指向一个返回指定类型值的函数。因此,函数标识符也不能是赋值操作中的l值。有关更多信息,参见第1章“C的基本元素”中的“标识符”。

    基本表达式中的常量

    一个常量操作数有它给定常量值的值和类型。一个字符常量有int类型。一个整数常量有int、long、unsigned int或unsigned long类型,这取决于该整数的尺寸和指定值的方式。有关更多信息参见第1章“C的基本元素”中的“常量”。

    基本表达式中的字符串文字

    一个“字符串文字”是一个字符、宽字符或用双引号括起的相邻字符序列。由于它们不是变量,因此字符串文字或任何它们的元素都不能作为一个赋值操作的左边操作数。一个字符串文字的类型是char的数组(或宽字符串文字的wchar_t数组)。在表达式中数组都转换成指针。有关字符串的更多信息,参见第1章“C的基本元素”的“字符串文字”。

    圆括号中的表达式

    你可以在圆括号中括起任何操作数而不改变该括起的表达式的类型或值。例如,在表达式中:

    (10+5)/5

    圆括号中的10+5意味着首先对10+5求值,它变成除法(/)运算符的左操作数。(10+5)/5的结果是3。没有圆括号的10+5/5的结果是11。虽然圆括号影响一个表达式中组合操作数的方式,它们不能保证在所有情况下的一个特殊求值次序。例如,如下表达式的圆括号和从左到右的组合都不能保证i在每个子表达式中是什么样的值。

    (i++ +1)*(2+i)

    编译器自由以任何次序计算乘法两边的值。如果i的初值为0,整个表达式以如下两个语句求值:

    (0+1+1)*(2+1)

    (0+1+1)*(2+0)

    副作用导致的异常在本章后面“副作用”中讨论。

    值和R值表达式

    指的是存储器位置的表达式称为“l值”表达式,一个l值表示一个存储区域的“定位器”值或“左边”值,它隐含出现在等号(=)的左边。l值经常是标识符。

    指的是右修改的位置的表达式称为“可修改的l值”。一个可修改的l值不能有一个数组类型,一个不完整类型或具有const属性的类型,对于可以是可修改l值的结构和联合,它们必须没有const属性的成员。标识符的名称指示一个存储位置,而该变量的值是存储在该位置的值。

    如果一个标识符指向一个存储器位置,其类型是算术、结构、联合或指针,则该标识符是一个可修改的l值。例如,如果ptr是一个存储区域的指针,那么*ptr是一个可修改的l值,指示该存储区域由ptr指向。

    如下任何C表达式可以是l值表达式:

    *一个整数、浮点、指针、结构或联合类型的标识符。

    *一个不对数组求值的下标([])表达式。

    *一个成员选择表达式(->或.)

    *一个不指向一个数组的单目间接访问(*)表达式。

    *一个圆括号中的l值表达式。

    *一个const对象(一个不能修改的l值)。

    本语“r值”有时用来描述一个表达式的值以区别于一个l值。所有l值都是r值,但不是所有r值都是l值。

    Microsoft特殊处

    C包括ANSI C标准的一个扩充是允许l值造型用作l值,以及该对象的尺寸不会通过该造型而伸长(有关更多信息,参见本章末尾的“类型造型转换”)。如下例子说明了这种特征:

    char *p;

    short i;

    long l;

    (long *) p=&l; /*合法造型*/

    (long) i=l; /*非法造型*/

    C缺省能使Microsoft扩充。使用/Za编译器选项禁止这种扩充。

    Microsoft特殊处结束

    量表达式

    一个常量表达式在编译时求值,而不是在运行时求值,可以使用在一个常量可以被使用的任何地方。常量表达式必须求值为一个在该类型可表示值的范围内的常量。一个常量表达式的操作数可以是整数常量、字符常量、浮点常量、枚举常量、类型造型、sizeof表达式和其它常量表达式。

    语法

    常量表达式:

    条件表达式

    条件表达式:

    逻辑OR表达式

    逻辑OR表达式 ? 表达式 : 条件表达式表达式:

    赋值表达式 表达式,

    赋值表达式赋值表达式:

    条件表达式

    单目表达式 赋值运算符 赋值表达式

    赋值运算符:如下之一:

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

    结构说明符、枚举器、直接说明符、直接抽象说明符和标号语句的非终结符包含常量表达式非终结符。

    一个整数常量表达式必须用于指定一个结构位域成员的尺寸、一个枚举常量的值、一个数组的尺寸或一个case常量的值。

    用在预处理器命令中的常量表达式满足一些附加限制。结果它们是众所周知的“限制的常量表达式”。一个限制的常量表达式不能包含sizeof表达式、枚举常量、到任何类型的类型造型或浮点类型常量。但它可以包含定义的特定常量表达式(标识符)。

    表达式求值

    涉及赋值、单目增1、单目减1或调用一个函数的表达式具有伴随它们求值的结果(副作用),当到达一个“顺序点”时,该顺序点之前的任何事情包括任何副作用都保证在开始计算该顺序点之后的任何事情之前进行求值。

    通过对一个表达式求值导致改变“副作用”。副作用出现在一个变量的值被一个表达式求值所改变的任何时候。所有赋值操作都有副作用。如果函数调用通过直接赋值或使用一个指针间接赋值来改变一个外部的可见项的值,则该函数调用也有副作用。

    副作用

    一个表达式的求值次序是由指定的实现确定的,除了当该语言保证一个特殊的求值次序外(正如本章后面的“优先级和求值次序”中描述的)。例如,在如下函数调用中出现副作用:

    add(i+1,i=j+2);

    myproc(getc(),getc());

    一个函数调用的参量可以以任何次序求值。表达式i+1可以在i=j+2之前求值,或者i=j+2可以在i+1之前求值,每种情况下的结果是不同的,同样,不可能保证哪个字符实际传送给myproc。由于单目增1和减1操作涉及到赋值,如下例子中的操作可能导致副作用:

    x[i]=i++;

    在这个例子中,被修改的x的值是不可预料的。下标的值可以是i的新值或旧值。其结果可能在不同编译器下或不同优化层都是不同的。由于C不确定副作用的求值次序,上面讨论的求值方法都是正确的,也可以实现。

    为了确保你的代码是可移植和清楚的,避免使用其副作用依赖于特殊求值次序的语句。

    顺序点

    连贯的“顺序点”之间,一个对象的值仅能被一个表达式改变一次。C语言定义如下顺序点:

    *逻辑AND运算符(&&)的左操作数。逻辑AND运算符的左操作数在继续之前完成求值和完成所有的副作用。如果该左操作数求值为假(0),不对其它操作数求值。

    *逻辑OR运算符(||)的左操作数。逻辑OR运算符的左操作数在继续之前完成求值和完成所有的副作用。如果该左操作数求值为真(非0),不对其它操作数求值。

    *逗号运算符的左操作数。逗号运算符的左操作数在继续之前完成求值和完成所有的副作用。逗号运算符的两个操作数总是求值。注意在一个函数调用中的逗号运算符不保证求值的次序。l函数调用运算符。在进入一个函数之前对该函数的所有参量求值和完成所有副作用,在参量中不指定求值的次序。

    *条件运算符的第一个操作数。在继续之前完成该条件运算符的第一个操作数的求值并完成所有的副作用。

    *一个完整初始化表达式结束(也就是,一个不是另一个表达式部分的表达式,例如,在一个说明语句中一个初始化的结果)。

    *一个表达式语句中的表达式。表达式语句由一个任选表达式后跟一个分号(;)。该表达式为了副作用而求值,并紧跟这个求值求一个顺序点。

    *在一个选择(if或switch)语句中的控制表达式。在依赖于该选择的代码执行之前完成该表达式的求值,并完成所有的副作用。

    *一个while或do语句的控制表达式。在while或do循环的下一次迭代中的任何语句执行之前完成该表达式的求值,并完成所有的副作用。

    *一个for语句的所有三个表达式。在for循环的下一次迭代中的任何语句执行之前完成该表达式的求值,并完成所有的副作用。

    *一个return语句中的表达式。在控制返回到调用函数之前完成该表达式的求值,并完成所有的副作用。


    运算符

    有三种类型的运算符。单目表达式由一个单目运算符加到一个操作数组成,或者sizeof关键字跟一个表达式,该表达式可以是一个变量的名称或一个造型表达式。如果该表达式是一个造型表达式,它必须包括在圆括号中。一个双目表达式由两个操作数加上连接的双目运算符组成。一个三目表达式由三个操作数加上连接的条件表达式运算符组成。C中包括如下单目运算符:

    符号 名称

    - ~ ! 取反和取非运算符

    * & 间接访问和取地址运算符

    sizeof 尺寸运算符

    + 单目加法运算符

    ++ -- 单目增1和减1运算符

    双目运算符从左向右结合。C提供如下双目运算符:

    符号 名称

    * / % 乘法运算符

    + - 加法运算符

    << >>移 位运算符

    <> <= >= == != 关系运算符

    & | ^ 按位运算符

    && || 逻辑运算符

    顺序求值运算符条件表达式运算符比双目表达式具有较低的优先级,且在右结合上不同于它们。带运算符的表达式也包括赋值表达式,它使用单目或双目赋值运算符。单目赋值运算符是增1(++)和减1(--)运算符;双目赋值运算符是简单赋值运算符(=)和复合赋值运算符。每个复合赋值运算符是另外的双目运算符与简单赋值运算符的组合。

    优先级和求值次序

    C运算符的优先级和结合律影响表达式中操作数的组合和求值。一个运算符的优先级仅在出现更高或更低优先级的另外运算符时才有意义。具有高优先级运算符的表达式首先求值。优先级也可以通过词“绑定”(binding)来描述。具有更高优先级的运算符被说成具有更紧密的联结。

    表4.1总结了C运算符的优先级和结合律(运算符求值的次序),以优先级从高到低列出。而几个运算符只能一起出现,它们具有相等的优先级并根据它们的结合律进行求值。该表中的运算符在以“后缀运算符”开头的小节中。本节的余下部分给出了优先级和结合律一般的信息。

    表4.1 C 运算符的优先级和结合律

    符号操作类型结合律
    [] () . -> 后缀++和后缀--表达式从左向右
    前缀++和前缀-- sizeof & + _~ !单目从右向左
    类型造型单目从右向左
    * / %乘法从左向右
    + -加法从左向右
    << >>位移从左向右
    <> <= >=关系从左向右
    == !=相等从左向右
    &按位AND从左向右
    ^按位异或从左向右
    |按位OR从左向右&&逻辑AND从左向右||逻辑OR从左向右?:条件表达式从右向左
    = *= /= %=+= -= <<= >>=&= ^= |=简单和复合赋值从右向左
    ,顺序求值从左向右

    1. 运算符以优先级递降次序列出。如果同一行或同一组中有几个运算符出现,它们具有相同的优先级。

    2.所有简单和复合赋值运算符具有相同的优先级。

    一个表达式可以包含几个具有相同优先级的运算符。当在一个表达式的同一层中出现几个这样的运算符时,根据这些运算符的结合律从右向左或从左向右进行求值。求值的方向不影响这样的表达式的结果,该表达式在同层中可以包含多个乘法(*)、加法(+)或双目按位(&|^)运算符。优先级的次序不是由语言定义的。编译器如果能够保证一致的结果,可以以任意次序对表达式求值。

    只有顺序求值(,)、逻辑AND(&&)、逻辑OR(||)、条件表达式(?:)和函数调用运算符设立顺序点,因此保证这些运算符的特殊求值次序。函数调用运算符是该函数标识符之后的一组圆括号。顺序求值运算符(,)保证从左向右对它的运算符求值(注意,在一个函数调用中逗号运算符号不同于顺序求值运算符,不提供任何这样的保证)。有关更多信息,参见本章前面的“顺序点”。

    逻辑运算符也保证它们的运算符从左向右求值,但它们求值需要确定该表达式结果的最少的运算符个数。这称之为“短路”求值。因些,该表达式的一些运算符可能不求值,例如,在表达式中:

    x && y++

    只有x为真(非0),第二个运算符y++才被求值,因此,如果x为假(0),则y不增1。

    例子

    下表说明一个编译器如何自动绑定几个基本表达式。

    表达式 自动联结

    a&b||c (a&b)||c

    a=b||c a=(b||c)

    q&&r||s-- (q&&r)||s--

    在第一个表达式中,按位AND运算符的优先级比逻辑OR运算符(||)的优先级高,因此,a&b形成了逻辑OR运算的第一个操作数。

    在第二个表达式中,逻辑OR运算符(||)比简单赋值运算符(=)有更高的优先级,因此b||c在赋值中作为右边运算符进行组合。注意赋给a的值是0或1。

    第三个表达式说明一个正确地组成的表达式可能产生一个不可预料的结果。逻辑AND运算符(&&)比逻辑OR运算符(||)的优先级高,因此,q&&r结合成一个操作数。由于逻辑运算符保证从左向右求值,q&&r在s--之前被求值。如果q&&r求值为非0值,s --不再求值,s不减1。

    如果s不减1,可能导致程序出问题,s--应在表达式中作为第一个操作数出现,或者s在一个分开的操作中减1。

    下面的表达式是非法的,在编译时产生一个诊断消息:

    非法表达式 缺省组合

    p==0?p+=1:p+=2 (p==0?p+=1:p)+=2

    在这个表达式中,相等运算符(==)有最高的优先级,因此p==0组合成一个操作数。条件表达式运算符(?:)有次高的优先级,它的第一个操作数是p==0,第二个操作数是p+=1。但该条件表达式运算符的最后操作数被认为是p而不是p+=2,这是由于p与该条件表达式运算符的结合比与该复合赋值运算符的绑定更紧密。因为+=2没有左边运算符,出现一个语法错误。

    你可以使用圆括号防止这种错误出现并产生更可读的代码。例如,你可以使用如下圆括号以改正前面的例子并使之更清楚

    (p==0)?(p+=1):(p+=2)

    常用的算术转换

    大多数C运算符执行类型转换把一个表达式的操作数转换成共同的类型或扩充短的值为机器操作中使用的整数尺寸。C运算符执行的转换依赖于特定运算符和操作数的类型或操作数。但很多运算符在整数和浮点类型的操作数上执行相似的转换,这些转换就是众所周知的“算术转换”。一个操作数值转换成一个兼容的类型使其值不发生改变。

    下面总结的算术转换称为“常用的算术转换”。这些步骤仅应用于算术类型的双目运算符以及两个操作数不是相同类型的情况。其目的是产生一个公共的类型,它也是最后结果的类型。为了确定实际发生的转换,编译器把如下算法应用于表达式中的双目操作。下面步骤的次序不是按优先级给出的:

    1. 如果操作数之一类型为long double,则其它操作数转换成long double类型。

    2. 如果上述条件不满足,且操作数之一类型为double,则其它操作数转换成double类型。

    3.如果上述两个条件不满足,且操作数之一为float类型,则其它操作数转换成float类型。

    4.如果上述三个条件都不满足(没有操作数为浮点类型),那么在操作数上执行如下整数转换:

    * 如果操作数之一为unsigned long类型,则其它操作数转换成unsigned long类型。

    * 如果上述条件不满足,且操作数之一为long类型,其它为unsignedint类型,则操作数都转换成unsigned long类型。

    * 如果上述两个条件都不满足,且操作数之一为long类型,则其它操作数转换成long类型。

    * 如果上述三个条件都不满足,且操作数之一为unsigned int,则其它操作数转换成unsigned int 类型。

    * 如果上述条件都不满足,则操作数都转换成int类型。

    以下例子说明了这些转换规则:

    float fVal;

    double dVal;

    inti Val;

    unsigned long ulVal;

    dVal = iVal * ulVal; /*iVal使用步骤4转换为unsigned long

    *乘法的结果转换成double */

    dVal = ulVal + fVal; /*ulVal使用步骤3转换为float

    *加法的结果转换成double */

    后缀运算符

    后缀运算符在表达式值中具有最高的优先级(最紧密的绑定)。

    语法

    后缀表达式:

    基本表达式

    后缀表达式[表达式]

    后缀表达式[参量表达式表opt]

    后缀表达式.标识符

    后缀表达式->标识符

    后缀表达式++

    后缀表达式--

    在这个优先级层中的运算符有数组下标、函数调用、结构和联合成员以及后缀增1和后缀减1运算符。

    一维数组

    一个后缀表达式后跟一个方括号([])中的表达式是一个数组对象的一个元素的下标表示。一个下标表达式表示这样地址的值,其地址是该后缀表达式加上表达式中指定值的位置,表示为:

    后缀表达式[表达式]

    通常,由后缀表达式表示的值是一个指针值,例如一个数组标识符和一个表达式是一个整数值。但所有语法上需要的是一个为指针类型的表达式,另一个为整数类型。因此,整数值可以在后缀表达式位置,指针值可以在方括号的表达式中或“下标”位置。例如,如下代码是合法的:

    int sum *ptr,a[10];
    int main
    {
    ptr=a;
    sum=4[ptr];
    }

    下标表达式通常用于指向数组元素,但你可以将下标应用于任何指针。无论值的次序,表达式必须包括在方括号([])中。

    下标表达式通过把整数值加上指针值,然后在结果上应用间接访问运算符(*)求值(有关间接访问运算符的讨论,参见本章后面的“间接访问和取地址运算符”)。实际上,如下四个表达式是相等的,假设a是一个指针,b是一个整数:

    a[b]

    *(a+b)

    *(b+a)

    b[a]

    根据加法运算符的转换规则(在“加法运算符”中给出),该整数值通过将它乘以指针地址类型的长度来将其转换成一个地址偏移量。

    假如,假设标识符line指向一个int类型的数组,如下过程用于计算下标表达式ling[i]的值:

    1. 整数值I被一个int项长度确定的字节个数相乘。i的转换值表示i个int位置。

    2. 这个转换的值加上最初指针值(line)产生一个地址,它是距离line偏移i个int的位置。

    3. 将间接访问运算符应用于该新地址。其结果是该位置数组元素的值(直观地为line[i])。

    下标表达式line[0]表示line的第一个元素的值,由于它偏移line所表示的地址为0。类似地,一个表达式例如line[5]指的是偏移line为5个位置的元素或该数组的第6个元素。

    多维数组

    一个下标表达式也可以有多个下标,如下:

    expression1[expression2][expression3]...

    下标表达式从左向右结合。最左下标表达式expression1[expression2]首先求值。其地址加上expression1和expression2构成一个指针表达式;expression3加上这个指针表达式构成一个新的指针表达式,如此等等直到最后下标表达式已加上了。在最后下标表达式求值之后应用间接访问运算符(*),除非最后的指针值是一个数组类型的地址(参见下面的例子)。

    具有多个下标的表达式指的是“多维数组”。一个多维数组是一个其元素是数组的数组。例如,一个三维数组的第一个元素是一个两维数组。

    例子

    对于如下例子中,一个命名为prop的数组用三个元素说明,每个是一个int类型的4×6数组。

    int prop[3][4][6];

    int i,*ip,(*ipp)[6];

    如下引用prop数组:

    i=prop[0][0][1];

    上面例子说明了如何引用prop的第2个int元素。数组以行存储,因此最后的下标最快地变化;表达式prop[0][0][2]指的是该数组的下一个(第3个)元素,等等。

    i=prop[2][1][3];

    这个语句更加复杂地引用prop的单个元素。该表达式求值如下:

    1. 第1个下标为2,由4×6的int数组的尺寸相乘,再加上prop的指针值,其结果指向prop的第3个4×6数组。

    2. 第2个下标为1,由6个元素int数组的尺寸相乘,再加上prop[5]表示的地址。

    3. 该6元素数组的每一个元素是一个int值,因此,最后下标3,在加到prop[2][1]之前由一个int的尺寸相乘。结果指针是该6元素数组的第4个元素。

    4. 将间接访问运算符应用到该指针值。其结果是该地址处的int元素。如下两个例子说明了不应用间接访问运算符的情况:

    ip=prop[2][1];

    ipp=prop[2];

    在上述语句的第一个语句中,表达式prop[2][1]是该三维数组prop的有效引用。它指的是第一个6元素数组(上述说明的)。由于该指针值是一个数组的地址,不能应用间接访问运算符。

    类似地,在第二个语句ipp=prop[2]中,表达式prop[2]的结果是一个二维数组的地址的指针值。

    函数调用

    一个“函数调用”是一个包含被调用函数的名称或函数指针值或者传送给该函数的任选参量的表达式。

    语法

    后缀表达式:

    后缀表达式(参量表达式表opt)

    参量表达式表:

    赋值表达式

    参量表达式表,赋值表达式

    后缀表达式必须计算一个函数地址(例如,一个函数标识符或一个函数指针的值),参量表达式表是一个其值(“参量”)传送给该函数的表达式表(用逗号分隔表达式)。参量表达式表参量可以为空。

    一个函数调用表达式具有该函数返回的值和类型,一个函数不能返回一个数组类型的对象,如果该函数的返回类型为void(也就是,该函数说明为

    没有返回值),则该函数调用表达式也有void类型(有关更多信息,参见第6章“函数”中的“函数调用”)。

    结构和联合成员

    一个“成员选择表达式”指的是结构和联合的成员,这样的一个表达式具有选择的成员的值和类型。

    语法

    后缀表达式.标识符

    后缀表达式->标识符

    这个表描述了成员选择表达式的两种格式:

    1. 在第一种格式中,后缀表达式表示一个struct或union类型的值,标识符命名该指定结构或联合的一个成员。该操作的值就是该标识符的值,如果后缀表达式是一个l值,则该操作的值也是一个l值。有关更多信息,参见本章前面的“L值和R值表达式”。

    2. 在第二种格式中,后缀表达式表示一个结构或联合的一个指针,标识符命名该指定结构或数组的一个成员。其值是该标识符的值,且是一个l值。

    成员选择表达式的两种格式具有类似的作用。事实上,涉及成员选择运算符(->)的表达式是这样的表达式的速记版本,这个表达式使用句点且该句点之前的表达式由作用于一个指针值的间接访问运算符组成。因此:表达式->标识符

    等价于:

    (*表达式).标识符

    这里表达式是一个指针值。

    例子

    如下例子指的是这种结构说明,有关在这些例子中使用间接访问运算符(*)的信息,参见本章后面的“间接访问和取地址运算符”。

    struct pair
    { int a;
    int b;
    struct pair *sp;
    } item,list[10];

    item的一个成员选择表达式如下:

    item.sp=&item在上述例子中,将item结构的地址赋给该结构的sp成员。这样item包含自身的一个指针。

    (item.sp)->a = 24;

    在这个例子中,指针表达式item.sp与成员选择运算符(->)一起使用把一个值赋给a:

    list[8].b = 12;这个语句说明如何从一个结构数组选择单个结构成员。

    后缀增1和减1运算符

    后缀增1和减1运算符的操作数是可修改的l值的标量类型。

    语法

    后缀表达式:

    后缀表达式++

    后缀表达式--

    后缀增1或减1操作的结果是该操作数的值。在获得结果后,该操作数增1(或减1)。如下代码说明该后缀增1运算符。

    if (var++>0)

    *p++=*q++;

    本例中,变量var是与0进行比较,如果var在增1之间是正数,则执行下一个语句。首先,由q所指对象的值赋给由q所指的对象,然后,q和p都增1。

    单目运算符

    单目运算符出现它的操作数之前并从右向左结合。

    语法

    单目表达式:

    后缀表达式

    ++单目表达式

    --单目表达式

    单目运算符 造型表达式

    sizeof 单目表达式

    sizeof(类型名称)单目运算符:如下之一

    & * + - ~ !

    前缀增1和减1运算符

    当增1和减1运算符出现在操作数之间时,单目运算符(++和--)被称为“前缀”增1和减1运算符。

    后缀增1和减1的优先级比前缀增1和减1运算符的优先级高。操作数必须是整数、浮点或指针类型以及必须是一个可修改的l值表达式(一个没有const属性的表达式)。其结果是一个l值。

    当运算符出现在操作数之前时,该操作被增1或减1,其新值是该表达式的结果。

    一个整数或浮点类型的操作数增大或减小整数值1,其结果的类型与操作数类型相同。

    一个指针类型的操作数增大或减小它地址所指对象的尺寸,一个增1的指针指向下一个对象,一个减1的指针指向前一个对象。

    例子

    这个例子说明单目前缀减1运算符:

    if (line [--i]!=′/n′)

    return;

    在这个例子中,变量i在作为line的下标之前减1。

    间接访问和取地址运算符

    间接访问运算符(*)通过一个指针间接访问一个值。该操作值必须是一个值。该操作数必须是一个指针值。其操作结果是该操作数所指的值,也就是该操作数所指地址处的值。其结果类型是该操作数所指地址的类型。

    如果操作数指向一个函数,其结果是一个函数指示符,如果它指向一个存储位置,其结果是一个l值指示该存储位置。

    如果该指针值是无效的,其结果是不确定的。如下表包括无效指针值的一些最普遍的条件:

    * 该指针是一个空指针。

    * 该指针指出一个在引用时不可见的局部项的地址。

    * 该指针指出一个不适合赋给该对象所指类型的地址。

    * 该指针指出一个执行程序不能使用的地址。

    取地址运算符(&)给出它的操作数的地址。取地址运算符的操作数可以是一个函数指示符或一个l值,该l值指示一个不是位域的对象,且没有用register存储类指示符说明。

    取地址运算符的结果是该操作数的指针,该指针所指地址的类型是操作数的类型。

    取地址运算符只能应用于在文件范围层说明的基本类型、结构或联合类型或者下标数组的引用。在这些表达式中,在该地址表达式中可以加上

    或减去一个不包括取地址运算符的常量表达式。

    例子

    下面的例子使用三个说明:

    int *pa,x;

    int a[20];

    double d;

    下面的语句使用取地址运算符:

    pa=&a[5];

    该取地址运算符(&)取数组a的第6个元素的地址,把结果存储在指针变量pa中。

    x=*pa;

    在这个例子中使用间接访问运算符(&)访问存储在pa地址处的int值,把该值赋给整数变量x。

    if (x == *&x)

    printf("True/n");

    这个例子打印单词True,从而证实把间接访问运算符应用于x的地址的结果与x是相同的。

    int roundup(void); /*函数说明 */

    int *proundup=roundup;

    int *pround=&roundup;

    一旦说明了函数roundup,该roundup的两个指针也说明和初始化了。第一个指针proundup仅使用该函数的名称初始化,而第二个指针pround在初始化中使用取地址运算符。这两种初始化是相同的。

    单目算术运算符

    在下表中讨论C单目加、算术取反、按位取反和逻辑非运算符。

    运算符 说明

    + 单目加运算符位于圆括号中一个表达式的前面强制组合括起的操作。它用在涉及多个结合或交换双目运算符的表达式中。该操作数必须是算术类型。其结果是操作数的值。一个整数操作数要进行整数提升,结果的类型是提升的操作数的类型。

    - 算术取反运算符产生该操作数的取反值(2的补码)。该操作数必须是整数或浮点值。这个运算符执行常用的算术转换。

    ~ 按位取反(或按位NOT)运算符产生其操作数的按位取反。该操作数必须是整数类型。这个运算符执行常用的算术转换,其结果具有转换后的操作数的类型

    ! 逻辑非(逻辑NOT)运算符在操作数为真(非0)时产生值0,在操作数为假(0)时产生值1。其结果具有int类型。该操作数必须是一个整数、浮点或指针值。

    指针上的单目算术操作是非法的。

    例子

    如下例子说明单目算术运算符:

    short x=987;

    x=-x

    ;在上述例子中,x的新值是987的算术取反即-987。

    unsigned short y=0xAAAA;

    y=~y;在这个例子中,y的新值是无符号值0xAAAA的按位取反即0x5555。

    if (!(x<y))

    如果x大于或等于y,该表达式的结果是1(真)。如果x小于y,其结果是0(假)。

    sizeof运算符

    sizeof运算符以字节为单位给出存储该操作数的类型的对象所需要的存储数目。这个运算符允许你在程序中避免指定依赖于机器的数据尺寸。

    语法

    sizeof单目表达式

    sizeof(类型名称)

    该操作数是一个为单目表达式或类型造型表达式(也就是,一个类型指示符放在圆括号中)的标识符。该单目表达式不能表示一个位域对象、一个不完整类型或一个函数指示符。其结果是一个无符号整数参量。标准头文件STDDEF.H定义这个类型为size_t。

    当你把该sizeof运算符应用到一个数组标识符时,其结果是整个数组的尺寸而不是该数组标识符表示的指针的尺寸。

    当你把该sizeof运算符应用到一个结构或联合类型名称,或者一个结构

    或联合类型的标识符时,其结果是该结构或联合的字节个数,包括内部的和尾部的填充。这个尺寸可以包括用于在存储器边界上对齐结构或联合的成员而使用的内部和尾部填充。因此,其结果可能不对应于所有成员所需存储相加的结果。

    如果一个没有尺寸的数组是一个结构的最后元素,则sizeof运算符返回没有该数组的结构的尺寸。

    buffer=calloc(100,sizeof(int));

    这个例子使用sizeof运算符传送一个int的尺寸,它在不同的机器中可能不同,作为一个参量传送给名称为calloc的运行函数。由这个函数返回的值存储在buffer中。

    static char *strings[] = 
    {
    "this is string one",
    "this is string two",
    "this is string three",
    };

    const int string_no = (sizeof strings) / sizeof strings[0]);

    在这个例子中,strings是一个char的指针的数组。该指针的成员是数组中的元素个数,但不是指定的。使用sizeof运算符计算该数组中的元素个数容易确定指针个数。const整数值string_no初始化为这个数。因为这是一个const值,string_no不能修改。

    造型运算符

    一个类型造型提供了在特定状态下显式转换一个对象类型的方法。

    语法

    造型表达式:

    单目表达式

    (类型名称)造型表达式

    在类型造型强制转换之后,编译器处理造型表达式为类型名称所指的类型。造型可以把任何标量类型的对象转换成另一种标量类型的对象。显式类型造型也遵守本章后面“赋值转换”中的讨论的隐含转换规则。造型上的另外限制来自指定类型的实际尺寸或表示。有关整数类型的实际尺寸的信息,参见第3章“说明和类型”中的“基本类型的存储”。有关类型造型的更多信息,参见本章后面的“类型造型转换”。

    乘法运算符乘法

    运算符执行乘法(*)、除法(/)和余数(%)操作。

    语法

    乘法表达式:

    造型表达式

    乘法表达式 * 造型表达式

    乘法表达式 / 造型表达式

    乘法表达式 % 造型表达式

    余数运算符(%)的操作数必须是整数。乘法(*)和除法(/)运算符可以使用整数或浮点类型操作数,操作数的类型可以不同。

    乘法运算符对操作数执行常用的算术转换,其结果的类型是转换后操作数的类型。

    注意:由于乘法运算符执行的转换不提供上溢出和下溢出条件,如果一个乘法运算的结果不能以转换后的操作数的类型表示,则可能丢失信息。

    c乘法运算符的描述如下:

    运算符 说明

    * 该乘法运算符导致它们两个操作数相乘

    / 该除法运算符导致第一个操作数被第二个操作数相除如果两个整数相除且结果不是一个整数,则根据如下规则截除它:

    * 根据ANSI C标准,除0的结果是不确定的,C编译器在编译时或运行时产生一个错误

    * 如果两个操作数是正数或无符号数,其结果向0进行截除

    * 如果有一个操作数为负数,该运算的结果是小于或等于代数商的最大整数或 者大于或等于代数商的最小整数,这被实现所定义(参见下面的Microsoft 特殊处小节)

    运算符 说明

    % 余数运算符的结果是第一个操作数除以第二个操作数所得的余数。当除法不精确时,其结果由如下规则确定:

    * 如果右边操作数是0,其结果是不确定的

    * 如果两个操作数都是正数或无符号数,其结果为正数

    * 如果有一个操作数为负数,其结果是不精确的,它们被实现所定义(参见下面 的Microsoft特殊处小节)

    Microsoft特殊处

    在有一个操作数为负数的除法中,截除的方向朝向0。

    如果使用余数运算符的除法中有一个操作数为负数,其结果与被除数(表达式中的第一个操作数)的符号相同。

    Microsoft特殊处结束例子

    下面的说明使用以下例子:

    int i=10,j=3,n;

    double x=2.0,y;

    这个语句使用乘法运算符:

    y=x*i;

    在这种情况下,x乘以i得到值20.0,其结果为double类型。

    n=i/j;

    在这个例子中,10被3相除,结果朝向0进行截除,产生整数3。

    n=i%j;

    这个语句赋给n为10除以3的余数即1。

    Microsoft特殊处

    余数的符号与被除数的符号相同,例如:50%-6=2-50%6=-2以下情况下,50与2具有相同的符号。

    Microsoft特殊处结束

    加法运算符

    加法运算符执行加法(+)和减法(-)

    语法

    加法表达式:

    乘法表达式

    加法表达式 + 乘法表达式

    加法表达式 - 乘法表达式

    注意:虽然加法表达式的语法包括乘法表达式,这并不表示一定需要使用乘法。有关乘法表达式、造型表达式和单目表达式,参见本书后面的附录A“C语言语法总结”。

    操作数可以是整数或浮点值。有些加法运算也可以在指针值上执行,正如每个运算符的讨论中概述的。

    加法运算符在整数和浮点操作数上执行常用的算术转换。其结果的类型是转换后操作数的类型。由于加法运算符执行的转换不提供上溢出或下溢出条件,如果一个加法运算的结果不能由转换后操作数的类型表示,则可能丢失信息。

    加法(+)

    加法运算符(+)导致两个操作数相加。两个操作数可以是整数或浮点类型,或者一个操作数且另一个操作数是一个整数。

    当把一个整数加上一个指针时,整数值(i)通过将它乘以该指针所指值的尺寸来转换。在转换之后,整数值表示i存储器位置,而每个位置都有其指针类型指定的长度。不转换的整数值加到该指针值时,其结果是一个新的指针值以表示距离最初地址i个位置的地址。该新的指针值指向与最初指针值相同类型的值,因此与数组指标相同(参见本章前面的“一维数组”和“多维数组”)。如果和的指针指向超过了该数组范围,除了在超过最高端的第一个位置外,其结果是不确定的。有关更多信息参见本章后面的“指针算术”。

    减法(-)

    减法运算符(-)从第一个操作数减去第二个操作数。两个操作数可以是整数或浮点类型,或者一个操作数可以是一个指针且另一个操作数是一个整数。

    当两个指针相减时,其差通过除以该指针所指类型的值的尺寸来转换成一个有符号整数值。该整数值的尺寸由包括在标准头文件STDDEF.H中的类型ptrdiff_t定义。其结果是表示两个地址之间相差的存储器位置的个数,该结果仅对相同数组的两个元素才是有意义的,正如“指针算术”中讨论的。

    当从一个指针值减去一个整数时,减法运算符通过该整数值(i)乘以该指针所指值的尺寸来转换,在转换之后,该整数值表示i存储位置,而每个位置都有该指针类型所指的长度。当从该指针值减去该转换的整数值时,其结果是最初地址的前i个存储器位置。新的指针指向最初指针值所指的类型的值。

    使用加法运算符

    下例说明了加和减运算符,使用了这些说明:

    int i=4,j;

    float x[10];

    float *px;

    如下语句是等价的:

    px=&x[4+i];

    px=&x[4]+i;

    i的值乘以一个float的长度并加到&x[4]中,其结果指针值是x[8]的地址。

    j=&x[i]-&x[i-2];

    在这个例子中,从x 的第五个元素的地址(由x[i]给出)减去x的第三个元素的地址(由x[i-2]给出)。其差除以一个float的长度,结果为整数2。

    指针算术

    涉及到一个指针和一个整数的加法运算只存在该指针操作数指向一个数组的元素以及该整数产生同一数组中的偏移量时才有意义。当该整数值转换成一个地址偏移量时,编译器假设只有相同尺寸的存储器位置位于最初地址和该地址加上偏离量的结果之间。

    对于数组成员这个假设是有效的。通过定义,一个数组是相同类型的值的序列,它的元素存储在相邻存储器位置。但除数组外,其它任何类型的存储不能保证由相同类型的标识符填充,也即,存储器位置之间可能出现空格,即使相同类型的位置也是如此。因此,除数组元素外任何值的地址加或减的结果都是不确定的。

    类似地,当两个指针值相减时,其转换假设只有相同类型的值,没有空格,且在操作数给定的地址空间。

    按位位移运算符

    位移运算符左移(<<)或右移(>>)第一个操作数的位置个数,由第二个操作数指定。

    语法

    位移表达式:

    加法表达式

    位移表达式 << 加法表达式

    位移表达式 >> 加法表达式

    两个操作数必须是整数值,这些运算符执行常用的算术转换,其结果的类型是转换后左操作数的类型。

    对于向左移位移,腾出的右边位设置为0。对于向右位移,腾出的左边位基于转换后第一个操作数的类型进行填充。如果该类型是unsigned,它们设置为0;否则,它们用符号位的拷贝进行填充。对于向左位移运算符,没有上溢出,语句:

    expr1 << expr2

    等价于乘以2expr2。对于右移运算符:

    expr1 >> expr2

    如果expr1是无符号的或有一个非负值,它等价于除以2expr2。

    如果第二个操作数是负数,或者如果右操作数大于或等于提升的左操作数中的位为单位的宽度,则一个位移运算的结果是不确定的。由于位移运算符执行的转换没有提供上溢出或下溢出条件,如果一个位移运算的结果不能由转换后第一个操作数的类型所表示,则可能丢失信息。

    unsigned int x,y,z;

    x=0x00AA;

    y=0x5500;

    z=(x<<8)+(y>>8);

    在这个例子中,x左移8个位置,y右移8个位置。位移的值相加,结果为0xAA55并赋给z。将一个负数右移一位产生的结果是其绝对值舍入的一半,例如,-253(二进制1111111100000011)右移一位产生-127(二进制1111111110000001)。一个正数253右移一位产生+126。

    右移保留符号位。当一个有符号整数右位移时,保留设置最重要的位。当一个无符号整数右位移时,该最重要位被清除。

    如果0xF000是无符号数,其结果是0x7800。如果0xF0000000是有符号的,右移一位产生0xF8000000。右移一个正数32次产生0xF0000000。右移一个负数32次产生0xFFFFFFFF。

    关系和相等运算符

    双目关系和相等运算符将其第一个操作数与第二个操作数进行比较以测试指定关系的有效性。如果测试的关系为真,则该关系表达式的结果为1,如果为假则为0。其结果的类型为int。

    语法:

    关系表达式:

    位移表达式

    关系表达式 < 位移表达式

    关系表达式 > 位移表达式

    关系表达式 <= 位移表达式

    关系表达式 >= 位移表达式 相

    等表达式:

    关系表达式

    相等表达式 == 关系表达式

    相等表达式 != 关系表达式

    关系和相等运算符测试如下关系:

    运算符 测试的关系

    < 第一个操作数小于第二个操作数

    > 第一个操作数大于第二个操作数

    <= 第一个操作数小于或等于第二个操作数

    >= 第一个操作数大于或等于第二个操作数

    == 第一个操作数等于第二个操作数

    != 第一个操作数不等于第二个操作数

    上表中的开头四个运算符比相等运算符(==和!=)具有更高的优先级。参见表4.1中的优先级信息。

    操作数可以是整数、浮点或指针类型。操作数的类型可以不同。关系运算符在整数和浮点类型操作数上执行常用的算术转换。另外,你可以用关系和相等运算符构成如下操作数类型的组合:

    * 任何关系或相等运算符的两个操作数可以是相同类型的指针。对于等于(==)和不等于(!=) 运算符,比较的结果指出两个指针是否指向相同存储器位置。对于其它关系运算符(<、>、<=和>=),比较的结果指出所指对象的两个存储器位置之间的关系。关系运算符仅比较偏移量。

    只对相同对象的部分定义了指针比较。如果该指针指向一个数组的成员, 该比较等价于对应下标的比较。第一个数组元素的地址“小于”最后一个元素的地址。在结构的情况下,后面说明的结构成员的指针“大于”前面说明的结构成员的指针。对于同一联合成员的指针是相等的。

    * 一个指针值可以与常量值0比较是相等(==)或不相等(!=)。具有0值的指针称为“空”指针,也就是,它不指向任何有效的存储器位置。

    * 相等运算符遵守与关系运算符相同的规则,但允许另外的可能:一个指针可以与一个具有0值的常量整数表达式或与void的指针进行比较。如果两个指针都为空指针,它们是相等的。相等运算符比较两个段和偏移量。

    例子

    如下例子说明关系和相等运算符

    int x=0,y=0;

    if (x<y)

    因为x和y相等,这个例子中的表达式产生值0。

    char array[10];

    char *p;

    for (p=array;p<&array[10];p++)

    *p=′/0′;

    本例中的段设置array的每个元素为一个空字符常量。

    enum color {red,white,green} col;

    if (col==red)

    .这些语句用标志color说明一个名称为col的枚举变量。在任何时候,该变量可以包含0、1或2的一个整数值,它表示枚举集color:分别为红、白和绿之一。当执行if语句时如果col包含0,则执行依赖该if的任何语句。

    按位运算符

    按位运算符执行按位AND(&)、按位异或(^)和按位OR(|)运算。

    语法

    AND表达式:

    相等表达式

    AND表达式 & 相等表达式

    异或表达式:

    AND表达式

    异或表达式 ^ AND表达式

    OR表达式:

    异或表达式

    OR表达式 | 异或表达式

    按位运算符的操作数必须有整数类型,但它们的类型可能不同。这些运算符执行常用的算术转换。其结果的类型是转换后操作数的类型。

    C按位运算符描述如下:

    运算符 说明

    & 按位AND运算符将第一个操作数的每个位与第二个操作数的对应位进行比较,如果两个位都为1,对应的结果位设置为1;否则,对应的结果位设置为0。

    ^ 按位异或运算符将第一个操作数的每个位与第二个操作数的对应位进行比较,如果一个位为0而另一位为1,对应的结果位设置为1,否则,对应的结果位设置为0

    | 按位OR运算符将第一个操作数的每个位与第二个操作数的对应位进行比较,如果有一个位为0,对应的结果位设置为1;否则,对应的结果位设置为1

    例子

    这些说明用于如下三个例子:

    short i=0xAB00;

    short i=0xABCD;

    short n;n=i&j;

    在第一个例子中赋给n的结果与i的值(十六进制0xAB00)相同。

    n=i|j;

    n=i^j;

    在第二个例子中按位OR产生结果0xABCD(十六进制数),而在第三个例子中的按位异或产生0xCD(十六进制数)。

    Microsoft特殊处

    在有符号整数上按位运算的结果是根据ANSI C标准实现定义的。对于C编译器,在有符号整数上的按位运算与在无符号整数上的按位运算相同。例如,-16&99可以以二进制表示为:

    11111111 11110000

    & 00000000 01100011

    -------------------

    00000000 01100000

    该按位AND的结果是十进制96。

    Microsoft特殊处结束

    逻辑运算符逻辑运算符执行逻辑AND(&&)和逻辑OR(||)运算。

    语法

    逻辑AND表达式

    OR表达式

    逻辑AND表达式 && OR表

    达式OR表达式:

    逻辑AND表达式

    逻辑AND表达式 || 逻辑AND表达式

    逻辑运算符不执行常用的算术转换。代替地,它们根据它的0的等值对每个操作数进行求值,一个逻辑运算的结果为0或1,其结果的类型为int。C逻辑运算符描述如下:

    运算符 说明

    && 如果两个操作数都为非0值,则逻辑AND运算符产生值1。如果有一个操作数为0,其结果为0。如果一个逻辑AND运算的第一个操作数等于0,第二个操作数不求值

    || 逻辑OR运算符在其操作数上执行一个OR运算。如果两个操作数都为0值,其结果为0。如果有一个操作数为非0值,其结果为1。如果一个逻辑OR运算符的第一个操作数为非0值,则第二个操作数不求值逻辑AND和逻辑OR表达式的操作数从左到右求值。

    如果第一个操作数的值能够足够确定该运算的结果,则第二个结果数不进行求值,这称为“短路求值”。在第一个操作数之后有一个顺序点。有关更多信息参见本章前面的“顺序点”。

    例子如下例子说明逻辑运算符:

    int w,x,y,z;

    if (x<y && y<z)

    printf("x is less than z/n");在这个例子中,如果x小于y且y小于z则调用printf函数打印一个消息。

    如果x大于y,第二个操作数(y<z)不求值且不打印该消息。注意,在这种情况可能出现问题,第二个操作数由于某些其它原因而有副作用。

    printf ("%d",(x==w || x==y || x==z));

    在这个例子中,如果x等于w和y或z,printf函数的第二个求值为真时打印1;否则,求值假时打印0。只要条件之一求值为真,则求值停止。

    条件表达式运算符

    C有一个三元运算符:条件表达式运算符(?:)。

    语法

    条件表达式:

    逻辑OR表达式

    逻辑OR表达式 ? 表达式 : 条件表达式

    逻辑OR表达式必须具有整数、浮点或指针类型。根据等值于0的方式进行求值。一个顺序点紧跟逻辑OR表达式。操作数求值过程如下:

    * 如果逻辑OR表达式不等于0表达式求值。该结果由非终结符表达式给出(这意味着表达式仅在逻辑OR表达式为真时求值)。

    * 如果逻辑OR表达式等于0,条件表达式求值。该结果是其条件表达式的值(这意味着仅在逻辑OR表达式为假时对条件表达式求值)。注意,表达式或条件表达式之一求值,但不是两者。一个条件表达式结果的类型依赖于表达式或条件表达式操作数的类型如下:

    * 如果表达式或条件表达式具有整数或浮点类型(它们的类型可以不同),该运算符执行常用的算术转换。该结果的类型是转换后操作数的类型。

    * 如果表达式和条件表达式具有相同的结构、联合或指针类型,该结果类型是同样的结构、联合或指针类型。

    * 如果两个操作数都具有void类型,该结果是void类型。

    * 如果操作数之一为任何类型的一个对象的指针,另一个操作数是一个void的指针,该对象的指针转换成一个void的指针,该结果是一个void的指针。

    * 如果表达式或条件表达式之一是一个指针,另一个操作数是一个具有0值的常量表达式。该结果的类型是void的指针。

    在指针的类型比较中,该指针所指的类型中的任何类型修饰符(const或volatile)都是不重要的。但结果类型以两个条件的组成成分继承修饰符。

    例子

    如下例子说明条件运算符的使用

    j=(i<0) ? (-i) : (i);

    这个例子把i的绝对值赋给j。如果i小于0,则把-i赋给j;如果i大于或等于0,i赋给j。

    void f1(void)

    void f2(void);

    int x;int y;

    (x == y) ? (f1()) : (f2());

    在这个例子中,说明了两个函数f1和f2以及两个变量x和y。在程序后面,如果这两个变量具有相同的值,则调用f1函数;否则调用f2函数。

    赋值运算符

    一个赋值运算把右边操作数的值赋给左边操作数命名的存储位置。因此,一个赋值运算的左边操作数必须是可修改的l值。在赋值之后,一个赋值表达式具有左边操作数的值,但不是一个l值。

    语法

    赋值表达式:

    条件表达式

    单目表达式 赋值运算符 赋值表达式

    赋值运算符:如下之一

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

    C中的赋值运算符可以在单个运算中进行转换和赋值。C提供了如下赋值运算符:

    运算符 执行的运算

    = 简单赋值

    *= 乘法赋值

    /= 除法赋值

    %= 余数赋值

    += 加法赋值

    -= 减法赋值

    <<= 左位移赋值

    >>= 右位移赋值

    &= 按位AND赋值

    ^= 按位异或赋值

    |+ 按位或赋值

    在赋值中,右边值的类型转换成左边值的类型,并在赋值发生后把该值存储在左边操作数中。左边操作数不能是数组、函数或常量。在本章后面的“类型转换”中详细讨论了指定的转换路径,它依赖于两种类型。

    简单赋值

    简单赋值运算符把它的右操作数赋给左操作数。右操作数的值转换成赋值表达式的类型,并替换存储在左操作数指定的对象中的值。应用赋值的转换规则(参见本章后面的“赋值转换”)。

    double x;

    int y;

    x=y;

    在这个例子中,y的值转换成double类型并赋给x。

    复合赋值

    复合赋值运算符组合简单赋值运算符和其它双目运算符。复合赋值运算符执行另外运算符指定的运算,然后把结果赋给左操作数。例如,这样的一个复合赋值表达式:

    expression1+=expression2

    可以理解为

    expression1=expression1+expression2

    但复合赋值表达式不等于扩充的版本,因为在加法运算中和在赋值运算中,复合赋值表达式仅求值expression1一次,而扩充的版本求值expression1两次。

    一个复合赋值运算符的操作数必须是整数或浮点类型。每个复合赋值运算符执行对应的双目运算符执行的转换,并相应地限制它的操作数的类型。加法赋值(+=)和减法赋值(-=)运算符也有一个指针类型的左操作数,在这种情况下右边操作数必须是整数类型。一个复合操作数的结果具有左操作数的值和类型。

    #define MASK 0xff00

    n&=MASK;

    在这个例子中,在n和MASK上执行一个按位AND运算,并把结果赋给n。显式常量MASK用一个#define 预处理器命令定义。

    顺序求值运算符

    顺序求值运算符也称为“逗号运算符”,从左向右顺序计算它的两个操作数。

    语法

    表达式:

    赋值表达式

    表达式,

    赋值表达式顺序求值运算符的左操作数作为一个void表达式求值。该运算的结果具有和右操作数相同的值和类型,每个操作数可以是任何类型。顺序求值运算符在它的操作数之间不执行任何类型转换,它不产生l值。在第一个操作数之后产生一个顺序点,这意味着从左操作数求值产生的副作用在右操作数求值开始之前均已完成。有关更多信息,参见本章前面的“顺序点”。

    顺序求值运算符通常用于对上下文中两个或多个表达式求值,而这里仅允许一个表达式。

    逗号在某些上下文中用作分隔符,但你必须小心不要将它作为分隔符使用和作为运算符使用相混淆,这两种使用是完全不同的。

    例子

    这个例子说明顺序求值运算符:

    for (i=j=1;i+j<20;i+=i,j--);

    在这个例子中,for语句的第三个表达式的每个操作数都分开求值。左操作数i+=i首先求值,然后是右操作数j--求值。

    func_one (x,y+2,z);

    func_two((x--,y+2),z);

    在func_one函数调用中,三个用逗号隔开的参量分别传送给:x,y+2和z。在func_two函数调用中,圆括号强制编译器解释第一个逗号为顺序求值运算符。这个函数调用传送两个参量给func_two,第一个参量是顺序求值运算(x--,y+2)的结果,它具有y+2表达式的值和类型,第二个参量是z。


    类型转换

    类型转换依赖于指定的运算符和操作数或运算符的类型。类型转换在如下情况下执行:

    * 当一个类型的值赋给不同类型的变量时,是或者一个运算符在执行该运算之前转换它的一个操作数或多个操作数的类型时。

    * 当一个类型的值显式造型转换为一个不同的类型时。

    * 当一个值作为一个参量传送给一个函数或者当从一个函数返回一个类型时。

    * 一个字符、一个短整数、一个整数位域、所有有符号的或无符号的或者枚举类型的一个对象都可以用在表达式能够使用整数的地方。如果一个int可以表示所有最初类型的值,那么该值转换成int,否则,它转换成unsigned int。这个过程称为“整数提升”。整数提升保持值,也就是,在提升之后的值保证与提升前的值相同。有关更多信息参见本章前面的“常用的算术转换”。

    赋值转换

    在赋值运算中,赋值的类型转换成接受赋值的变量的类型。C允许整数和浮点类型之间通过赋值进行转换,即使在转换中选择信息也如此。使用的转换方法依赖于赋值中涉及的类型,正如“常用的算术转换”和如下小节描述的:

    * 从有符号整数类型转换

    * 从无符号整数类型转换

    * 从浮点类型转换

    * 从指针类型转换和转换到指针类型

    * 从其它类型转换类型修饰符不影响转换的允许性,虽然一个const值不能用作赋值的左边。

    从有符号整数类型转换

    当一个有符号整数用相等或更大的尺寸转换成一个无符号整数,且该有符号整数不是负数时,该值不改变。通过符号扩充该有符号整数来进行转换,一个有符号整数通过高端的位转换成一个更短的有符号整数,其结果解释为一个无符号整数,如下例子:

    int i=-3;

    unsigned short u;

    u=i;

    printf("%hu/n",u); /*打印65533*/

    当一个有符号整数转换成一个浮点类型时,没有信息丢失,除当一个longint或unsigned long int值转换成一个float值可能丢失某些精度外.

    表4.2总结了有符号整数类型的转换。这个表假设缺省char类型为有符号的。如果你使用一个编译选项改变缺省char类型为无符号的,这个转换在表4.3中给出,即应用unsigned char类型代替表4.2中的转换。

    表4.2 有符号整数类型的转换

    方法
    charshort符号扩充
    charlong符号扩充
    charunsigned char保留模式,丢失高端作为符号的位
    charunsigned short符号扩充为short,转换short为unsignedshort
    charunsigned long符号扩充为long,转换long为unsigned long
    charfloat符号扩充为long,转换long为float
    chardouble符号扩充为long,转换long为double
    charlong double符号扩充为long,转换long为double
    shortChar保留低端字节
    shortlong符号扩充
    shortunsigned char保留低端字节
    shortunsigned short保留模式,丢失高端作为符号位的位
    shortunsigned long符号扩充为long,转换long为unsigned long
    shortfloat符号扩充为long,转换long为float
    shortdouble符号扩充为long,转换long为double
    shortlong double符号扩充为long,转换long为double
    longchar保留低端字节longshort保留低端字
    longunsigned char保留低端字节
    longunsigned short保留低端字
    longunsigned long保留模式,丢失高端作为符号位的位
    longfloat作为float表示,如果long不能精确表示,丢失精度longdouble作为double表示,如果long不能作为一个double精确表示,丢失精度
    longlong double作为double表示,如果long不能作为一个double精确表示,丢失精度

    1. 所有char项假设char类型缺省是有符号的。

    Microsoft特殊处

    对于一个Microsoft 32位C编译器,一个整数等价于long。一个int值转换处理与long的相同。

    Microsoft特殊处结束

    从无符号整数类型转换

    一个无符号整数通过截除高端的位转换成一个更短的无符号或有符号整数,或者通过0扩充(参见表4.3)转换成一个更长的无符号或有符号整数。当整数类型的值用更小的尺寸降级为一个有符号整数,或者一个无符号整数转换成它对应的有符号整数时,如果它能在新类型中表示则不改变它的值。但如果设置符号位,则它表示的值发生改变,如下例子:

    int j;

    unsigned short k=65533;

    j=k;

    printf("%hd/n",j); /*打印-3*/

    如果它不能表示,其结果是实现定义的。有关C编译器处理整数降级的更多信息,参见本章后面“类型造型转换”。从整数类型转换或从造型整数类型的转换的结果相同。

    无符号值只能以保留它们的值的方式进行转换,在C中是不可直接表示的。唯一的异常是从unsigned long转换到float,它丢失了最重要的低端位。否则保留值,无论是有符号的还是无符号的。当一个整数类型的值

    转换成浮点时,该值超过了表示范围,其结果是不可预料的(有关整数和浮点类型的范围的信息,参见第3章“说明和类型”中的“基本类型的存储”)。

    表4.3总结了从无符号整数类型的转换。

    表4.3 从无符号整数类型的转换

    方法
    unsigned charchar保留位模式,高端位变成符号位
    unsigned charshort0扩充
    unsigned charlong0扩充
    unsigned charunsigned short0扩充
    unsigned charunsigned long0扩充
    unsigned charfloat转换为long,转换long为float
    unsigned chardouble转换为long,转换long为double
    unsigned charlong double转换为long,转换long为double
    unsigned shortchar保留低端字节
    unsigned shortshort保留位模式,高端位变成符号位
    unsigned shortlong0扩充
    unsigned shortunsigned char保留低端字节
    unsigned shortunsigned long0扩充
    unsigned shortfloat转换为long,转换long为float
    unsigned shortdouble转换为long,转换long为double
    unsigned shortlong double转换为long,转换long为double
    unsigned longchar保留低端字节
    unsigned longShort保留低端字
    unsigned longlong保留位模式,高端位变成符号位
    unsigned longunsigned char保留低端字节
    unsigned longunsigned short保留低端字
    unsigned longfloat转换为long,转换long为float
    unsigned longdouble直接转换为double
    unsigned longlong double转换为long,转换long为double

    Microsoft特殊处

    对于Microsoft 32位C编译器,unsigned int类型等价于unsigned long类型。一个unsigned int值的转换的方式与一个unsigned long的转换方式相同。如果转换的值大于最大正的有符号long值,那么从unsigned long值转换到float是不准确的。

    Microsoft特殊处结束

    从浮点类型转换

    一个float 值可以转换成一个double或long double,或者一个double转换成一个long double,不改变其中的值。如果可能,一个double值转换成一个准确表示的float值。如果该值不能准确表示,则可能丢失精度。如果结果超出了范围,其行为是不可确定的。有关浮点类型的范围,参见第1章“C的基本元素”中的“浮点常量的限制”。

    通过把一个浮点值先转换成一个long,然后把该long值转换成指定的整数值,正如表4.4所说明的,把该浮点值转换成一个整数值,在转换成long中,丢弃该浮点值的小数部分。如果仍太大不适合long表示,则该转换的结果是不确定的。

    Microsoft特殊处

    当把一个double或long double浮点数转换成一个较小的浮点数时,如果发生下溢出则朝向0截除该浮点数的值。一个上溢出导致一个运行错误。注意,C编译器把long double映射成double。

    Microsoft特殊处结束

    表4.4总结了从浮点类型的转换。

    表4.4 从浮点类型的转换

    方法
    floatchar转换为long;转换long为char
    floatshort转换为long;转换long为short
    floatlong在小数点处截除。如果结果太大不能表示成long,则其结果是不确定的
    floatunsigned short转换为long,转换long为unsigned short
    floatunsigned long转换为long,转换long为unsigned long
    floatdouble改变内部表示
    floatlong chouble改变内部表示doublechar转换为float,转换float为char
    doubleshort转换为float,转换float为short
    doublelong在小数点处截除。如果该结果太大不能用long表示,则结果是不确定的
    doubleunsigned short转换为long,转换long为unsigned short
    doubleunsigned long转换为long,转换long为unsigned longdoublefloat表示为float。如果double值不能以float类型准确地表示,会出现精度丢失。如果该值太大不能作为float表示,其结果是不确定的
    longdoublechar转换为float,转换float为char
    long doublshort转换为float,转换float为short
    longdoublelong在小数点处截除。如果结果太大不能用long表示,则结果是不确定的
    longdoubleunsignedshort转换为long;转换long为unsigned short
    longdoubleunsignedlong转换为long;转换long为unsigned long
    longdoublefloat表示为float。如果double值不能以float类型准确地表示,会出现精度丢失。如果该值太大不能作为float表示,其结果是不确定的
    longdoubledouble该long double值作为double处理如果被转换的值大于最大正数long值,则从float、double或long double值到unsigned long的转换是不准确的。

    转换到指针类型和从指针类型转换

    一个值类型的指针可以转换成一个不同类型的指针。虽然这个结果可能因为对齐要求和不同类型的存储尺寸而导致其结果是不确定的。一个对象的指针可以转换成一个其类型需要较少或完全相同的对象的指针,反过来没有改变。

    一个void的指针可以转换成任何类型的一个指针,或者从任何类型的指针可以转换成一个void的指针。

    如果其结果被回过来转换成最初的类型,则恢复最初的指针。如果一个指针转换成另一个指针,后者有相同的类型,但有不同的或另外的修饰符,该新的指针与老的指针相同,除了强制加上新的修饰符之外。一个指针值也可以转换成一个整数值。根据如下规则,其转换路径依赖于该指针的尺寸和整数类型的尺寸:

    * 如果指针的尺寸大于或等于该整数的尺寸,该指针的行为在转换中像一个无符号值一样,除了它不能转换成一个浮点值外。

    * 如果该指针小于整数类型,首先把该指针转换成一个与整数类型相同尺寸的指针,然后再转换成整数类型。相反地,一个整数类型也可以根据如下规则转换成一个指针类型:

    * 如果该整数类型与指针类型具有相同的尺寸,简单地转换该整数值作为一个指针(一个无符号整数)处理。

    * 如果该整数的尺寸不同于该指针类型的尺寸,首先把整数类型转换成该指针的尺寸,这里使用表4.2和表4.3中给出的转换路线;然后把它作为一个指针值处理。

    一个具有0型的整数常量表达式或一个强制造型为类型void *的表达式可以通过一个类型造型、赋值或与任何类型的指针进行比较来转换。这产生一个等于另一个相同类型空指针的空指针,但这个空指针不等于一个函数或一个对象的任何指针。不为常量0的其它整数也可以转换成指针类型,但其结果不是可移植的。

    从其它类型转换

    由于一个enum值是一个定义的int,从一个enum值转换或转换到enum值与int类型的方式相同。对于C编译器,一个整数与一个long是相同的。

    Microsoft特殊处

    结构或联合类型之间不允许转换。

    任何值可以转换成类型void,但这个转换的结果仅用在一个表达式值被丢弃的上下文中,例如在一个表达式语句中。

    由定义,void类型没有值,因此它不能转换成任何其它类型,其它类型也不能通过赋值转换成void,然而你可以显式造型转换一个值为类型void,正如下一节“类型造型转换”中讨论的。

    Microsoft特殊处结束

    类型造型转换

    你可以使用类型造型显式转换类型。

    语法造型表达式:

    单目表达式

    (类型名称)造型表达式

    类型名称:

    指示符修饰符表 抽象说明符opt

    类型名称是一个类型,造型表达式是要转换成这个类型的值。一个带有类型造型的表达式不能是一个l值。造型表达式是被转换赋给类型名称指定类型的变量。赋值的转换规则(在前面“赋值转换”中描述)也适用于类型造型。

    表4.5说明了可以造型转换为给定类型的类型。

    表4.5 合法的类型造型

    目的类型可能的源
    整数类型任何整数类型、浮点类型或一个针对对象的指针
    浮点类型任何算术类型
    一个针对对象的指针或(void*)任何整数类型、(void*)、一个针对对象的指针或一个函数指针函数指针任何整数类型、
    void类型任何类型

    任何标识符都可以造型转换为void,但如果在一个类型造型表达中的指定的类型不是void,那么该标识符造型转换成不是一个void表达式的类型。任何表达式都可以造型为void,但一个类型为void的类型不能造型转换为任何其它类型。例如,一个具有void类型的函数不能有另一个类型的返回值。

    注意,一个void表达式有一个void的类型指针,而不是类型void。如果一个对象类型为void类型,其结果表达式不能赋给任何项。类似地,一个类型造型对象不是一个可接受的l值,因此不能赋给一个类型造型对象。

    Microsoft特殊处

    只要该标识符的尺寸不发生改变,一个类型造型可以是一个l值表达式。有关l值表示式的信息,参见本章开头的“L值和R值表达式”。

    Microsoft特殊处结束

    你可以用一个造型把一个表达式转换为类型void,但结果表达式只能用于不需要一个值的地方。一个转换为void *的对象指针,在回过来转换为最初的类型时将返回它的最初值。

    函数调用转换在一个函数调用中参量上执行的转换类型依赖于用于说明该被调用函数的参量的函数原型(向前说明)。

    如果有一个函数原型上包括说明的参量类型,编译器执行类型检测(参见第6章“函数”)。

    如果没有函数原型,则在函数调用中参量上执行常用的算术转换。这些转换在调用中的每个参量上独立执行。这意味着一个float值可以转换为一个double值;一个char或short值可以转换为一个int;一个unsignedchar或unsigned sort可以转换为一个unsigned int。

    展开全文
  • 首先学习了代码的格式,然后学习了python的赋值语句表达式,流程控制语句。 1.代码风格 Python的格式规范——PEP8 主要规范: 注意缩进,不能把tab键与四个空格连用,最好使用四个空格 一行不超过79个字符...

    目录

    1.代码风格

    2.赋值语句

    3.表达式

    4.流程控制

    5.迭代


    学习完Python数据格式后,本节学习Python的语句与表达式。

    首先学习了代码的格式,然后学习了python的赋值语句,表达式,流程控制语句。

    1.代码风格

    Python的格式规范——PEP8

    主要规范:

    • 注意缩进,不能把tab键与四个空格连用,最好使用四个空格
    • 一行不超过79个字符(直接换行即可)
    • 函数与函数之间空两行

    2.赋值语句

    基本赋值:=

    序列赋值:

    x,y = 5,10 
    
    x,y,z = 10,20,30
    
    # Python交换两个值
    x,y = y,x
    
    # 拆包------>结果 a='l' b ='i' c='k' 注:如果序列短于或长于被赋值的元素会发生错误
    a,b,c = 'lik'

    扩展序列的拆包赋值:剩余元素会赋值给*元素,*元素可以为空

    # *代表获取剩余元素到list--->结果a='h',b='e',c=['l','l','o']  
    a,b,*c = 'hello'

    多目标赋值:

     a = b = 10
    # 注意:内存中会内置小于256的数字,会内置长度小于等于3的字符串
    # 也就是说,如果a=b=10或者a=b='aaa'他们指向内存中相同的地址可以用is判断
    
    # 注意:如果因为列表是可变类型所以a=b=列表时,要注意改变其中一个另一个会跟着改变的问题

    参数化赋值:

    a += b
    
    c = [1,3]
    d = [2,4]
    c.expend(d)
    # 注:也可用c += d,但效率低于.expend()

     

    3.表达式

    • 字面值表达式
    • 函数调用:len()
    • 方法调用:如序列.pop()
    • 打印操作:
      # sep='分隔符'
      print(a,b,c,sep='|')
      
      # end='终止符'
      print(a,b,c,end='.../n')
      
      # 将输出打印到文件中 file='指定元素'
      print(a,b,c,file = open('result.txt','w',encoding='utf8')

       

    4.流程控制

    if条件判断

    格式: if...elif...elif...else

    三元运算的if:a = Y if X else Z (如果X成立将Y赋值给a如果不成立将Z赋值给a)

    while循环

    break,continue,pass,else

    # else的用法,若循环不是由执行break退出的话就执行else里面的语句
    x = 10
    while x:
        x -= 1
        print(x)
    else:
        print("循环结束")
    

    for循环

    for x in 目标序列(可以是列表,字符串,字典,元组)

    range()

    for i in range(10)
        print(i)

    enumerate()

    for i,t in enumerate(s):
        print('{}) {}'.format(i,t))

    python中的switch操作:

    operation = { 'add':'添加操作',
                  'update':'更新操作',
                  'delete':'删除操作'
                }
    
    print(operation.get('add'))
    print(operation.get('update'))
    print(operation.get('delete'))

    也可以指定执行函数

    def add(x):
        print(x+10)
    
    
    operation = { 'add': add,
                  'update':lambda x:print(x*2),
                  'delete':lambda x:print(x*3)
                }
    
    
    def default_method(x):
        print('默认方法,什么都不做')
    
    operation.get('add',default_method)(10)
    operation.get('update',default_method)(10)
    operation.get('delete',default_method)(10)

    5.迭代

    Python中哪些对象可以用for呢?

    实现了迭代协议的对象或者是可迭代对象。

    迭代器对象

    实现了迭代协议的对象称为可以使用.__next__() 或者全局函数next()

    如文件格式

    f = open("data.txt","r",encoding="utf8")
    f.__next__()
    next(f)

    列表不能使用__next__()方法但可以用for循环?

    列表,可以用for迭代,但其实是for自动给可迭代对象添加了迭代器方法。使用iter(可迭代对象)

    可迭代对象

    没有实现迭代器方法,转换成迭代器对象使用iter(可迭代对象),如列表。

    判断可迭代对象是否实现了迭代协议

    l = [1,2,3]
    iter(l) is l  # False

    内置可迭代对象

    range()、zip(['x','y','z'],[1,2,3])、map(函数,可迭代对象)

    迭代的总结

    python实现了迭代协议的对象或者可迭代对象可以用for循环,使用iter()可让可迭代对象实现迭代协议,实现迭代协议后就可以使用.__next__()或next()能够获取下一个元素。python中有些对象已经帮我们写好了迭代协议如文件,有些没写好如list,因为list可嵌套会比较复杂。for循环如果直接使用list虽然看起来比较简单但列表过大时内存中占用大效率低,所以内置将list先变为可迭代对象,可迭代对象虽然有一些list操作不支持,但是它使用next指针占用内存比较小。

     

    展开全文
  • 表达式和基本语句

    千次阅读 2009-09-16 21:40:00
    表达式和基本语句 1.表达式 1.1. 算术运算符 算术运算符包括一元运算符二元运算符。 运算符的优先级结合性   二元运算符(即*、/、%、+-)都是左结合的,所以 i-j-k 等价于 (i-j...

     

    表达式和基本语句

    1.表达式

    1.1. 算术运算符

    算术运算符包括一元运算符和二元运算符。

    运算符的优先级和结合性

     

    二元运算符(即*、/、%、+和-)都是左结合的,所以

    i-j-k 等价于 (i-j)-k

     

    一元运算符(+和-)都是右结合的,所以

    -+i  等价于 -(+i)

     

    1.2. 赋值运算符

    1.2.1. 简单赋值

    赋值运算符(=)是右结合的,所以

    i=j=k=0;

    等价于

    i=(j=(k=0));

     

    1.2.2. 左值

    赋值运算符要求它左边的操作数必须是左值(lvalue)。左值表示存储在计算机内存中的对象,而不是常量和计算结果。变量是左值,而诸如10或2*1这样的表达式则不是左值。

    下列赋值式子都是不合法的:

    12 = i; /** WRORG**/

    i + j = 0; /** WRORG**/

    -i = j; /** WRORG**/

    编译器将会检查出这个自然错误,并且显示出诸如”Lvalue required”这样的错误信息。

     

    1.2.3. 复合赋值

    复合赋值运算符:=、*=、/=、%=、+=、-=

    例如:v+=e 表示v加上e,然后结果存在v中。

     

    复合赋值运算符是右结合的,所以

    i += j += k;

    等价于

    i += (j += k);

     

      注意:表达式i += j与表达式 i = +j是不一样的。

    1.3. 自增运算符和自减运算符

    ++(自增)和—(自减)

    ++i意味着“立即增一”,而i++则意味着“现在先用i的原始值,稍后再自增-”。

    注意:后缀++和后缀—比一元的正号、负号优先级高,而且都是左结合的。前缀++和前缀—与一元的正号、负号优先级相同,而且都是右结合的。

     

    1.4. 关系运算符

    关系运算符:<、<=、>和>=

    关系运算符在C语言的表达式时产生的结果是0或1。关系运算符可以用来比较整数和浮点数,以及允许的混合类型的操作数。关系运算符的优先级低于算术运算符,且是左结合的。比如

    i<j<k

    等价于

    (i<j)<k

    也就是说,表达式先检测i是否小于j,然后用比较后的结果1或0来和k进行比较。

     

    1.5. 判等运算符

    判等运算符(==和!=)也是左结合的,而且也是产生了0或1作为结果。判等运算符的优先级低于关系运算符。例如

    i<j == j<k  等价于表达式 (i<j)==(j<k)

     

    1.6. 逻辑运算符

    !是一元运算符,而&&和||是二元运算符。逻辑运算符所产生的结果是0或1。

    运算符&&和||都对操作数进行“短路”计算。也就是说首先计算出左侧操作数的值,然后是右侧操作数。如下:

    (i != 0) && (j/i > 0)

    为了得到此表达式的值,首先必须计算表达式(i != 0)的值。如果i不等于0,那么需要计算表达式(j/i > 0)的值,确定整个表达式的值为真还是为假。

     

    运算符!的优先级和一元运算符正号、负号的优先级相同。运算符&&和||的优先级低于关系运算符和判等运算符。运算符!是右结合的,运算符&&和||是左结合的。

     

    1.7. 条件表达式

    表达式1?表达式2:表达式3

    称为三元运算符,读作“如果表达式1成立,那么表达式2,否则表达式3“

    条件运算符的优先级低于先前介绍过的所有运算符。

     

    1.7. 逗号运算符

    表达式1,表达式2

    逗号表达式的计算通过两步来实现:第一步,计算表达式1并且扔掉计算出的值;第二步,计算表达式2的值,把这个值作为整个表达式的值。例如

    ++i,i+j时变量i先进行自增,然后在计算i+j,所以表达式的值为7。

    逗号运算符的优先级低于所有其他运算符,是左结合的。

    i=1,j=2,k=i+j;

    等价于

    ((i=1,j=2),k=i+j;)

     

     

    1.8. Q&A

    问:如何对对数进行幂操作?

    答:通过重复乘法运算的方法可以进行整数的小范围正整数次幂运算(i*i*i是i的立方运算)。如果想计算数的非正整数次幂运算,可以调用pow函数。

     

    问:如果想吧%运算符用于浮点数,而程序又无法编译通过,怎么办?

    答: %要求整数操作数,所以可以调用fmod函数。

     

    问:为什么v+=e不等价于v=v+e?

    答:计算v+=e只是导致求一次v的值,而v=v+e则会求两次v的值。任何副作用都能导致两次求v的值,在下面的例子中,i自增一次:

    a[i++] += 2;

    如果用=代替+=,则i会自增两次:

    a[i++] = a[i++] +2;

     

    问:++和—是否可以处理float型变量?

    答:可以。自增和自减运算可以用于所有数值类型。

     

    问:当用=代替==时编译器没有发出警告。是否有方法可以强制编译器注意这类问题?

    答:可以。if(i == 0) …

    习惯上改写成

    if(0 == i) …

    如果运算符==意外地写成了=;

    if(0 = i) …

    因为不可能给0赋值,所以编译器会产生错误信息。

     

    2. 基本语句

    2.1. if语句

    if(表达式)

    语句

    else if(表达式)

    语句

    。。。

    else if(表达式)

    语句

    else

    语句

    当嵌套if语句时,注意“悬空else“的问题。C语言遵循的规则是else子句应该属于离它最近的且还没有和其他else匹配的if语句。例如

    if(y > 0)

    if(x > 0)

        result = x / y;

    else

        printf(“ERROR: y is equal to 0/n”);


      为了使else子句属于外层的if语句,可以把内层的if语句用大括号括起来:

      if(y > 0)
      {

    if(x > 0)
            result = x / y;
      }

      else

    printf(“ERROR: y is equal to 0/n”);

     

     

     

    2.2. switch语句

    switch (表达式){

       case 常量表达式:多条语句

                       break;

         。。。

    case 常量表达式:多条语句

    break;

    case 常量表达式:多条语句

    break;

    default:多条语句

                    break;

    }

    switch语句等价于级联if语句。


    2.3. while语句

    while (表达式)语句

     

    2.4. do语句

    do 语句 while (表达式);

    do语句本质上就是while语句,只不过do语句是在每次执行循环体之后对控制表达式进行判断。

     

    2.5. for语句

    for (表达式1;表达式2;表达式3)语句

     

    除了极少数情况以外,for循环总可以用等价的while循环替换:

    表达式1;

    while(表达式2){

    语句

    表达式3;

    }

    for循环体内含有continue语句时,for循环不再等价于while循环。

     

    for语句中省略表达式

    如果忽略第一个表达式,那么在执行循环语句前没有初始化操作;

    如果忽略第三个表达式,那么循环体有责任要确认第二个表达式的值最终会变为假。

    如果忽略第一个和第三个表达式,循环结果和while语句没有任何分别。

    如果忽略第二个表达式,那么它默认为真值,因此for语句不会终止。

     

    2.6. break语句

    break语句可以用来退出while、do和for循环,不过当出现循环嵌套时,只能跳出一层嵌套。

     

    2.7. continue语句

    continue语句把程序控制正好转移到循环体结束之前的一点,会把程序控制留在循环内。

     

    2.8. goto语句

    goto 标示符;

    goto语句可以跳出多层嵌套。建议少用、慎用goto语句。

     

    2.9. Q&A

    问:如果i是int型变量,而f是float型变量,那么条件表达式(i>0?i:f)是哪一种类型的值?

    答:当int和float型的值混合在一个条件表达式中,表达式的类型float型。如果i>0,那么变量i转化为float型后的值就是表达式的值。

     

    问:下面哪种写法,循环语句的效率更高?

     

    for (row=0; row<100; row++)

    {

    for ( col=0; col<5; col++ )

    {

    sum = sum + a[row][col];

    }

    }

    for (col=0; col<5; col++ )

    {

    for (row=0; row<100; row++)

    {

        sum = sum + a[row][col];

    }

    }

    (a)长循环在最外层                        (b)长循环在最内层

    答:C++/C循环语句中,for语句使用频率最高,while语句其次,do语句很少用。在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。例如(b)的效率比(a)的高。

     

    问:下面哪种写法,循环语句的效率更高?

     

    for (i=0; i<N; i++)

    {

    if (condition)

        DoSomething();

    else

        DoOtherthing();

    }

    if (condition)

    {

    for (i=0; i<N; i++)

        DoSomething();

    }

    else

    {

        for (i=0; i<N; i++)

        DoOtherthing();

    }

    (c) 逻辑判断在循环体内                (d) 逻辑判断在循环体外

    答:如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。示例(c)的程序比示例(d)多执行了N-1次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果N非常大,最好采用(d)的写法,可以提高效率。如果N非常小,两者效率差别并不明显,采用示例(c)的写法比较好,因为程序更加简洁。

     

    3. 运算符的优先级

           C++/C语言的运算符有数十个,运算符的优先级与结合律下表所示。注意一元运算符 +  -  * 的优先级高于对应的二元运算符。

     

    优先级

    运算符

    结合律

     

     

     

     

     

     

     

    ( )  [ ]  ->  .

    从左至右

    !  ~  ++  --  (类型) sizeof

    +  -  *  &

    从右至左

     

    *  /  %

    从左至右

    +  -

    从左至右

    <<  >>

    从左至右

    <   <=   >  >=

    从左至右

    ==  !=

    从左至右

    &

    从左至右

    ^

    从左至右

    |

    从左至右

    &&

    从左至右

    ||

    从右至左

    ?:

    从右至左

    =  +=  -=  *=  /=  %=  &=  ^=

    |=  <<=  >>=

    从左至右

    运算符的优先级与结合律

     

     

     

     

    展开全文
  • 赋值语句 注释,用(--)来表示 定义,lua中没有定义...Lua可以对多个变量同时赋值,变量列表值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。a, b = 10, 2*x a=10; b=2*x  遇到赋值语句
  • 6.2 表达式和语句

    2020-07-06 15:27:13
    在前几章中,我们已经多次使用了术语表达式(expression)和语句 (statement)。现在,我们来进一步学习它们。C的基本程序步骤由语句组 成,而大多数语句都由表达式构成。因此,我们先学习表达式。 1、表达式 ...
  • 变量和赋值语句

    千次阅读 2018-10-27 11:48:54
    赋值语句 statements 符号 = 语法: 变量名 = 表达式 或 变量名1 = 变量名2 = 表达式 或 变量名1, 变量名2, ... = 序列 作用: 创建一个变量并绑定数据对象 改变一个变量的绑定的数据对象 示例: a = 10 b = 20 ...
  • 表达式和语句

    2009-05-28 18:43:00
    在C++中语句控制程序的执行顺序、计算表达式的值或什么都不做(空语句)。所有的C++语句都以分号结尾。在C++中任何结果为一个值的东西都是表达式表达式总是返回一个值。语句3+2;返回5,因此它是表达式。所有表达式...
  • 表达式: 就是有一个或多个操作数或0个以上运算符组成的序列就叫做表达式表达式里面可以包含文本值,方法调用,运算符,操作数甚至是一些简单名称。 语句: 执行流或控制流。是以分号结尾的单行代码,即用来实现...
  • 04运算符,表达式和语句 一、运算符 1单目运算符 +(正号运算符) ...赋值表达式和赋值语句: 赋值表达式的末尾没有分号,而赋值语句有分号 一个表达式可以包含赋值表达式,但决不能包含赋值语句 复合赋值运算
  • 46-C++表达式和语句

    2018-09-04 14:32:25
    表达式和语句 for语句的控制部分使用3个表达式。由于其自身加强的句法限制,C++成为非常具有表现力的语言,任何值或任何有效的值运算符的组合都是表达式。例如,10是值为10的表达式(一点都不奇怪),28*20是值为...
  • 七、运算符与表达式   1.运算符的分类 2.各种运算符的使用方法 六类: 算数运算符(+,-,*,/,%(求余数),++,--);注意:int a = 3/2; (a的值为1,因为a为整型,去除小数点后面的数字);i++,赋值...
  • 由算术运算符运算对象构成的表达式即为算术表达式。 例如, 以上都属于算术表达式。 在执行算术表达式的时候,需要考虑算术运算符的优先顺序,负号(-)与正号(+)的优先级最高,其次为乘(*)、除(/)、取余(%),最后是...
  • 1.语句和表达式 JavaScript中的表达式和语句是有区别的.一个表达式会产生一个值,它可以放在任何需要一个值的地方,比如,作为一个函数调用的参数.下面的每行代码都是一个表达式: myvar3 + xmyfunc("a", "b")...
  • 第4章 语句和表达式;主要内容;4.1 语句与表达式;4.1 语句与表达式;4.1 语句与表达式;4.2 赋值语句;4.2 赋值语句;4.3 算术运算与赋值;4.3 算术运算与赋值;4.3 算术运算与赋值;4.3 算术运算与赋值;4.3 算术运算与赋值;...
  • C#语言—表达式与基本语句

    千次阅读 热门讨论 2017-02-23 11:35:15
    以及如何将这些表达式灵活地引用一些if 语句,for语句。接下来,小编通过传智播客视频的学习,总结出如下内容,与大家交流。 表达式 一、概念 1.表达式是运算符操作数的字符串。 2.表达式可以由许多嵌套的子...
  • 过程赋值语句的基本形式: 寄存器变量 = 表达式 考虑赋值过程的定时控制时,根据定时控制在过程赋值语句中的不同位置,存在两类
  • 一、表达式分为简单表达式和复杂表达式 1、简单表达式:最简单的表达式只包含单独的操作数:一个简单变量、字面常量符号常量 PI:程序中定义的符号常量 20:字面常量 rate:变量; -1.24:字面常量 2、复杂...
  • 设计出给定源语言中包含有算术表达式、关系表达式和逻辑表达式的赋值语句的文法,文法满足采用的语法分析方法的要求。 选择最有代表性的语法分析方法,如算符优先法(或简单优先法)、递归下降分析法、LL分析法LR...
  • 蓝鸥iOS培训讲师推荐:记得在哪好像说过有关C++赋值语句了,但是不记得是在哪了,没关系反正有时间,今天就为大家再...不过在其它很多语言中赋值语句不能这样写,是不对的2、对于赋值表达式和赋值语句的概念,在C...
  • 一、要能够熟练区分运算符、表达式和语句  运算符与表达式  Java提供了丰富的运算符,如算术运算符、关系运算符、逻辑运算符、位运算符等。  算术运算符与算术表达式 1.加减运算符: +,-  加减运算符是二目...
  • 表达式语句

    2020-01-23 20:02:04
    *表达式 是由运算符运算数组成的,单独的一个运算数...包括:赋值表达式语句,函数表达式语句,空语句,复合语句(由花括号括起来的一条或多条语句语句&指令的关系语句经编译器编译后变为指令(前者是...
  • Python的表达式和语句、优先级、对象引用计数 表达式表达式表达的是某件事,由值、变量运算符组成的。它是由一个或多个操作数以及0个或0个以上的运算符组成的序列。 语句语句表达的是做某件事,它是以分号...
  • Python表达式和语句之间的区别

    千次阅读 2018-11-27 11:51:54
    Python代码由表达式和语句组成,并由Python解释器负责执行。它们的主要区别是“表达式”是一个值,它的结果一定是一个Python对象。当Python解释器计算它时结果可以是任何对象。例如42,1+2,int(‘123’),range(10)...
  • python的赋值语句 statement

    千次阅读 2018-08-10 11:38:04
    赋值语句 statement 符号 = 语法: 变量名 = 表达式 或 变量名1 = 变量名2 = 表达式 或 变量名1, 变量名2, ... = 序列 作用: 用于将一个变量绑定(或关联)在一个对象上 说明: 1. 当变量不存在时,创建...
  • C++中赋值语句的执行顺序

    千次阅读 2013-05-08 14:07:06
    (1)C++的赋值语句具有其他高级语言的赋值语句的功能。但不同...(2) 关于赋值表达式赋值语句的概念。在C++中,赋值表达式可以包括在其他表达式之中,例如  if((a=b)>0) cout0" 按语法规定if后面的( )内是一个条件。现
  • java的运算符基本与c语言相似,算术运算符是+,-,*,/和%... java的语句有六种,分别是方法调用语句,表达式语句,复合语句,空语句,控制语句,package语句和import语句。其中一些常用语句有if,if-else,if-else i
  • 提示:本文是对C语言基础知识语句、操作符和表达式部分的回顾总结...C语言并不存在专门的“赋值语句”,赋值符号“=”就是一种操作符号,赋值就在表达式内进行。 代码块 :位于一对 花括号 之内的可选的声明语句列表
  • 常用于测试文件的编写,用来产生仿真测试信号(激励信号),或者用于对存储器变量赋值。 always语句一直在不断地重复活动。但是只有一定的时间控制结合在一起才有作用。 //给输入信号初始值 initial begin sys_...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,258
精华内容 46,903
关键字:

关系表达式和赋值语句