本系列博客用于记录学习浙江大学翁恺老师的C语言程序设计,系列笔记链接如下:
C语言程序设计学习笔记:P1-程序设计与C语言
C语言程序设计学习笔记:P2-计算
C语言程序设计学习笔记:P3-判断
C语言程序设计学习笔记:P4-循环
C语言程序设计学习笔记:P5-循环控制
C语言程序设计学习笔记:P6-数据类型
C语言程序设计学习笔记:P7-函数
C语言程序设计学习笔记:P8-数组
C语言程序设计学习笔记:P9-指针
C语言程序设计学习笔记:P10-字符串
C语言程序设计学习笔记:P11-结构类型
C语言程序设计学习笔记:P12-程序结构
C语言程序设计学习笔记:P13-文件
C语言程序设计学习笔记:P14-链表
文章目录
一、变量
1.1 变量定义
上周我们写了一行代码,来计算两个数相加。
printf("12+34=%d", 12+34);
当然,我们也可以做相减。比如我们是收银员,顾客给我们100元,他们买的东西是23元,我们应该找零100-23元。
printf("100-23=%d", 100-23);
数据输入
问题引入: 我们如何在程序执行过程中再输入23这个数字呢,即23不是我们写在程序里面的。因为如果写在程序里面,那么当我们要算不同的数字时,我们需要将程序修改并重新编译运行。想要在程序运行过程中输入我们想输入的数字,那么我就需要三件事情:
①程序中有地方放输入的数字;
②有办法从外面输入数字;
③输入的数字能参与计算。
现在我们写一段代码来实现在运行过程中输入数字。
#include <stdio.h>
int main()
{
int price = 0;
printf("请输入金额(元):");
scanf("%d", &price); //接受输入
int change = 100 - price;
printf("找您%d元。\n", change);
return 0;
}
我们运行一下,终端的界面如下图所示:
我们输入23,并按下回车,程序将正确的结果打印出来。我们可以看出是在终端窗口中进行输入,是以行为单位进行的,行的结束标志就是你按下了回车键。在你按下回车之前,你的程序不会读到任何东西。
变量
我们输入的数字放到哪里去了呢?实际上输入的数字放在了一个叫做变量的地方。我们可以看到第一行输入了一句
int price = 0;
这一行定义了一个变量,变量的名字是price,类型是int,初始值是0。
变量的含义
当我们需要在程序里保存数据时,比如上面的例子中要记录用户输入的价格,就需要一个变量来保存它。用一个变量保存了数据,它才能参加到后面的计算中,比如计算找零。变量定义的一般形式就是:
<类型名称> <变量名称>;
int price;
int amount;
int price, amount;
(定义了两个int类型的变量,用逗号隔开)
标识符
变量需要一个名字,变量的名字是一种标识符,意思是它是用来识别这个和那个的不同的名字。比如上面那段代码中,我们用price代表商品的金额,用change代表找零的金额。标识符有标识符的构造规则,基本的原则是:标识符只能由字母、数字和下划线组成,数字不可以出现在第一个位置上,C语言的关键字(有的地方叫它们保留字)不可以用做标识符。C语言的部分关键字如下所示:
auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while, inline, restrict
1.2 变量赋值与初始化
详细解读int price = 0
int
C是一种有类型的语⾔言,所有的变量必须具有确定的数据类型。数据类型表示在变量中只能存放指定类型的数据,程序运行过程中也不能改变变量的类型。
price=0
这是一个式子,这里的=
是一个赋值运算符,表示将=右边的值赋给左边的变量。有运算符的式子就叫做表达式。和数学不同,a=b在数学中表示关系,即a和b的值一样。而在程序设计中,a=b表示将b的值赋给a,关系是静态的,而动作是动态的。在数学中,a=b和b=a是等价的,而在程序设计中,两者的意思完全相反。
初始化
由于上面的赋值发生在定义变量时,我们也叫它初始化。因此我们在做变量初始化时,格式如下:
<类型名称> <变量名称> = <初始值>;
int price = 0;
int amount = 100;
int price = 0, amount = 100;
未初始化的结果
虽然C语言并没有强制要求所有的变量都在定义的地方做初始化,但是所有的变量在第一次被使用(出现在赋值运算符的右边)之前被应该赋值一次。如果没有初始化呢?我们来做个实验,输入两个变量
i
和j
,直接让j=i+10
,看其会有什么结果。
运行一下,我这里会报错:
而在视频中使用Dev C++不会报错,会得到一个很奇怪的值。为什么会得到这个奇怪的值?原因在于:变量都是存储在内存中,如果我们没有对它初始化,那么这个变量在内存中的那个地方原本有些什么值在里面,那么它就是那个值。
不同标准中变量的定义方法
在程序中我们还定义了第二个变量
change
,并且做了计算。
值得注意的是,这种写法是C99的写法,因为C99允许你在程序的任何地方定义变量,只要它出现在第一次使用之前即可。而传统的ANSI C只能在代码开头的地方定义变量。
1.3 程序的输入
数据输入
在开头第一段代码中,我们使用
scanf("%d", &price);
这行代码实现输入数据。这句代码中,要求scanf这个函数读入下一个整数,读到的结果赋值给变量price。要注意price前面的&,具体这个符号的意义我们在指针那一章再讲解。
如果我们不输入一个整数,输入一些其它的东西呢?我们输入了一个hello,程序没有从中读到有效的数字,于是它只能给一个默认的数值0。
1.4 常量VS变量
在代码中我们输入了下面这行计算找零的代码,其中100是一个固定不变的数,即常数。直接写在程序里,我们称作直接量(literal)。
int change = 100 - price;
其实对于这种情况有更好的方式,那就是定义一个常量。
const int AMOUNT = 100;
其中,const是一个修饰符,加在int的前面,用来给这个变量加上一个const(不变的)的属性。这个const的属性表示这个变量的值一旦初始化就不能再修改了。如果你试图对常量做修改,把它放在赋值运算符的左边,就会被编译器发现,指出为一个错误。我们来试一下,对一个常量的值进行修改。
#include <stdio.h>
int main()
{
const int AMOUNT = 100;
AMOUNT = 90;
return 0;
}
运行,发现会报错。
因此,我们的代码可以写成这样。这样做有许多好处:别人去看,如果看到100不会知道是什么意思。如果看到AMOUNT,则会知道是金额。同时,我们把这个常量放在前面,如果以后需要修改,可以很容易找到这个AMOUNT。
#include <stdio.h>
int main()
{
const int AMOUNT = 100;
int price = 0;
printf("请输入金额(元):");
scanf("%d", &price);
int change = AMOUNT - price;
printf("找您%d元。\n", change);
return 0;
}
当AMOUNT也需要我们输入时,需要对程序进行修改,如下所示:
#include <stdio.h>
int main()
{
int amount = 100;
int price = 0;
printf("请输入金额(元):");
scanf("%d", &price);
printf("请输入票面:");
scanf("%d", &amount);
int change = amount - price;
printf("找您%d元。\n", change);
return 0;
}
可以看出,当程序需要读入多个数字时,你可以在一行里面输入,然后中间用空格隔开。也可以在多行里面输入,即使用多个scanf。一行里面多个输入如何操作呢,代码如下所示:
#include <stdio.h>
int main()
{
int a;
int b;
printf("请输入两个整数:");
scanf("%d %d", &a, &b);
printf("%d + %d = %d\n", a, b, a + b);
return 0;
}
我们开始执行,有两种方式输入。
我们依次输入1、空格、2。
我们输入1、回车、2。
我们一直输入多个回车也没有问题,它会等到我们输入一个有效的值才会读入。
如果我们输入个hello,则会出错。
1.5 浮点数
当我们在计算身高时,通常使用公制单位(米),比如身高1米75。而美国人常使用英制单位(英尺),比如身高5尺7寸,那么他的身高就应该是 ( 5 + 7 ÷ 12 ) × 0.3048 = 1.7018 (5 + 7 ÷ 12 ) × 0.3048 = 1.7018 (5+7÷12)×0.3048=1.7018米(这是因为1英尺=0.3028米,同时1英尺=12英寸)。现在我们写一个程序,让用户输入几尺几寸,将其转换为公制的米数。
#include <stdio.h>
int main()
{
printf("请分别输入身高的英尺和英寸,"
"如输入\"5 7\"表示5英尺7英寸:");
int foot;
int inch;
scanf("%d %d ", &foot, &inch);
printf("身高是%f米。\n",
((foot + inch / 12) * 0.3048));
return 0;
}
运行,并输入不同的数字,发现存在一个问题:似乎我们输入的英寸没起作用。
所以,什么地方错了呢?我们做一个小小的实验,看看10/3的结果是多少。
#include <stdio.h>
int main()
{
printf("10/3=%d\n", 10/3);
return 0;
}
可以看出结果为3,明显错误。
我们再看看10/3*3的结果是多少。
#include <stdio.h>
int main()
{
printf("10/3*3=%d\n", 10/3*3);
return 0;
}
可以看出10/3*3=9,结果明显错误。
问题出现的原因:
- 在C语言中,两个整数的运算的结果只能是整数。如果结果有小数,它会将小数直接扔掉。比如7/12=0.58,而实际上会将小数扔掉,结果为0
- 在C语言中10和10.0是两个不同的数,10.0是浮点数。那么我们使用10.0/3来看看结果。注意printf里面要使用%f,因为这是浮点数,而%d代表整数。
#include <stdio.h>
int main()
{
printf("10.0/3=%f\n", 10.0/3);
return 0;
}
可以看出结果正确。
我们使用10.0/3*3时,看一下结果
#include <stdio.h>
int main()
{
printf("10.0/3=%f\n", 10.0/3);
return 0;
}
可以看出,结果正确。
改进
因此我们把程序改进一下,当浮点数和整数放到一起运算时,C会将整数转换成浮点数,然后进行浮点数的运算。
代码如下:
#include <stdio.h>
int main()
{
printf("请分别输入身高的英尺和英寸,"
"如输入\"5 7\"表示5英尺7英寸:");
int foot;
int inch;
scanf("%d %d", &foot, &inch);
//将12改为12.0
printf("身高是%f米。\n",
((foot + inch / 12.0) * 0.3048));
return 0;
}
我们还有另外一种改法,将输入的英尺和英寸保存为double类型的变量。double的意思是“双”,它本来是双精度浮点数(double float)的第一个单词。人们用来表示浮点数类型除了double,还有float(意思就是浮点)表示单精度浮点数。
#include <stdio.h>
int main()
{
printf("请分别输入身高的英尺和英寸,"
"如输入\"5 7\"表示5英尺7英寸:");
double foot; //将int更改为double
double inch;
scanf("%lf %lf", &foot, &inch); //使用scanf输入double类型的变量时,需要使用%lf
printf("身高是%f米。\n",
((foot + inch / 12) * 0.3048));
return 0;
}
整数和浮点数的输入输出用法
到现在为止我们已经知道有整数和浮点数两种数据类型了,他们的用法如下:
①整数: int
printf(“%d”,…)
scanf(“%d”,…)
②浮点数: double
printf(“%f”,…)
scanf(“%lf”,…)*
注: 整数类型不能表达有小数部分的数,整数和整数的运算结果还是整数。计算机里会有纯粹的整数这种奇怪的东西,是因为整数的运算比较快,而且占地方也小。其实人们日常生活中大量做的还是纯粹整数的计算,所以整数的用处还是很大的。
小测验
1、以下哪些是有效的变量名?
A. main
B. 4ever
C. monkey-king
D. __int
答案:A、D
2、给定:
则以下哪些输入方式是正确的?
A. 1 2
B. 1,2
C. 1(回车)2
D. 1、2
答案:A、C
3、给定:
以下哪些scanf的使用是正确的?
A. scanf(“%d”, &a);
scanf(“%d”, &b);
B. scanf(“%d %d”, &a, &b);
C. scanf(“%d, %d”, &a, &b);
D. scanf(“%d %d”, a, b);
答案:A、B、C
4、给定以下代码段:
则a的初始值是0
答案:错
5、写出下式的运算结果:
答案:10
6、写出下式的运算结果:
答案:9
二、表达式
2.1 表达式
表达式的定义
一个表达式是一系列运算符和算子的组合,用来计算一个值,如下面所示:
amount = x ∗ * ∗(1+0.03) ∗ * ∗(1+0.03);
total = 57;
count = count + 1;
value = (min / 2) ∗ * ∗ lastValue;
表达式的组成
在表达式里面有两种东西:运算符和算子。
①运算符(operator)是指进行运算的动作,比如加法运算符+,减法运算符-。
②算子(operand)是指参与运算的值,这个值可能是常数,也可能是变量,还可能是一个方法的返回值。
例1: 计算时间差。输入两个时间,每个时间分别输入小时和分钟的值,然后输出两个时间之间的差,也以几小时几分表示。
①首先,拿到问题我们可以知道需要四个变量来存储两个时间的小时与分钟的值,同时需要scanf来读取输入的值。
int hour1, minute1;
int hour2, minute2;
scanf("%d %d", &hour1, &minute1);
scanf("%d %d", &hour2, &minute2);
②其次,我们如何计算呢?如果是2点20分和1点10分,那我们可以直接将两个时间对应的小时与分钟的值相减。但是这样计算会出问题,比如会出现分钟借位的情况:1点40分和2点10分的差?我们会得到1小时负30分。这显然是不合理的,因为分钟数出现了借位。这时候我们可以将两个时间都换算成分钟数,然后相减。最后使用t/60计算出小时数,使用t%60计算出分钟数。
#include <stdio.h>
int main()
{
int hour1, minute1;
int hour2, minute2;
scanf("%d %d", &hour1, &minute1);
scanf("%d %d", &hour2, &minute2);
int t1 = hour1 * 60 + minute1;
int t2 = hour2 * 60 + minute2;
int t = t2-t1;
printf("时间差是%d小时%d分。", t/60, t%60);
return 0;
}
2.2 运算符优先级
现在我们写一个程序,输入两个整数,输出它们的平均值。
#include <stdio.h>
int main()
{
int a,b;
scanf("%d %d", &a, &b);
double c = (a+b)/2.0;
printf("%d和%d的平均值=%f\n", a, b, c);
return 0;
}
可以看到我们程序中写的计算表达式为(a+b)/2,这说明程序和我们的数学也一样,符号具有优先级,先计算括号内的。优先级表达的就是谁先算,谁后算。C语言的运算优先级如下图所示。
其中:
①加号和减号一般是双目运算符,因为它有两个算子。但是也会存在这种情况,比如a已经有个值了,我想把这个值取负,这个时候我们写-a就行了。这个时候的 - 就是个单目运算符,因为它只有一个算子a。
②结合关系从左到右也很符合我们的数学逻辑,比如存在多个相同优先级的运算符,如a+b+c会先算a+b,再算+c。
③C语言中赋值比较特殊,也是个运算符,而不是特殊的语句。为什么我们说赋值运算符优先级最低呢,比如b=a+5,如果赋值运算符优先级最高,那么会先执行b=a,然后将其结果加上5,然后就没有下文了。
注意: 为了让这些表达式看起来和正常的算式是一样的,我们必须让赋值是一个优先级很低的运算符。既然赋值是个运算符,赋值运算有结果,就有人写出这样的式子。他想一个式子做两件事,既把a的值赋给了b,又将1+a的值给了c。这种赋值叫做嵌入式赋值。不利于阅读容易产生错误。
例2: 在银行存定期的时候,可以选择到期后自动转存,并将到期的利息计入本金合并转存。如果1年期的定期利率是3.3%,那么连续自动转存3年后,最初存入的x元定期会得到多少本息余额?
分析:C语言没有直接做幂次的运算符,因此我们这里暂时只能将(1+0.033)连续乘三次。等我们学习循环,就能很方便地计算了。
#include <stdio.h>
int main()
{
int x;
scanf_s("%d", &x);
double amount = x * (1+0.033) * (1 + 0.033) * (1 + 0.033);
printf("%f", amount);
return 0;
}
2.3 交换变量
交换变量
现在我有两个变量:int a = 6; int b = 5; 如何交换a、b两个变量的值?如果是数学中,我们要表达的关系是把b的值给a,然后把a的值给b。即可以使用a=b; b=a; 但是程序表达的是顺序执行的动作,而不是关系,这样做的结果使得a和b都得到b原来的值。
解决方案: 那我们该怎么做?比如说我有两杯水A和B,现在要把这两杯水交换一下,那我一定需要一个空杯子C。我需要先把一杯水A倒入那个空杯子C中,然后将B中的水倒入A中,最后将C中的水倒入B中。
#include <stdio.h>
int main()
{
int a=5;
int b=6;
int t;
t=a;
a=b;
b=t;
printf("a=%d,b=%d", a, b);
return 0;
}
运行,可以看出a=6,b=5,已经成功交换了a和b的值。
2.4 复合赋值与递增递减
复合赋值
5个算术运算符可以和赋值运算符
=
结合起来,形成复合赋值运算符:+=
、-=
、*=
、/=
和%=
。用法如下:
total += 5;
实际上做的操作是:total = total + 5;
注意两个运算符中间不要有空格
递增递减
++
和--
是两个很特殊的运算符,它们是单目运算符,这两个运算符分别叫做递增和递减运算符,他们对应的算子还必须是变量。他们的作用就是给这个变量+1或者-1。用法如下:
count++;
实际上做的操作是:count += 1; 或 count = count + 1;
前后缀形式:++
和--
可以放在变量的前面,叫做前缀形式,也可以放在变量的后面,叫做后缀形式。a++的值是a加1以前的值,而++a的值是加了1以后的值,无论哪个,a自己的值都加了1了。
例: 递增运算符前后缀形式
#include <stdio.h>
int main()
{
int a;
a=10;
printf("a++=%d\n", a++);
printf("a=%d\n", a);
printf("++a=%d\n", ++a);
printf("a=%d\n", a);
return 0;
}
运行,结果如下。
小测验
1、写出以下代码执行后,t1和t2的值
答案:14 16
2、写出以下表达式的结果
6 + 5 / 4 - 2
2 + 2 * (2 * 2 - 2) % 2 / 3
10 + 9 * ((8 + 7) % 6) + 5 * 4 % 3 * 2 + 3
1 + 2 + (3 + 4) * ((5 * 6 % 7 / 8) - 9) * 10
答案:5 、2、44、-627