《C语言程序设计》第三章 基本算术运算

目录:

3.1  C运算符和表达式

  • 3.1.1  算术运算符和表达式
  • 3.1.2  复合的赋值运算符
  • 3.1.3  增1和减1运算符

3.2  宏常量与宏替换

3.3  const常量

3.4  自动类型转换与强制类型转换运算符

3.5  常用的标准数学函数

3.6  本章知识点小结

3.7  本章常见错误小结


3.1.1  算术运算符和表达式

  •  算术运算符的优先级与结合性
运算符 含义

需要的操作

数个数

运算实例

运算

结果

优先级 结合性
-

取相反数

(Opposite Number)

1个(一元)

-1

-(-1)

-1

1

最高 从右向左

*

/

%

乘法(Multiplication)

除法(Division)

求余(Modulus)

2个(二元)

12/5

12.0/5

11%5

11.0%(-5)

(-11)%5

2

2.4

1

1

-1

较低 从左向右

+

-

加法(Addition)

减法(Subtraction)

2个(二元)

5+1

5-1

6

4

最低 从左向右
  • 算术表达式:由算术运算符及其操作数组成的表达式;操作数也称为运算对象,它可以是常量、变量、函数。
  • 只需一个操作数的运算符称为一元运算符(或单目运算符),需要两个操作数的运算符称为二元运算符(或双目运算符),需要三个操作数的运算符称为三元运算符(或三目运算符)。条件运算符是C语言提供的唯一一个三元运算符。
  • 不同于数学中的算术运算,C语言中的算术运算的结果与参与运算的操作数类型相关。以除法运算为例,两个整数相除后的商仍为整数。例如,1/2与1.0/2运算 的结果值是不同的,前者是整数除法,后者则是浮点数除法。eg.12/5=2, 12.0/5.0(或者12/5.0,或者12.0/5)=2.4。
  • 在C语言中,求余运算限定参与运算的两个操作数必须为整型,不能对两个实型数据进行求余运算。
  • 注意,C语言中没有幂运算符。
//计算并输出一个三位整数的个位、十位和百位数字之和。
#include<stdio.h>
int main(void)
{
    int x=153,b0,b1,b2,b3,sum;
    b2=x/100;            //百位
    b1=(x-b2*100)/10;    //十位
    b0=x%10;             //个位
    sum=b0+b1+b2;
    printf("b2=%d,b1=%d,b0=%d,sum=%d",b2,b1,b0,sum);
    return 0;
 } 

输出:
b2=1,b1=5,b0=3,sum=9

3.1.2  复合的赋值运算符

  • 涉及算术运算的复合赋值运算符有5个:+=,-=,*=,/=,%=

例如:n*=m+1等价于n=n*(m+1),但不等价于n=n*m+1.

#include<stdio.h>
int main(void)
{
    int a=3;
    a+=a-=a*a;
    printf("a=%d",a);
    return 0;
 } 

输出:a=-12


#include<stdio.h>
int main(void)
{
    int a=3;
    a+=a-=a*=a;
    printf("a=%d",a);
    return 0;
 } 

输出:a=0

 

 

  • 增1和减1运算的例子(初始n=3)
    语句 等价的语句 执行该语句后m的值 执行该语句后n的值
    m=n++;

    m=n;

    n=n+1;

    3 4
    m=n--;

    m=n;

    n=n-1;

    3 2

    m=++n;

    n=n+1;

    m=n;

    4 4
    m=--n;

    n=n-1;

    m=n;

    2 2

 3.1.3  增1和减1运算符

语句         m=-n++;

等价于      m=-(n++);

进一步,等价于     m=-n;    n=n+1;

假使 int n=3,则m=-3,n=4.

  • 后缀形式与前缀形式的区别在于:后缀是先使用变量的值,然后再增1(减1);前缀是先增1(减1),然后再使用变量的值,并且后缀增1(减1)运算符的优先级高于前缀增1(减1)运算符,前者是左结合的,而后者是右结合的。

3.2  宏常量与宏替换

//计算并输出半径r=5.3的圆的周长和面积。
#include<stdio.h>
int main(void)
{
    double r=5.3;
    printf("circumference=%f\n",2*3.14159*r);
    printf("area=%f\n",3.14159*r*r);
    return 0;
 } 

输出:
circumference=33.300854
area=88.247263

若要做到一次编译,多次运行计算不同半径的圆的周长和面积,则每次运行时可以让用户从键盘输入圆的半径r的值,这就要用到C标准函数库中的函数scanf()。

