跳转至

二、表达式与简单程序设计

对于表达式和简单程序设计的讲解,此文章主要会以一些简单案例来引入,案例中将囊括基本的理论和术语,最后会简要介绍简单程序设计的原理和方法。

表达式

操作符和操作数

在C语言中,表达式是由操作符和操作数而形成的式子

操作符 执行某种操作的语法符号,又称运算符 C语言中的操作符多数同于大家熟悉的数学运算符号 如表示相乘的符号“ * ” 表示相加的符号“ + ”

四则运算 C语言运算符 意义
+ + 加
- - 减
× * 乘
÷ / 除
% 取余
() () 括号

注:" % "符号是取余数 即9%6=3

对于大于等于以及小于等于比较的符号略有不同,但也仅是将两个符号进行简单组合

比较运算 C语言运算符
大于 >
小于 <
等于 ==
大于等于 >=
小于等于 <=

操作数 执行操作的对象,一个运算符可以有一个或多个操作数

a = b + 3;
8+5;
'A';

//对于上面的代码 //第一行的“ a " " b " " 3 " 均为操作数,“ + ” “ = ” 为操作符 //第二行的“ + ”为操作符 “ 8 ” “ 5 ”为操作数

C语言中,根据操作符和操作数的数量和位置的不同,划分出7钟不同类型的表达式,在此不做一一展示,具体的分类可查阅资料

我们可以以一个简单的找零计算器程序进行演示

#include<stdio.h>
int main(){
    int myYuan,myJiao;// 现金的元单位数和角单位数
    int waresYuan,waresJiao;//商品售价的元单位数 角单位数

    printf("please enter two numbers: ");
    scanf("%d%d",&myYuan,&myJiao);//输入现金
    printf("please enter two numbers: ");
    scanf("%d%d",&waresYuan,&waresJiao);//输入商品价格

    //思考一下,如果这件商品的价格是1元8角,手中的现金数是2元5角
    //如果直接分别减就会出现角借位的情况
    //为了解决错位的问题,我们当然可以统一单位进行相减
    int t1 = myYuan*10+myJiao;//将手中现金转化为角单位
    int t2 = waresYuan*10+waresJiao;//将商品价格化为角单位

    int t = t1 - t2;//将两个相同单位的数进行相减
    //此时我们将得到一个角为单位的数值
    //我们可以把它化为几元几角的方式,这样更符合我们的认知
    //这时我们将用到前面介绍的取余运算符,即“ % ” 
    //t/10->表示元单位
    //t%10->表示角单位

    printf("应找零%d元%d角",t/10,t%10);//输出结果

}

聪明的同学当然会想到,如果钱不够怎么办 当然我们可以用C语言的程序来判断,只不过这暂时不是现在学习的内容,等到第三章也就是下一章 分支程序设计 的时候将会解决这个问题 现在我们先就假设现金数大于商品价格

运算符优先级

在C语言的表达式中,运算符是有优先级的,它规定了在一个表达式中,各个运算符执行的先后次数

我们可以用一个简单的求平均值的程序来引入

#include<stdio.h>
int main(){
    int a,b;
    scanf("%d %d",&a,&b);
    //这里定义了一个单精度浮点型变量c,用来存储计算后的数值
    //为什么是浮点型而不是整型呢,由于我们是计算两个数的平均数,我们当然不能保证这两个数的平均数一定是整数
    //同时 在C语言中,两个整型相运算,只会得到一个整型,并且计算机会自动向下取整 即8/3会等于2
    //而整型与浮点型相运算会得到浮点型,这其实是C的 强制类型转换 至于它什么是,接下来的内容会讲到

    //我们在计算a+b时,使用了(),我们在小学数学时就知道a+b是加法,它的优先级要比除法要低
    //所以我们在计算时,为了满足我们的需求,我们会这样做
    double c = (a+b) / 2.0;

    printf("%f",c);
}

下面是常用的运算的符优先级

优先级 运算符 意义 举例
1 + 单目不变 +b
1 - 单目取负 -b
2 * 乘 a*b
2 / 除 a/b
2 % 取余 a%b
2 + 加 a+b
3 - 减 a-b
4 = 赋值 a=b

