小朋友学C语言(6)

(一)

先动手编写一个程序:

#include <stdio.h>

int main()
{
if(1)
{
printf("The condition is true!\n");
} 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\n", (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 = '\0';
if(c1)
{
printf("The condition is true!\n");
}
else
{
printf("The condition is false!\n");
} char c2 = ' ';
if(c2)
{
printf("The condition is true!\n");
}
else
{
printf("The condition is false!\n");
} char c3 = 'A';
if(c3)
{
printf("The condition is true!\n");
}
else
{
printf("The condition is false!\n");
} return 0;
}

运行结果:

The condition is false!
The condition is true!
The condition is true!

说明:C语言中用’\0’来表示空字符。空格’ ‘也是一个字符,这从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\n\n");
} int y = 200; if(y >= 0 && y <= 100)
{
printf("y is between 0~100\n");
} 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\n");
} int y = 200;
if(y < 0 || y > 100)
{
printf("y is not between 0~100\n");
} 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\n");
} int y = 0;
if(!y)
{
printf("y is zero\n");
} 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\n", x);
printf("男生的人数为%d\n", x + 1);
printf("苹果共有%d个\n", 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\n");
} return 0;
}

运行结果:

x equals 10

(二)将x == 10改为10 == x

#include <stdio.h>

int main()
{
int x = 10;
if(10 == x)
{
printf("x equals 10\n");
} 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\n");
} 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\n");
}
else
{
printf("x not equals 0\n");
} 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\n", 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的时候,程序的执行顺序为
①-->②-->③-->④-->⑤-->⑥-->⑦-->⑧-->⑨

 
小朋友学C语言(6)
fiboccina.png

(3)
从(1)和(2)的分析过程可以看出,n为1或2是递归的终止条件。无论原先输入的正自然数n的值是多少,最终都会递归减少到n=1或n=2的情况。

开头讲的那个例子,不是严格的递归,因为那个故事是讲不完的,没有终止条件。

作业:
(1)执行断点前,在fibonacci()加上printf(“n = %d\n”, n);

int fibonacci(int n)
{
printf(“n = %d\n”, 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\n", ch);
printf("%d\n", ch);
printf("********************\n"); int num = 66;
printf("%c\n", num);
printf("%d\n", num);
printf("********************\n"); ch = 'a';
printf("%c\n", ch);
printf("%d\n", ch);
printf("********************\n"); num = 100;
printf("%c\n", num);
printf("%d\n", num);
printf("********************\n"); ch = 'B' + 24; // 相当于ch = 'Z';
printf("%c\n", ch);
printf("%d\n", ch);
printf("********************\n"); num = 97 + 25; // 97是'a'
printf("%c\n", num);
printf("%d\n", 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\n", 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 = '\0';
printf("%d\n", ch);
ch = ' ';
printf("%d\n", ch);
ch = '\n';
printf("%d\n", ch); return 0;
}

运行结果:

0
32
10

分析:
从运行可以看出:
(1)‘\0’与‘0’是不一样的,因为二者对应的ASCII编码不一样。
‘\0’对应着0,‘0’对应着48。
‘\0’代表空字符(NULL),通常用来做为字符串的结束符。
'0'则是字符0或者数字48。

(2)程序里的‘\0’、‘\n’是转义字符。
什么叫转义呢?就是改变原来的意义。
比如‘0’表示字符‘0’,加上斜杆后‘\0’就变成了空字符。
再比如‘n’表示字符‘n’,加上斜杆后‘\n’就变成了换行符。

(3)所有的键盘操作(比如按Enter键进行换行)都对应着一个字符,当然也就对应了一个整数。都可以转化成机器可以认识的二进制。

注意:空格是用字符' '来表示,不是用''来表示,两个单引号之间一定要加个空格,否则会报错。

上一篇:Hanoi问题 算法


下一篇:js 对时间进行判断 现在的时间是否在后台给的开始时间 和 结束时间 内 (时间格式为:2018-09-03 09:20:30)