//从键盘输入圆的半径r,计算并输出圆的周长和面积。
#include<stdio.h>
int main(void)
{
    double r;
    printf("Input r:");   //提示用户输入半径的值
    scanf("%lf",&r);      //以双精度实型从键盘输入半径的值
    printf("circumference=%f\n",2*3.14159*r);
    printf("area=%f\n",3.14159*r*r);
    return 0;
 } 

程序会在屏幕上显示:
Input r:
假使输入5.3并回车,则输出
Input r:5.3
circumference=33.300854
area=88.247263
  • scanf()函数用于从键盘输入一个数,%lf指定输入的数据类型应为双精度实型。第六行语句中r前面的&是必要的,&称为取地址运算符,&r指定了用户输入数据存放的变量的地址。
  • 幻数:在程序中直接使用的常数,如π,但幻数存在影响程序的可读性和可维护性的问题,常把幻数定义为宏常量或const常量。
#include<stdio.h>
#define PI 3.14159    //定义宏常量PI
int main(void)
{
    double r;
    printf("Input r:");
    scanf("%lf",&r); 
    printf("circumference=%f\n",2*PI*r);   /*编译时PI被替换成3.14159*/
    printf("area=%f\n",PI*r*r);
    return 0;
 } 
  • 宏常量:也称符号常量,是指用一个标识符号来表示的常量,这时该标识符号与此常量等价。宏常量是由宏定义编译预处理命令来定义的。
  • 宏定义的一般形式为:#define 标识符   字符串,其作用是用#define编译预处理指令定义一个标识符和一个字符串,凡在源程序中发现该标识符时,都由其后指定的字符串来替换。
  • 宏名:宏定义中的标识符,习惯用字母全部大写的单词来命名;宏替换:将程序中出现的宏名替换成字符串的过程。
  • 注意:宏定义中的宏名与字符串之间可有多个空白符,但无需加等号,且字符串后一般不以分号结尾,因为宏定义不是C语句,而是一种编译预处理命令。

3.3  const常量

宏常量的缺陷:宏常量没有数据类型,字符串替换时极易产生错误。

  • const常量:可以声明具有某种数据类型的常量。在声明语句中,形式为:const 类型  常量名 = 初始值;    例如:const  double PI = 3.14159;
//使用const常量定义π,编程从键盘输入圆的半径r,计算并输出圆的周长和面积。
#include<stdio.h>
int main(void)
{
    const double PI=3.14159;   //定义实型的const常量PI 
    double r;
    printf("Input r:");
    scanf("%lf",&r);
    printf("circumference=%f\n",2*PI*r);
    printf("area=%f\n",PI*r*r);
    return 0;
 } 

3.4  自动类型转换与强制类型转换运算符

  1.  表达式中的自动类型转换
  • 类型提升:C编译器在对操作数进行运算之前将所有操作数都转换成取值范围较大的操作数类型,可以避免数据信息丢失情况的发生。
  • 整数提升:类型提升的规则如下图所示,纵向箭头表示必然的转换,即将所有的char和short都提升为int的步骤称为整数提升。

                            float                                                                           char,short

                              ↓                                                                                     ↓

long double  ←  double  ←  unsigned long  ←  long  ←  unsigned int  ←  int

高 ←—————————————————————————————— 低

  • 上图横向箭头表示不同类型的操作数进行混合运算时由高到低的类型转换方向,但不代表转换的中间过程。
  • 例如,一个int型操作数与一个 float型操作数进行算术运算,则在对其进行运算之前要先将 float型操作数自动转换为 double型,并将int型操作数转换成 double型(注意,无须经过int型先转换为 unsigned int再转换成long型,再转换为 unsignedlong型,再转换为 double型的过程)。一个特例是:如果一个操作数是long型,另一个是unsigned型,同时 unsigned型操作数的值又不能用long型表示,则两个操作数都转换成 unsigned long型。

        2.赋值中的自动类型转换

  • 在一个赋值语句中,若赋值运算符左侧(目标侧)变量的类型和右侧表达式的类型不一致,则赋值时将发生自动类型转换自动类型转换。类型转换的规则是:将右侧表达式的值转换成左侧变量的类型。
  • 自动类型转换虽然给取整等某些特殊运算带来方便,但在某些情况下有可能会发生数据信息丢失、类型溢出等问题。

        3.强制类型转换运算符

  • 强制类型转换运算符简称强转运算符或转型运算符,它的主要作用是将一个表达式值的类型强制转换为用户指定的类型,它是一个一元运算符,与其他一元运算符具有相同的优先级。通过下面方式可以把表达式的值转为任意类型:(类型)  表达式
  • 强转与指针,可并称为C语言的两大神器,必须恰当使用。