结合关系

赋值运算符

a = b + 3;//把“ = ”左边的“ b + 3 ”的值赋给“ a ” a此时的值就是“ b + 3 ” 的值
a += 8;//+=是复合赋值 意为a = a + 8;
a -= b;
a *= c;

上面程序中出现的+= -= *= 均为复合赋值运算符,是简单赋值表达式的缩略形式

下面是常用的复合赋值运算符

复合赋值表达式 等价的简单赋值表达式
x*=y x=x*y
x/=y x=x/y
x+=y x=x+y
x-=y x=x-y

注意

  1. 赋值运算左边一定是变量,右边可以是任何形式的表达式
  2. 赋值运算的结果是把右边式子的值储存到变量里面
  3. 整个式子的值计算变量中储存的值

后缀表达式

  1. 函数调用 形如printf();其中函数名为操作数 函数后面的括号是运算符

对于函数调用 这里不做太多介绍 后面单独学到函数章节的时候会讲到

  1. 后置自增/自减
int a=10;
a++;
//此时a的值为11
//表达式的值为10
int b=10;
b--;
//此时b的值为9
//表达式的值为10

后缀表达式a++和a=a+1的作用是一样的,a++也可以看作对a=a+1的缩略,a的值会加1 后缀表达式a--和a=a-1的作用是一样的,a的值会减1

注意 “ a++ ”/“ a-- ”这个表达式的值是a的值本身 我们可以用一个简单的程序来展示一下

#include<stdio.h>
int main(){
    int a = 10;
    int x,y;
    x=a--;
    //此时 x的值为a执行 “a--” 之前的值也就是10 a的值为a-1也就是9
    y=a++;
    //此时 y的值为a执行 “a++” 之前的值也就是9  a的值为10
}

聪明的同学一定会想到,既然有a++那就会有++a 我们当然可以将它叫做前置自增/自减 我们接下来就会讲到

一元表达式

  1. 前置自增/自减

前面讲到后置自增/自减 这两者可以放在一起来理解

我们看下面的程序 简单来演示前置和后置

#include<stddio.h>
int main(){
    int a = 10;
    int b = 10;

    int x = a++;
    //按照后置自增 很容易可以知道 x=10 a=11
    int y = ++b;
    //前置的作用也相似于后置 b的值也会+1 b=11
    //不同的是 此时y的值将会是 b加1以后的值 也就是11
}

这样一来 前置/后置都可以很明白的理解了

  1. sizeof()运算

sizeof()运算用于测定对象所占用字节数

可以用以下程序来展示sizeof()函数的使用方法和作用

#include<stdio.h>
int main(){
    int a;
    float b;
    char c;

    sizeof(a);//a是整型变量 占用字节数为4 结果是4
    sizeof(b);//b是float型变量 占用字节数为4 结果的4
    sizeof(c);//c是char型变量 占用字节数为1 结果为1
    sizeof(9.8);//9.8是double型常量 占用字节数为8 结果为8
}

补充 C语言程序中浮点型常量的默认类型就是double类型 也就是双精度浮点型

副作用

借助下面的程序 简单介绍副作用的基本概念

#include<stdio.h>
int main(){
    int a = 2;
    int b = 3;
    int c = 4;

    //给出如下表达式

    a + b;//并没有引起任何变量的值产生变化 则认为此表达式无副作用
    ++a;//引起了变量a的数值加了1 变量a的值发生了变化 则认为此表达式有副作用
    a = b * c;//引起变量啊的数值变为了 b * c 的值 变量a的值发生了变化 则认为此表达式有副作用
}

多数资料书和教材中对副作用了类型做了分类 感兴趣的同学可以查阅 这里不做赘述

语句

语句就是程序中实现某种功能的指令,前面引用的所有程序,都是有一个个的语句组成

这里我们主要简单介绍 表达式语句 和 复合语句

分支语句 循环语句 后面章节会详细介绍

表达式语句

