C Primer Plus(6) 中文版 第5章 运算符、表达式和语句 5.2 基本运算符

5.2 基本运算符
C用运算符(operator)表示算术运算。
基本运算的运算符:=、+、-、*、/(C没有指数运算符。不过,C的标准数学库提供了一个pow()函数用于指数运算)。
5.2.1 赋值运算符
在C语言中,=是一个赋值运算符。
赋值运算符左边是变量,右边是待赋值的值。 
赋值行为是从右往左进行。
在C语言中,类似这样的语句没有意义(实际上是无效的): 
2002 = bmw;
因为在这种情况下,2002被称为右值(rvalue),只能是字面常量,不能给常量赋值,常量本身就是它的值。
实际上,赋值运算符左侧必须引用一个存储位置。最简单的方法就是使用变量名。不过“指针”也可用于指向一个存储位置。概括地说,C使用可修改的左值(modifiable lvalue)标记那些可赋值的实体。
几个术语:数据对象、左值、右值和运算符
赋值表达式语句的目的是把值存储到内存位置上。用于存储值的数据存储区域统称为数据对象(data object)。C标准只有在提到这个概念时才会用到对象这个术语。使用变量名是标识对象的一种方法。当然,还有其他方法。例如,可以指定数组的元素、结构的成员,或者指针表达式(指针中存储的是它所指向对象的地址)。
左值(lvalue)是C语言的术语,用于标识特定数据对象的名称或者表达式。因此,对象指的是实际的数据存储,而左值是用于标识或定位存储位置的标签。
对于早期的C语言,提到左值意味着:
1.它指定一个对象,可以引用内存中的地址;
2.它可用在赋值运算符的左侧,左值(lvalue)中的1源自left。
但是后来,标准中新增了const限定符。用const创建的变量不可修改。因此,const标识符满足第1项,但是不满足第2项。一方面C继续把标识对象的表达式定义为左值,一方面某些左值却不能放在赋值运算的左侧。
为此,C标准新增了一个术语:可修改的左值(modifiable lvalue),用于标识可修改的对象。所以,赋值运算符的左侧应该是可修改的左值。当前标准建议,使用术语对象定位值(object locator value)更好。
右值(rvalue)指的是赋值给可修改变量的值,且本身不是左值。
右值中的r源自right。右值可以是常量、变量或其他可求值的表达式。实际上,当前标准在描述这一概念时使用的是表达式的值(value of an expression),而不是右值。
可修改的左值可用于赋值运算符的左侧和右侧。
const int TWO = 2;
这里的=运算符表示初始化而不是赋值。
int why;
int zee;
表达式why + zee是右值,该表达式不能表示特定内存位置,而且也不能给它赋值。它只是程序计算的一个临时量,在计算完毕后便会被丢弃。
在学习名称时,被称为“项”(如,赋值运算符左侧的项)的就是运算对象(operand)。运算对象是运算符操作的对象。
C的基本运算符有些与众不同。 
/* golf.c -- golf tournament scorecard */
#include <stdio.h>
int main(void)
{
    int jane, tarzan, cheeta;
    
    cheeta = tarzan = jane = 68;
    printf("                  cheeta   tarzan    jane\n");
    printf("First round score %4d %8d %8d\n",cheeta,tarzan,jane);
    
    return 0;
}

/*输出:

*/