//演示强制类型转换运算符的使用。 
#include<stdio.h>
int main(void)
{
    int m =5;
    printf("m/2=%d\n",m/2);
    printf("(float)(m/2)=%f\n",(float)(m/2));
    printf("(float)m/2=%f\n",(float)m/2);
    printf("m=%d\n",m);
    return 0;
 } 

输出:
m/2=2
(float)(m/2)=2.000000
(float)m/2=2.500000
m=5
分析:
表达式m/2是整数除法,其运算结果仍为整数,因此输出的第一行结果值是2;
表达式(float)(m/2)是将表达式(m/2)整数相除的结果值(已经舍去了小数位)强转为实型数(在小数位添加了0),因此输出的第二行结果值是2.000000,可见这种方法并不能真正获得m与2相除后的小数部分的值;
表达式(float)m/2是先用(float)m将m的值强转为实型数据,然后再将这个实型数据与2进行浮点数除法运算,因此输出的第三行结果值是2.500000;
由于(float)m只是将m的值强转为实型数据,但是它并不改变变量m的数据类型,因此输出的最后一行结果值仍然是5.

3.5  常用的标准数学函数

/*已知三角形的三边长为a、b、c,计算三角形面积的公式为:area=√s(s-a)(s-b)(s-c),其中s=(a+b+c) /2
试编程从键盘输入a、b、c的值(假设a、b、c的值可以保证其构成一个三角形),计算并输出三角形的面积*/
#include<stdio.h>
#include<math.h>
int main(void)
{
    float a,b,c,s,area;
    printf("Input a,b,c:");
    scanf("%f,%f,%f",&a,&b,&c);
    s=(a+b+c)/2;
    area=(float)sqrt(s*(s-a)*(s-b)*(s-c));
    printf("area=%f\n",area);
    return 0;
 } 

输出:

Input a,b,c:3,4,5↙
area=6.000000

  • 分析:当a、b、c被定义为整型变量时,将数学公式s=(a+b+c)/2写成如下各种形式的C语言表达式: s=0.5*(a+b+c)     s=1.0/2*(a+b+c)     s=(a+b+c)/2.0     s=(float)(a+b+c)/2  都是正确的。而如果写成s=1/2*(a+b+c) 或者 s=(float)((a+b+c)/2) 虽无语法错误,但计算结果错误。
  • 注意,如果将第5行的变量改为double类型,那么第9行的sqrt()的返回值无需强转为float,这样计算的结果会比float计算的结果精度更高,导致输出结果中的小数后的某些位有可能不一样。
  • 使用数学函数时,只要在程序开头加上如下的编译预处理命令即可:
    #include<math.h>

 

3.6  本章知识点小结


3.7  本章常见错误小结

常见错误实例 常见错误描述 错误类型
2*π*r 表达式中使用了非法的标识符 编译错误
4ac 或者 4×a×c 将乘法运算符*省略,或者写成× 编译错误
1.0/2.0+[a-b]/{a-b}

使用方括号“[”和“]”以及花括号“{”和“}”限定表

达式运算顺序

编译错误
sinx

使用数学函数运算时,未将参数用圆括号括起来,且

未注意到其定义域要求和参数的单位

编译错误
3.5%0.5 对浮点数执行求余运算 编译错误
1/2 误将浮点数除法当做整数除法 运行时错误
float(m)/2 强转表达式中的类型名未用圆括号括起来 运行时错误

误以为(float)m这种强制运算可以改变变量m的

类型和数值

理解错误

误以为用双括号括起来的字符串中与宏名相同的字

符也被宏替换,误以为宏替换时可以做语法检查

理解错误
误以为三角函数中的角的单位是角度 理解错误
#define PI = 3.14159;

将宏定义当做C语句来使用,在行末加上了分号,或

者在宏名后加上了“=”

编译错误
+=,-=,*=,/=,%=

将复合的赋值运算符+=,-=,*=,/=,%=的两个字符

中间加入了空格

编译错误
(a+b)++ 对一个算术运算符使用增1或者减1运算 编译错误
posted @ 2021-01-26 23:02  油菜园12号  阅读(1258)  评论(1编辑  收藏  举报