任意表达式末尾加上";",就构成了表达式语句

int a;
b = 8;
v;
;

这些都是表达式语句,包括单独的";"

复合语句

复合语句是由花括号=={}==括起来的语句,又叫语句块,由声明部分和语句部分组成

#include<stdio.h>
int main(){

    //这部分为声明区
    int a;
    int c;
    double n;

    //这部分为语句区
    a = 6;
    c = 'v';
    n = 7.6;

}

部分库函数

我们在上一章讲到输入和输出的时候,用到了两个标准库函数printf() scanf(),像这样的库函数还有很多 前面我们讲到了许多操作符,可以帮助我们解决一些简单的问题 但是对于一些复杂的运算,想平方根,C中并没有内置运算符,但是提供了一些库函数帮助我们解决问题

下面将简单介绍两种类型的库函数

数学函数

C中提供了许多数学函数,这里选取几个常用到的库函数,函数原型多数包含在头文件math.h中,想要了解其他的库函数可以查阅资料

#include<stdio.h>
//前面第一章讲到预处理命令,这里是预包含命令,我们使用某个库函数时,要加入包含该库函数的头文件
//我们要使用到的abs/labs的函数原型包含在头文件stdlib.h中
//fabs/pow/sqrt的函数原型包含在头文件math.h中
#include<stdlib.h>
#include<math.h>
int main(){

    int a = -1;
    int a1 = abs(a);
    //abs()是用来求int类型数据的绝对值的

    long b = -994722222;
    long b1 = labs(b);
    //labs()是用来求long类型数据的绝对值的

    double c = 4.55;
    double c1 = fabs(c);
    //fabs()是用来求double类型数据的绝对值的

    double x = 4.0;
    double y = 3,0;
    double f = pow(x,y);
    //pow()函数的作用是返回x的y次幂的结果,pow()函数的参数和返回值(简单理解就是计算结果,后面会详细介绍)均为double类型

    double p = 4.0;
    double q = sqrt(p);
    //sqrt()函数的作用是计算参数的平方根,参数和返回值均为double类型
}

简单介绍函数原型 返回值 参数 以pow()为例

函数原型为

double pow (double x,double y)

使用pow()函数时要严格按照函数原型来使用,不然编译器将会报错

前面的double为返回值类型,也就是执行函数得到的数值的类型 里面的double x,double y为参数类型,我们可以看到,pow()函数需要两个参数,类型均为double类型

标准库函数

在日常的应用中,可能会遇到需要用某个范围的随机数问题 在C中有两个标准库函数srand和rand配合产生随机数,它们的原型声明包含在头文件stdlib.h中

  1. 种子数函数srand()

函数原型为 void srand(unsigned seed) 根据前面提到对函数原型的介绍,srand()函数的返回值为void 也就是没有返回值,参数为一个unsigned类型的变量或常量

它用来为rand()函数设置一个基数,这个基数是rand函数产生第一个随机数的基准,基数不同产生的随机数就不同

srand(111);
//为随机数产生设置了一个基准111
srand(888);
//为随机数产生设置了一个基准888
  1. 随机数函数rand()

函数原型为 int rand(void)

返回值类型为int,执行函数后会返回一个int类型的数值,不需要参数 注意 虽然rand函数不需要参数,但是使用时()不能省略

它用来生成一个0-32767之间的随机整数

注意

  1. 调用一次rand函数就会产生一个随机数,需要几个随机数就调用几次rand函数
  2. 连续产生多个随机数时,第一个随机数是以srand函数设置的基数为基准产生的,第二个随机数是以第一个随机数为基准产生的,以此类推
  3. 若没有调用srand函数来设置基准,系统默认的基数为1

srand函数和rand函数的使用