5.2.2 加法运算符:+
加法运算符(addition operator)用于加法运算,使其两侧的值相加。
相加的值(运算对象)可以是变量,也可以是常量。
5.2.3 加法运算符:-
减法运算符(substraction operator)用于减法运算,使其左侧的数减去右侧的数。
+和-运算符都被称为二元运算符(binary operator),即这些运算符需要两个运算对象才能完成操作。
5.2.4 符号运算符
减号还可用于表明或改变一个值的代数符号。
rocky = -12;
smokey = -rocky;
以这种方式使用的负号被称为一元运算符(unary operator)。一元运算符只需要一个运算对象。
C90标准新增了一元+运算符,它不会改变运算对象的值或符号,只能这样使用:
dozen = +12;
5.2.5 乘法运算符:*
符号*表示乘法。
C没有平方函数,打印一个平方表。
/* squares.c -- produces a table of first 20 squares */
#include <stdio.h>
int main(void)
{
    int num = 1;
    
    while (num < 21)
    {
        printf("%4d %6d\n", num, num * num);
        num = num + 1;
    }
    
    return 0;

/* 输出:

*/

1.指数增长
/* wheat.c -- exponential growth */
#include <stdio.h>
#define SQUARES 64             // squares on a checkerboard
int main(void)
{
    const double CROP = 2E16;  // world wheat production in wheat grains
    double current, total;
    int count = 1;
    
    printf("square     grains       total     ");
    printf("fraction of \n");
    printf("           added        grains    ");
    printf("world total\n");
    total = current = 1.0; /* start with one grain   */
    printf("%4d %13.2e %12.2e %12.2e\n", count, current,
           total, total/CROP);
    while (count < SQUARES)
    {
        count = count + 1;
        current = 2.0 * current;
        /* double grains on next square */
        total = total + current;     /* update total */
        printf("%4d %13.2e %12.2e %12.2e\n", count, current,
               total, total/CROP);
    }
    printf("That's all.\n");
    
    return 0;

/* 输出:

*/

5.2.6 除法运算符:/
C使用符号/来表示除法。/左侧的值是被除数,右值的值是除数。
整数除法和浮点数除法不同。浮点数除法的结果是浮点数,而整数除法的结果整数。在C语言中,整数除法结果的小数部分被丢弃,这一过程被称为截断(truncation)。
/* divide.c -- divisions we have known */
#include <stdio.h>
int main(void)
{
    printf("integer division:  5/4   is %d \n", 5/4);
    printf("integer division:  6/3   is %d \n", 6/3);
    printf("integer division:  7/4   is %d \n", 7/4);
    printf("floating division: 7./4. is %1.2f \n", 7./4.);
    printf("mixed division:    7./4  is %1.2f \n", 7./4);
    
    return 0;

/* 输出:

*/

一般情况下还是要避免使用混合类型。
混合整数和浮点数计算的结果是浮点数。实际上,计算机不能真正用浮点数除以整数,编译器会把两个运算对象转换成相同的类型。因此,在进行除法运算前,整数会被转换成浮点数。
C99标准以前,C语言给语言的实现者留有一些空间,让他们来决定如何进行负数的整除除法。一种方法是,舍入过程采用小于或等于浮点数的最大整数。但是,另一种舍入方法是直接丢弃小数部分。这种方法被称为“趋零截断”。在C99之前,不同的实现采用不同的方法。但是,C99规定使用趋零截断。
5.2.7 运算符优先级
C语言对此有明确的规定,通过运算符优先级来解决操作顺序的问题。每个运算符都有自己的优先级。乘法和除法的优先级比加法和减法高,所以先执行乘法和除法。如果两个运算符相同,且它们处理同一个运算对象,则根据它们在语句中出现的顺序来执行。
对大多数运算符而言,这种情况都是按从左到右的顺序进行(=运算符除外)。
许多人喜欢用表达式树(expression tree)来表示求值的顺序。
可以通过添加圆括号改变执行顺序。
             表5.1 运算符优先级(从高到低)
运算符        结合律
()               从左往右
+ -(一元)    从右往左
* /               从左往右
+ -(二元)    从左往右
=                从右往左
5.2.8 优先级和求值顺序
运算符优先级为表达式求值顺序提供了重要的依据,但是并没有规定所有的顺序。
y = 6 * 12 + 5 * 20; 
当运算符共享一个运算对象是,优先级决定了求值顺序。但是,优先级并未规定到底先进行哪一个乘法。C语言把主动权留给语言的实现者,根据不同的硬件来决定先计算前者还是后者。无论采用哪种方案,表达式都会简化为72+100,所以这并不影响最终的结果。结合律只适用于共享同一运算对象的运算符。
/* rules.c -- precedence test */
#include <stdio.h>
int main(void)
{
    int top, score;
    
    top = score = -(2 + 5) * 6 + (4 + 3 * (2 + 3));
    printf("top = %d, score = %d\n", top, score);
    
    return 0;

/* 输出:

*/

首先,圆括号的优先级最高。先计算-(2 + 5)还是(4 + 3 * (2 + 3))取决于具体的实现。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_40186813

你的能量无可限量。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值