5.3其他运算符
其他运算符包括:sizeof运算符和size—t类型、求模运算符、递增运算符、递减运算符等。
5.3.1 sizeof运算符和size—t运算符
sizeof运算符以字节为单 位返回运算对象的大小(在C中,1字节定义为char类型占用的空间大小)。
在过去,1字节(byte)通常是8位,但是一些字符集可能使用更大的字节。
注意:1.运算对象可以是具体的数据对象(如,变量名)或类型。
2.如果运算对象是类型(如, float),则必须用圆括号将其括起来。
下列示例演示了sizeof运算符的两种用法:
C 语言规定,sizeof 返回 size_t 类型的值。这是一个无符号整数类型, 但它不是新类型。
size_t是语言定义的标准类型。C有一个 typedef机制,允许程序员为现有类型创建别名。
例如:
typedef double real;
在上述示例的操作中,real就为double的别名,并且可以在程序中声明一个real类型的变量:
real deal;
编译器查看real时会发现,在typedef声明中real已成为double的别名,于 是把deal创建为double 类型的变量。
那么类似地看,在上述的示例程序中,C 头文件系统可以使用 typedef 把 size_t 作为 unsigned int 或unsigned long的别名。这样,在使用size_t类型 时,编译器会根据不同的系统替换标准类型。
在C语言标准中,C99新增了%zd转换说明可用于printf()显示size—t类型的值。如果系统不支持%zd,可用%u、%lu代替%zd。
5.3.2求模运算符:%
求模运算符(modulus operator)用于整数运算。求模运算符给出其左侧 整数除以右侧整数的余数(remainder)。例如:13%5(13求模5)得数为3,因为13除以5的余数得3。
注意:1.求模运算符只能用于整数类型,不能用于浮点数类型或者混合类型。
2.求模运算符的用途:求模运算符在C语言编程中非常有用,它可以并通常用于控制程序流。
下列程序示例展示出了求模运算符得另一种用法及while循环得另一种用法:
该程序的输出结果如下:
程序清单5.9则通过scanf()为变量sec获取一个新值。只 要该值为正,循环就继续。当用户输入一个0或负值时,循环退出。
这两种情况设计的要点是,每次循环都会修改被测试的变量值。
负数求模
C99规定“趋零截断”之前,该问题的处理方法很多。但自从有了这条规则之后,如果第1个运算对象是负数,那么求模的结 果为负数;如果第1个运算对象是正数,那么求模的结果也是正数。
例如:
注意:**
1.如果当前系统不支持C99标准,会显示不同的结果。
2.实际上,标准规 定:无论何种情况,只要a和b都是整数值,便可通过a - (a/b)*b来计算a%b。
例如,用此方法计算-11%5:
5.3.3递增运算符:++
递增运算符执行简单的任务,将其运算对象递增1。
该运算符出现的两种形式:
1.++出现在其作用的变量前面, 这是前缀模式;
2.++出现在其作用的变量后面,这是后缀模式。
前缀与后缀的区别:两种模式的区别在于递增行为发生的时间不同。
下列示例演示递增运算符如何工作:
运行该程序后,输出结果如下:
上述程序的结果与用下列两条语句分别代替程序中的两条递增语句,程序输出结果相同:
创建递增运算符的原因:
1.紧凑结构的代码让程序更为简洁,可读性更高。
2.它把控制循环的两个过程集中在一个地方。该循环的主要过程是判断是否继续循环 (下例中,要检查鞋子的尺码是否小于 18.5),次要过程是改变待测试的元 素(下例中是递增鞋子的尺码)。
3.通常它生成的机器语言代码效率更高,因 为它和实际的机器语言指令很相似。(在未来可能因为商家推出的编译器越来越智能,而使得其消失)
4.最后,递增运算符还有一个在某些场合特别有用的特性。
在上例中,把循环测试和更新循环放在一处,就不会忘记更新循 环。 但是,把两个操作合并在一个表达式中,降低了代码的可读性,让代码难以理解。而且,还容易产生计数错误。
注意:递增运算符还有一个在某些场合特别有用的特性。如下例:
输出结果为:
由此可得,a和b都递增了1,但是,a_post是a递增之前的值,而b_pre是b递增之后 的值。
这就是++的前缀形式和后缀形式的区别。
注意:单独使用递增运算符时(如,ego++;),使用哪种形式都没关系。但 是,当运算符和运算对象是更复杂表达式的一部分时(如上面的示例),使用前缀或后缀的效果不同。**
在使用递增运算符时,能否互换使用前缀和后缀形式,或者当前环境只能使用某种形式。
如果使用前缀形式和后缀形式会对代码产生不同的影响,那么最为明智的是不要那样使用它们。例如:
小心仔细思考在不同场合下使用递增运算符的前缀形式与后缀形式!!!**
5.3.4递减运算符:--
每种形式的递增运算符都有一个递减运算符与之对应,用--代替++即可,即递减运算符也有前缀和后缀。递减运算符也是用于将其对象递减1。
递减运算符的两种形式:
该程序输出结果如下:
在上述示例程序中,>运算符表示“大于”,<运算符表示“小于”,它们都是关系运算符。
5.3.5优先级
递增运算符和递减运算符都有很高的结合优先级,只有圆括号的优先级比它们高。
因此,,x*y++表示的是(x) *(y++),而不是(x+y)++。不过后者无 效,因为递增和递减运算符只能影响一个变量(或者,更普遍地说,只能影 响一个可修改的左值),而组合x *y本身不是可修改的左值。
递增、递减运算符的优先级与求值顺序的区别:
根据优先级的规定,可以判断某一运算符作用的运算对象,优先级可以判断何时使用n的值对表达 式求值,而递增、递减运算符的性质决定了何时递增或递减运算对象的值。
注意:递增运算符和递减运算符前缀与后缀的区别:
如果n++是表达式的一部分,可将其视为“先使用n,再递增”;而++n则表示“先递增n,再使用”。与此规则相似,递减运算符的前缀与后缀形式的用法。
5.3.6不要自作聪明
1.不要一次性使用太多运算符。如果一次用太多递增运算符,自己都会糊涂。例如可将程序清单5.4中的while循环换为下列循环:
但是问题在于:1.事实上,修改后的程序只能在某些系统上能正常运行。该 程序的问题是:当 printf()获取待打印的值时,可能先对最后一个参数( ) 求值,这样在获取其他参数的值之前就递增了num。
得到结果1:
问题2.:它甚至可能从右往左执行,对最右边的num(++作用的num)使用5,对 第2个num和最左边的num使用6。
得到结果2:
另外,在C语言中,编译器可以自行选择先对函数中的哪个参数求值。这样做提高了编译器的效率,但是如果在函数的参数中使用了递增运算符,就会有一些问题。
类似下列示例:
问题1.:编译器可能不会按预想的顺序来执行。你可能 认为,先计算第1项(num/2),接着计算第2项(5*(1 + num++))。但是, 编译器可能先计算第2项,递增num,然后在num/2中使用num递增后的新 值。因此,无法保证编译器到底先计算哪一项。
存在同样问题的还有一类,如:
类似的语句,执行完后n的值会比旧值大2。但是,y的 值不确定。在对y求值时,编译器可以使用n的旧值(3)两次,然后把n递增 1两次,这使得y的值为6,n的值为5。或者,编译器使用n的旧值(3)一 次,立即递增n,再对表达式中的第2个n使用递增后的新值,然后再递增n, 这使得 y 的值为 7,n 的值为 5。两种方案都可行。对于这种情况更精确地 说,结果是未定义的,这意味着C标准并未定义结果应该是什么。
因此,为避免类似的问题,要遵循下列规则:
1.如果一个变量出现在一个函数的多个参数中,不要对该变量使用递增或递减运算符;
2.如果一个变量多次出现在一个表达式中,不要对该变量使用递增或递减运算符。