#include<stdio.h>
#include<stdlid.h>
#include<time.h>
int main(){
    int rand1,rand2,rand3;//定义三个变量存储接下来要产生的随机数

    srand(time(NULL));
    //前面我们讲到,第一次调用rand函数产生的随机数是以srand函数内的参数为基准产生的,后面调用rand时是以前面产生的随机数做基准
    //为了保证我们每次使用时产生的随机数都不同,我们就得每次给srand设置不同的基准显然这将不那么便捷
    //上面使用time函数是获取当前时刻计算机系统的时钟(单位是秒),每次都以当前时刻为基准,就可以解决每次都需要改变基准的问题
    //NULL是C语言的一个系统常量,代表的值为0
    rand1 = rand();//以srand设置的基准产生第一个随机数
    rand2 = rand();//以上一个rand函数产生的随机数为基准产生第二个随机数
    rand3 = rand();//以上一个rand函数产生的随机数为基准产生第三个随机数
}

有时候,实际问题需要的随机数是有范围的 例如要求产生10到20之间的随机数,这个问题不难解决

int rand1 = rand()%(20+1-10)+10;
//%符号前面提到过,作用是取余数
//简单的道理,一个数对n取余得到的数的范围将会是0--n-1,如果我们要求产生10--20之间的随机数
//我们就可以先产生一个0--10之间的随机数,然后再加上10,就可以解决

程序设计基础知识

基本掌握了前面的知识之后,我们就可以用C来按照需要来编写一些简单的程序了

对于这部分的内容,教材和一些资料书的内容较为冗杂,这里不做过多赘述,我将通过一个简单的程序设计实例来介绍

问题描述:设置一个简单的C程序实现提示用户输入三个整型变量,并求出它们的平均值并输出

#include<stdio.h>
int main(){
    //在任何学科中,解决问题的第一步往往都是分析问题
    //我们先简单剖析一下要求,提取出下面关键词“提示输入” “输入三个整型变量” “平均值” “输出”
    //同时也可以知道题目中需要四个变量,分别是需要输入的三个整型变量,还有一个变量用来存储平均值
    //在程序的开头我们就先把他们定义出来,这是较为推荐的一种做法
    int a,b,c;
    double ave;
    //首先“提示输入”就是通过printf函数输出一行提示,提示用户输入数据的类型和数量
    printf("please enter three integral numbers:");
    //然后是使用scanf函数输入三个整型变量a,b,c
    scanf("%d%d%d",&a,&b,&c);
    //然后是“计算平均值”
    //用来存储平均值的变量ave也是需要提前定义的
    ave = (a+b+c)/3.0;//至于为什么是3.0,这个不做赘述,前面已经多次提到
    //最后是“输出”
    printf("%lf",d);
}

当然,上面这个充满了注释的程序看起来有点让人眼花缭乱 我们实际设计时,并不需要把这些思路之类的注释写在程序里面 就像数学题的演草部分当然不会出现在答题卡上面

写注释当然是一个好的程序设计习惯,注释的作用是帮助自己和程序的其他阅读者更加清楚的理解程序的步骤,同时防止一段时间之后自己看不懂自己的程序

这里为了帮助大家更好的了解程序设计的思路方法,写了很多设计程序时的思路,看起来会有些复杂 当然这些脑子里面思考的东西你们不需要也一定不会写在程序里面的

三种基本结构

这类概念性的问题本来是不介绍的,但是对于前面程序设计实例中对于变量定义位置的不唯一性,还是添加了本章节

三大基本结构分别为 顺序结构 分支结构 循环结构

这里主要介绍顺序结构 后面两种结构都有单独的章节,这里就不做介绍

顺序结构是C语言最基本的结构,基于这种结构的程序代码是按照自前向后的顺利来运行的,就是说程序的运行顺序是自上而下的 对于前面部分程序设计实例中定义变量的问题来讲,我们在输入函数scanf中要用到三个变量a,b,c,所以这三个变量必须是已经定义好了的 这样我们才能使用它,并向它存入数值 否则,如若我们在使用a,b,c,之前没有定义它,换句话说,如果我们把a,b,c的定义放在输入函数scanf之后 程序当然会出错,遵循顺序结构的程序,对于变量的用法,必须是先定义后使用

但对于定义变量和提示输入的先后,从顺序结构上面并没有硬性要求,这个主要是个人书写习惯

至此第二章表达式与程序设计基础结束。