(一)
先动手编写一个程序:
#include <stdio.h>
int main()
{
if(1)
{
printf("The condition is true!
");
}
return 0;
}
运行结果:
The condition is true!
再把1依次改为,2,5,100,-10,发现运行结果完全一样。
再改成if(0),此时发现没有运行结果,说明printf()语句没被执行。
C语言把判断语句中的任何非0或非空的值当作真。所以if(1), if(2), if(5), if(100), if(-10)的效果是一样的。
(二)
再编写一个程序:
#include <stdio.h>
int main()
{
int a = 100;
if(a > 0)
{
printf("The condition value is %d
", (a > 0));
}
return 0;
}
运行结果:
The condition value is 1
分析:
a = 100,a > 0成立 ,所以if( a > 0)等价于if(1)。
在C语言中,判断语句是有值的,要么为1,要么为0。比如本程序中a > 0的值就是1。
(三)
最后编写一个程序:
#include <stdio.h>
int main()
{
char c1 = ' ';
if(c1)
{
printf("The condition is true!
");
}
else
{
printf("The condition is false!
");
}
char c2 = ' ';
if(c2)
{
printf("The condition is true!
");
}
else
{
printf("The condition is false!
");
}
char c3 = 'A';
if(c3)
{
printf("The condition is true!
");
}
else
{
printf("The condition is false!
");
}
return 0;
}
运行结果:
The condition is false!
The condition is true!
The condition is true!
说明:C语言中用’ ’来表示空字符。空格’ ‘也是一个字符,这从if(c2)条件为真就可以看出来。
逻辑运算符有三种,“&&”(逻辑与)、“||”(逻辑或)和“!”(逻辑非)
(一)逻辑与&&
“&&”相当于生活中说的“并且”,就是两个条件都同时成立的情况下“&&”的运算结果才为“真”。只要有一个条件不成立,则结果为“假”。
1 && 1 = 1
1 && 0 = 0
0 && 1 = 0
0 && 0 = 0
验证程序:
#include <stdio.h>
int main()
{
int x = 50;
if(x >= 0 && x <= 100)
{
printf("x is between 0~100
");
}
int y = 200;
if(y >= 0 && y <= 100)
{
printf("y is between 0~100
");
}
return 0;
}
运行结果:
x is between 0~100
(二)逻辑或||
“||”相当于生活中说的“或者”,只要有一个条件成立,“||”的运算结果就为“真”。两个条件都不成立结果才为“假”。
1 || 1 = 1
1 || 0 = 1
0 || 1 = 1
0 || 0 = 0
验证程序:
#include <stdio.h>
int main()
{
int x = 50;
if(x < 0 || x > 100)
{
printf("x is not between 0~100
");
}
int y = 200;
if(y < 0 || y > 100)
{
printf("y is not between 0~100
");
}
return 0;
}
运行结果:
y is not between 0~100
(三)逻辑非!
如果条件为真,加上“!”后判断为假;如果条件为假,加上”!”后判断为真。
!0 = 1
!1 = 0
注意,计算机非0即为真,比如x = 1或x = 3或x = 50或x=-27,这些情况下if(x)判断都为真。
验证程序:
#include <stdio.h>
int main()
{
int x = 50;
if(!x)
{
printf("x is zero
");
}
int y = 0;
if(!y)
{
printf("y is zero
");
}
return 0;
}
运行结果:
y is zero
分析:
if(判断语句)
{
printf("xxx");
}
只有“判断语句”为真的情况下,printf()语句才能被执行。
这里if(!x),只有!x为真,printf()语句才会被执行。
既然!x为真,则x为假。所以printf()一旦被执行,必然说明x的值就是0。
题目
有两堆一样多的苹果,老师将第一堆苹果分给男生,每人4个,最后剩下6个。
老师又将第二堆苹果分给女生,每人5个,最后剩下5个。
已知男生比女生多1人。
求:女生有多少人?男生有多少人?苹果有多少个?
解法(一)
女生每人分5个苹果,最后剩下5个。假如女生的人数多1个(这样就跟男生数量一样多啦),那么苹果恰巧被分完。
这样问题就等价为:
一堆苹果,分给一组人。假如每个人分4个苹果,剩下6个苹果。假如每个人分5个苹果,恰巧分完。
这样,这组人的人数 = 剩下的苹果总数 / 每个人剩下的苹果个数 = 6 / (5 - 1) = 6。
所以,男生6人,女生5人,苹果总数是6 * 4 + 6 = 30个。
解法(二):使用方程求解
设苹果总数为y,女生人数为x,则有
y = 5 * x + 5 (1)
y = 4 * (x + 1) + 6 (2)
(2) 式- (1)式得,
0 = 4 * (x + 1) + 6 - (5 * x + 5)
解得x = 5, y = 30
所以,女生5人,男生6人,苹果30个。
解法(三):编程求解
在解法(二)的思想基础上,可以编写程序如下:
#include <stdio.h>
int main()
{
int x;
for(x = 1; x < 100000000; x++)
{
if(4 * (x + 1) + 6 == 5 * x + 5)
{
break; // 找到合适的x,跳出for循环
}
}
printf("女生的人数为%d
", x);
printf("男生的人数为%d
", x + 1);
printf("苹果共有%d个
", 5 * x + 5);
return 0;
}
运行结果为
女生的人数为5
男生的人数为6
苹果共有30个
总结
对比解法二和解法三,我们可以发现,两种方法的思路是一样的,只不过解法三是用程序来体现解法二的数学思想。事实上,通常所听到的算法,指的就是数学的计算方法,只不过在计算机领域,是用编程的方式来体现计算方法罢了。这也是计算机与数学关系密切的原因。
作业
(1)断点调试程序
(2)默写程序
“变量==常量”与“常量==变量”的区别
(一)编写程序
#include <stdio.h>
int main()
{
int x = 10;
if(x == 10)
{
printf("x equals 10
");
}
return 0;
}
运行结果:
x equals 10
(二)将x == 10改为10 == x
#include <stdio.h>
int main()
{
int x = 10;
if(10 == x)
{
printf("x equals 10
");
}
return 0;
}
运行结果:
x equals 10
结论:
C语言中,x == 10与10 == x的结果是一样的,都是判断x和10是不是相等。相等为真,不相等为假。
(三)假如在编写第一个程序的时候,if中的等号少写了一个,程序变成:
#include <stdio.h>
int main()
{
int x = 10;
if(x = 10)
{
printf("x equals 10
");
}
return 0;
}
运行结果:
x equals 10
虽然运行结果是对的,但是逻辑上已经不一样了。这里是两次把10赋值给x,然后再判断x是否为真。
(四)如果第2个程序中漏打了一个等号,程序变为
#include <stdio.h>
int main()
{
int x = 10;
if(10 = x)
{
printf("x equals 10");
}
return 0;
}
编译出错 ,这是因为,C语言中,只允许把常量赋值给变量,不允许把变量赋值给常量。常量不能被赋值。
结论:建议写成10 == x,不要写成x == 10。这样一旦漏打了一个等号,编译器报错,程序员可以立马修改。
(五)看看x = 0的情景
#include <stdio.h>
int main()
{
int x = 0;
if(x == 0)
{
printf("x equals 0
");
}
else
{
printf("x not equals 0
");
}
return 0;
}
运行结果:
x equals 0
(六)假如第5个程序中,程序员因粗心漏打了一个等号,程序变为
#include <stdio.h>
int main()
{
int x = 0;
if(x = 0)
{
printf("x equals 0");
}
else
{
printf("x not equals 0");
}
return 0;
}
运行结果
x not equals 0
分析:原本x与0是相等的,但是因为这里少打了一个等号,导致两次都是赋值操作,x为0,if里的判断条件为假,得出了x不等于0的错误结论。
结论:
当少打一个等号的时候,写成“常量 == 变量”编译不成功,这样程序员可以立马发现少打了一个等号。;
尤其是在变量为0的情况下,可以避免写成if(变量 = 0)而得到相反的结论。
建议:
写程序时一律写成if(“常量 == 变量”),比如if(10 == x)
不要写成if(“变量 == 常量”),比如if(x == 10)
(七)作业
默写上面程序中的任何一个。
斐波那契数列的非递归实现
一、斐波那契简介
斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........
这个数列从第3项开始,每一项都等于前两项之和。
二、非递归实现
动手编写程序:
#include <stdio.h>
int fibonacci(int n)
{
if(1 == n || 2 == n)
{
return 1;
}
int f1 = 1;
int f2 = 1;
int f3 = 0;
for(int i = 3; i <= n; i++)
{
f3 = f1 + f2;
f1 = f2;
f2 = f3;
}
return f3;
}
int main()
{
int m, result;
printf("input item number: ");
scanf("%d", &m);
result = fibonacci(m);
printf("The result is %d", result);
return 0;
}
运行结果:
input n: 6
The result is 8
新知识点:
(1)这里出现了一个新的函数scanf()。scanf()的作用是读取键盘或鼠标的输入。n是你通过键盘输入的值,&是取地址符,&n就是n在内存里的地址。找到了n在内存中的地址,也就取到了n的值。
假如你输入n 的值为 3,则&n就是3在内存里的地址,则n就是3。
scanf()的作用与printf()的作用相反。printf()的作用是打印、输出。
这两个函数都是在stdio.h中声明的。
【注意】多数线上编译器不支持scanf()函数,所以这个程序要用本机编译器(比如苹果电脑的Xcode,PC的dev c++)来编译。
(2)
if(1 == n || 2 == n)
{
return 1;
}
这段表示,假如你输入的n为1或2,则返回1。下面的语句都不被执行。
(3)假如你输入的值大于2,比如你输入了6,则fibonacci()函数中的for循环是这么执行的:
第一次,i = 3, i <= 6为真,f3 = f1 + f2 = 1 + 1 = 2, f1 = f2 = 1, f2 = f3 = 2
第二次,i = 4, i <= 6为真,f3 = f1 + f2 = 1 + 2 = 3, f1 = f2 = 2, f2 = f3 = 3
第三次,i = 5, i <= 6为真,f3 = f1 + f2 = 2 + 3 = 5, f1 = f2 = 3, f2 = f3 = 5
第四次,i = 6, i <= 6为真,f3 = f1 + f2 = 3 + 5 = 8, f1 = f2 = 5, f2 = f3 = 8
第五次,i = 7, i <= 7为假,循环结束。最终返回的f3的值为8
三、作业
(1)输入n = 1,用断点查看程序的执行过程。
(2)输入n = 2,用断点查看程序的执行过程。
(3)输入n = 3,用断点查看程序的执行过程。
(4)输入n = 4,用断点查看程序的执行过程。
(5)输入n = 5,用断点查看程序的执行过程。
(6)输入n = 6,用断点查看程序的执行过程。
(4)在纸上默写这个程序
斐波那契数列的递归实现
什么是递归呢?先举个例子:
从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?"从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?'从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……'"
这个例子里,故事内嵌套着故事,构成了递归。
动手编写程序:
#include <stdio.h>
int fibonacci(int n)
{
if(1 == n || 2 == n)
{
return 1;
}
return fibonacci(n-2) + fibonacci(n - 1);
}
int main()
{
int m;
printf("input m: ");
scanf("%d", &m);
int result = fibonacci(m);
printf("fibonacci(%d) = %d
", m, result);
return 0;
}
运行结果:
input m: 5
fibonacci(5) = 8
新知识点:
(1)
函数调用自身,就叫函数的递归调用。这个程序里,fibonacci()函数就调用了它本身。
但是不要以为return语句有两个fibonacci()函数,就误认为是2次调用自身。实际的调用次数是不固定的,要看n的值 。
咱们这里以n=5为例。
按照程序,有
fiboccina(5) = fiboccina(5 - 2) + fiboccina(5 - 1) = fiboccina(3) + fiboccina(4) ①
fiboccina(3)和fiboccina(4)同样会调用fiboccina()函数自身:
fiboccina(3) = fiboccina(3 - 2) + fiboccina(3 - 1) = fiboccina(1) + fiboccina(2) ②
fiboccina(4) = fiboccina(4 - 2) + fiboccina(4 - 1) = fiboccina(2) + fiboccina(3) ③
在②式里,fiboccina(1)和fiboccina(2)都不会调用自身,
因为n=1或n=2的时候,直接返回1。
所以fiboccina(3) = 1 + 1 = 2
在③中,fiboccina(2) = 1,fiboccina(3)会调用自身。
fiboccina(3) = fiboccina(1) + fiboccina(2) = 1 + 1 = 2
所以,fiboccina(4) = fiboccina(2) + fiboccina(3) = 1 + 2 = 3
将求得的fiboccina(3)和fiboccina(4)的值代入①,得
fiboccina(5) = fiboccina(3) + fiboccina(4) = 2 + 3 = 5,即为最终结果。
(2)
从(1)中的分析过程,可以看出n=5的时候,程序的执行顺序为
①-->②-->③-->④-->⑤-->⑥-->⑦-->⑧-->⑨
(3)
从(1)和(2)的分析过程可以看出,n为1或2是递归的终止条件。无论原先输入的正自然数n的值是多少,最终都会递归减少到n=1或n=2的情况。
开头讲的那个例子,不是严格的递归,因为那个故事是讲不完的,没有终止条件。
作业:
(1)执行断点前,在fibonacci()加上printf(“n = %d
”, n);
int fibonacci(int n)
{
printf(“n = %d
”, n);
if(1 == n || 2 == n)
{
return 1;
}
return fibonacci(n-2) + fibonacci(n - 1);
}
输入n = 1,用断点查看程序的执行过程。
输入n = 2,用断点查看程序的执行过程。
输入n = 3,用断点查看程序的执行过程。
输入n = 4,用断点查看程序的执行过程。
输入n = 5,用断点查看程序的执行过程。
输入n = 6,用断点查看程序的执行过程。
(2)默写这个程序
二进制
一、十进制与二进制
我们日常所用到的计数方式,是十进制(数字用0,1,2,3,4,5,6,7,8,9这十个数字来表示)。
十进制的进位规则是”逢十进一”。
比如零、一、二、三、四、五、六、七、八、九都是用一位数来表示。再进一的话,是十。十无法用1位数来表示,所以要”进一”,用两位数来表示,即10。
19进一是二十,无法以1X来表示,所以得用20来表示。
99进一是一百,无法用9X来表示,所以得用100来表示。
计算机用二进制(数字用0和1来表示)来存储数据。二进制的进位规则是“逢二进一”。
零用0来表示;
一用1来表示;
那么二该如何表示呢?因为总共只能用0和1来表示,二就相当于十进制里的十,需要进位了,所以二用10表示;
同理三用11来表示;
四需要再进一位,用100来表示;
五用101来表示;
六用110来表示;
七用111来表示;
八需要再进一位,用1000来表示;
九用1001来表示;
其余的依此类推。
二、二进制转换为十进制
在考虑二进制之前,咱们先看一下十进制的幂表示方法:
0 = 0 * 10^0;
1 = 1 * 10^0;
2 = 2 * 10^0;
3 = 3 * 10^0;
10 = 1 * 10^1 + 0 * 10^0
11 = 1 * 10^1 + 1 * 10^0
12 = 1 * 10^1 + 2 * 10^0
13 = 1 * 10^1 + 3 * 10^0
20 = 2 * 10^1 + 0 * 10^0
21 = 2 * 10^1 + 1 * 10^0
22 = 2 * 10^1 + 2 * 10^0
23 = 2 * 10^1 + 3 * 10^0
30 = 3 * 10^1 + 0 * 10^0
31 = 3 * 10^1 + 1 * 10^0
99 = 9 * 10^1 + 9 * 10^0
100 = 1 * 10^2 + 0 * 10^1 + 0 * 10^0
123 = 1 * 10^2 + 2 * 10^1 + 3 * 10^0
这样,就可以得出任何一个十进制数的幂表示方法。比如
32078 = 3 * 10^4 + 2 * 10^3 + 0 * 10^2 + 7 * 10^1 + 8 * 10^0
二进制同样可以用这种方式来表示,并且可以算出相应的十进制值
二进制 | 十进制 |
---|---|
0 | 0 * 2^0 = 0 |
1 | 1 * 2^0 = 1 |
10 | 1 * 2^1 + 0 * 2^0 = 2 |
11 | 1 * 2^1 + 1 * 2^0 = 3 |
100 | 1 * 2^2 + 0 * 2^1 + 0 * 2^0 = 4 |
101 | 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 5 |
110 | 1 * 2^2 + 1 * 2^1 + 1 * 0^0 = 6 |
111 | 1 * 2^2 + 1 * 2^1 + 1 * 2^0 = 7 |
1000 | 1 * 2^3 + 0 * 2^2 + 0 * 2^1 + 0 * 2^0 = 8 |
1001 | 1 * 2^3 + 0 * 2^2 + 0 * 2^1 + 1 * 2^0 = 9 |
1010 | 1 * 2^3 + 0 * 2^2 +1 * 2^1 + 0 * 2^0 = 10 |
1011 | 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = 11 |
1100 | 1 * 2^3 + 1 * 2^2 + 0 * 2^1 + 0 * 2^0 = 12 |
1101 | 1 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 13 |
1110 | 1 * 2^3 + 1 * 2^2 + 1 * 2^1 + 0 * 2^0 = 14 |
1111 | 1 * 2^3 + 1 * 2^2 + 1 * 2^1 + 1 * 2^0 = 15 |
10000 | 1 * 2^4 + 0 * 2^3 + 0 * 2^2 + 0 * 2^1 + 0 * 2^0 = 16 |
作业:
(1)计算并牢记2 ^ 0, 2 ^ 1, 2 ^ 2, 2 ^ 3, 2 ^ 4, 2 ^ 5, 2 ^ 6, 2 ^ 7, 2 ^ 8, 2 ^ 9, 2 ^ 10
(2)求二进制11011, 101010, 11111111对应的十进制数
三、十进制正整数转换为二进制
十进制整数转换为二进制整数采用"除2取余,逆序排列"法。
具体做法是:用2整除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为0时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。
例1:十进制13转化为二进制
解:
13 / 2 = 6,余数为1
6 / 2 = 3,余数为0
3 / 2 = 1,余数为1
1 / 2 = 0,余数为1
上面的余数为1,0,1,1。逆排列后变为1,1,0,1
所以13 = (1101)B。这里B代表Binary,二进制的意思。
例2:1024转化为二进制
解:
1024 / 2 = 512,余数为0
512 / 2 = 256,余数为0
256 / 2 = 128,余数为0
128 / 2 = 64,余数为0
64 / 2 = 32,余数为0
32 / 2 = 16,余数为0
16 / 2 = 8,余数为0
8 / 2 = 4,余数为0
4 / 2 = 2,余数为0
2 / 2 = 1,余数为0
1 / 2 = 0,余数为1
所以,1024 = (10000000000)B。从这里也可以看出,1024 = 2 ^ 10
例3:十进制255转化为二进制
解:
255 / 2 = 127, 余数为1
127 / 2 = 63,余数为1
63 / 2 = 31,余数为1
31 / 2 = 15,余数为1
15 / 2 = 7,余数为1
7 / 2 = 3,余数为1
3 / 2 = 1,余数为1
1 / 2 = 0,余数为1
所以,255 = (11111111)B
作业:将十进制25, 100, 32767转化为二进制。
字符和整数的关系
程序(一)
#include <stdio.h>
int main()
{
char ch = 'A';
printf("%c
", ch);
printf("%d
", ch);
printf("********************
");
int num = 66;
printf("%c
", num);
printf("%d
", num);
printf("********************
");
ch = 'a';
printf("%c
", ch);
printf("%d
", ch);
printf("********************
");
num = 100;
printf("%c
", num);
printf("%d
", num);
printf("********************
");
ch = 'B' + 24; // 相当于ch = 'Z';
printf("%c
", ch);
printf("%d
", ch);
printf("********************
");
num = 97 + 25; // 97是'a'
printf("%c
", num);
printf("%d
", num);
return 0;
}
运行结果:
A
65
********************
B
66
********************
a
97
********************
d
100
********************
Z
90
********************
z
122
分析:
从程序的运行结果可以看出,字符和整数是对应的。
字符’A’对应于65,’B’对应于66,’C’对应于67,……,’X’对应于88,’Y’对应于89,’Z’对应于90。
’a’对应于97,’b’对应于98,’c’对应于99,……,’x’对应于120,’y’对应于’121’,’z’对应于122。
为什么会有对应关系呢?
具体原因请参考:
ASCII编码简介
程序(二)
#include <stdio.h>
int main()
{
for(char ch = '0'; ch <= '9'; ch++)
{
printf("字符%c <---> 数字%d
", ch, ch);
}
return 0;
}
运行结果:
字符0 <---> 数字48
字符1 <---> 数字49
字符2 <---> 数字50
字符3 <---> 数字51
字符4 <---> 数字52
字符5 <---> 数字53
字符6 <---> 数字54
字符7 <---> 数字55
字符8 <---> 数字56
字符9 <---> 数字57
分析:
从程序运行结果可以看出,字符‘0’对应于48,‘1’对应于49,‘2’对应于50,‘3’对应于51,‘4’对应于52,‘5’对应于53,‘6’对应于54,‘7’对应于55,‘8’对应于56,‘9’对应于57。
那么字符串“10”对应于数字多少呢?58吗?
不是的。计算机把字符串“10”当成了两个字符:‘1’和‘0’,分别对应49和48。
具体也可查阅ASCII编码简介中的表格。
还有一个问题:当你用键盘输入“0”的时候,计算机怎么知道你输入的是字符还是数字呢?
很简单,看格式化符号,如果是scanf(“%c”, ch),计算机就当做是字符,如果是scanf(“%d”, ch),计算机就当成是数字。
printf(“%c”, ch)和printf(“%d”,ch)也是同样的道理,这从上面的代码就可以看出来。
程序(三)
#include <stdio.h>
int main()
{
char ch = ' ';
printf("%d
", ch);
ch = ' ';
printf("%d
", ch);
ch = '
';
printf("%d
", ch);
return 0;
}
运行结果:
0
32
10
分析:
从运行可以看出:
(1)‘ ’与‘0’是不一样的,因为二者对应的ASCII编码不一样。
‘ ’对应着0,‘0’对应着48。
‘ ’代表空字符(NULL),通常用来做为字符串的结束符。
'0'则是字符0或者数字48。
(2)程序里的‘ ’、‘
’是转义字符。
什么叫转义呢?就是改变原来的意义。
比如‘0’表示字符‘0’,加上斜杆后‘ ’就变成了空字符。
再比如‘n’表示字符‘n’,加上斜杆后‘
’就变成了换行符。
(3)所有的键盘操作(比如按Enter键进行换行)都对应着一个字符,当然也就对应了一个整数。都可以转化成机器可以认识的二进制。
注意:空格是用字符' '来表示,不是用''来表示,两个单引号之间一定要加个空格,否则会报错。