【C语言】MOOC翁恺老师-入门-分支结构与循环结构-3
三、分支结构与循环结构
1. 判断
-
判断语法
// if if(条件){ .... } // if后没有{},仅作紧跟的一句处理后结束 if(条件) .... // if-else if(条件){ .... }else{ .... } // if-elif-else if(条件){ .... }else if(条件){ /* Python是elif */ .... }else{ .... }
-
C语言中判断后true == 1;flase == 0
-
问题:
-
/* */是注释,编译器不管其中的内容。但是有没有想过编译器会把它替换成什么呢?是一个空格?还是不留痕迹?
答: 在把代码翻译成汇编代码前,还有一个步骤叫预处理。 预处理之后注释代码会被处理掉。 一条语句前的注释 => 等长的空格; 一条语句中的注释 => 一个空格; 一条语句后的注释 => 直接删除。
-
2. 循环
-
循环语法
// while,其它情况用while while(循环条件){ 循环体.. } // do-while, 至少执行循环体一次。如果必须执行一次,用do-while do { 循环体.. }while(循环条件); // for,如果知道固定次数用for for(初始条件; 循环条件; 步进式){ //括号中每一个表达式都可以省略 循环体.. }
-
while练习-计算某个数的位数
# include <stdio.h> int main() { int x; int n = 0; scanf("%d", &x); n++; x /= 10; // 从右往左依次划掉一位 while(x > 0){ n++; x /= 10; } printf("%d", n); return 0; }
-
for循环的循环次数思考
-
for( i = 0; i<n; i++)
-
循环的次数是n,而循环结束以后,
i
的值是n。循环的控制变量i
,是选择从0开始还是从1开始,是判断i<n
还是判断i<=n
,对循环的次数,循环结束后变量的值都有影响。如下所示// 1. for(i=0; i<5; i++){ ... } i=0; i=1; i=2; i=3; i=4, 最后i=5,共5次 // 2. for(i=1; i<=5; i++){ ... } i=1; i=2; i=3; i=4; i=5, 最后i=6,共5次
-
任何for循环都能改写成while循环
-
-
验证测试的方法
- 测试用例一定要多种情况都考虑到,正数,负数,0...
- 断点
- 人脑模拟电脑处理枚举情况,排除逻辑上的错误
- 在代码中穿插打印语句,看到每次循环的结果
- 可以在代码中加上打印语句,提醒自己现在运行到哪个位置
-
问题
-
for循环是高级语言出现的第一种循环,它和现代的程序设计理念是否存在差距?为什么Python语言就没有C语言这种形式的for循环了?
答:因为for循环不符合人类的语言逻辑,并不易于理解,因此就造成了第一次接触语言的人难以理解for循环所表达的意思。而现代程序设计理念更强调与人类语言的逻辑统一性而非编译逻辑。故在新兴的python语言中祛除掉了C中的for循环逻辑,改用了python独有的for循环形式
in range
。但由于for循环抽象程度高,所以在掌握语言基础后,其表现逻辑更为简洁易于回溯,反而更方便使用。
-
3. 进一步的判断与循环
3.1 逻辑类型和运算
-
逻辑运算
-
C99标准bool类型:引入#include <stdbool.h>后才能使用bool和true,false
# include <stdbool.h> int main() { bool b = 6>5; bool t = true; /* 虽然bool类型可以用true赋值,但是后续t=2也可以通过编译。另外C并没有规定输出时候什么格式输出bool, 实际上输出后它还是1-true, 0-false */ t = 2; printf("%d\n", b); return 0; }
-
-
逻辑运算符:
- && 、|| 是短路,左边能决定了就不会再做右边判断了,要小心点。
- 编码习惯:不要把赋值,包括复合赋值放进判断表达式中,例如
a>1 && a++<2
-
条件运算符
- 三目运算符,用于条件判断,例如
count = (count > 20) ? count - 10 : count +10;
- 三目运算符,用于条件判断,例如
3.2 级联和嵌套的判断
- else的匹配:如果没有大括号的情况下,else总和最近的 if 匹配。为了避免代码阅读时出错,我们始终在 if 或 else后面用 { },即使只有一条语句的时候也如此,养成良好的编码习惯。
3.3 多路分支
-
switch语法
switch( 控制表达式 ){ // 控制表达式只能是整数型 case 常量: // 常量可以是常数,也可以是常数计算的表达式 ...; break; case 常量: ...; break; default: ....; }
3.4 循环的例子
-
套路:
- 计算之前先保存原始的值,后面可能有用
-
练习1:猜数的游戏
# include <stdio.h> # include <stdlib.h> // rand()产生随机数的库函数头文件 # include <time.h> int main() { int guess; int count; srand(time(0)); int number = rand()%100+1; // %100是产生100以内的数 // x%n的结果是产生【0, n-1】的一个整数 printf("please insert your guess_number between 1 to 100.\n"); scanf("%d", &guess); while(guess != number){ if(guess < number){ printf("your guess_number is too small.\n"); }else{ printf("your guess_number is too big.\n"); } count++; scanf("%d", &guess); } printf("wonderful!! you costed %d times to guess it right.", count); return 0; }
-
猜数游戏,为什么100以内的数猜7次就够了?
答:二分法,2的7次方是2的次方里面第一个比100大的
-
-
练习2:数字的逆序
# include <stdio.h> int main() { int x; scanf("%d", &x); int digit; int ret = 0; while( x>0 ){ digit = x % 10; // 取最后一位 ret = ret*10 + digit; // 进行逆序处理 x /= 10; // 删掉最后一位 } printf("%d", ret); return 0; }
4. 循环控制
-
嵌套循环练习:从2开始打印前50个素数
// 素数:大于1的自然数除了1和它自身整除 # include <stdio.h> int main() { int count = 0; // 前50个数的计数 int x; // 用户输入当前的数字x,进行往后判断 scanf("%d", &x); while( count<50 ){ int isPrime = 1; // 设置判断flag for( int j = 2; j<x; j++ ){ // j从2开始 if( x % j == 0){ isPrime = 0; break; } } if( isPrime == 1 ){ printf("%d\n", x); count++; } x++; } printf("sum_count:%d times", count); return 0; }
-
多重循环练习:凑硬币(如何用1角,2角和5角的硬币凑出10元以下的金额?)
# include <stdio.h> int main() { int x; int one, two, five; int exit = 0; // 设置限制,使得到一个方案后执行接力break跳出 scanf("%d", &x); for( one = 1; one < x*10; one++){ for( two = 1; two < x*10/2; two++){ for( five = 1; five < x*10/5; five++){ if( one + two*2 + five*5 == x*10 ){ printf("可以用%d个1角+%d个2角+%d个5角得到%d元\n", one, two, five, x); exit = 1; // 表示获得一个方案,执行接力break break; } } if(exit == 1) break; } if(exit == 1) break; } return 0; }
- 接力break是一种跳出多重循环的判断,还有另一种起到同样效果的就是goto语句。goto语句适合于多重循环跳出外层循环的场景
# include <stdio.h> int main() { int x; int one, two, five; scanf("%d", &x); for( one = 1; one < x*10; one++){ for( two = 1; two < x*10/2; two++){ for( five = 1; five < x*10/5; five++){ if( one + two*2 + five*5 == x*10 ){ printf("可以用%d个1角+%d个2角+%d个5角得到%d元\n", one, two, five, x); goto out; // 入口 } } } } out: // 出口 return 0; }
建议:goto用多了程序会变得复杂,但在某些情况下使用更为方便。比如:从多重循环中直接跳出 ,减少了接力break。
4.1 循环应用
-
练习1:求和:f(n) = 1+ 1/2 + 1/3 + 1/4 + .... + 1/n
# include <stdio.h> int main() { double sum = 0.0; int n; scanf("%d", &n); for(int i=1; i<=n; i++){ // 起点和终点都确定,用for循环 double j = 1.0/i; sum += j; } printf("f(%d)=%.2lf\n", n, sum); return 0; }
-
练习2:求和:f(n) = 1 - 1/2 + 1/3 - 1/4 + .... + 1/n
# include <stdio.h> int main() { double sum = 0.0; int n; scanf("%d", &n); int flag = 1; // 符号标志 for(int i=1; i<=n; i++){ sum += flag*1.0/i; flag = -flag; } printf("f(%d)=%.2lf\n", n, sum); return 0; }
-
练习3:求两个数的最大公约数(普通方法)
# include <stdio.h> int main() { int a, b; int min; scanf("%d %d", &a, &b); int ret = 0; int i; for(i=1; i < a<b?a:b; i++){ if(a % i == 0 && b % i == 0){ ret = i; } } printf("%d和%d的最大公约数是%d.\n", a, b, ret); return 0; }
-
求两个数的最大公约数(辗转相除法)
/* 1. 如果b等于0, 计算结束, a就是最大公约数; 2. 否则,计算a除以b的余数,让a等于b,而b等于那个余数; 3. 回到第一步 for example: a b2 t 12 18 12 18 12 6 12 6 0 6 0——> exit——>ret = 6 */ # include <stdio.h> int main() { int a, b, t; scanf("%d %d", &a, &b); int ret = a; int b_two = b; while(b_two!=0){ t = ret % b_two; ret = b_two; b_two = t; } printf("%d和%d的最大公约数是%d.\n", a, b, ret); return 0; }
-
-
练习4:正序分解整数(数组方法)
# include <stdio.h> int main() { int number; int count = 0; scanf("%d", &number); int t = number; while(t > 0){ // 判断数是几位数,得出数组应该得有几位数据 t /= 10; count++; } int numbers[count-1]; // 定义几个数据的数组类型0~count-1 int i = 0; while(number > 0){ int single = number % 10; numbers[i] = single; number /= 10; i++; } for(int j=count-1; j>=0; j--){ if(j==0 || j!=count-1) // 间隔空格条件 printf(" "); printf("%d", numbers[j]); } return 0; }
-
正序分解整数(普通方法)
/* x = 13425; 13425 / 10000 ——> 1 13425 % 10000 ——> 3425 10000 / 10 ——> 1000 3425 / 1000 ——> 3 3425 % 1000 ——> 425 1000 / 10 ——> 100 425 / 100 ——> 4 425 % 100 ——> 25 100 / 10 ——> 10 25 / 10 ——> 2 25 % 10 ——> 5 10 / 10 ——> 1 5 / 1 ——> 5 5 % 1 ——> 5 1 / 10 ——> 0 */ # include <stdio.h> int main() { int x; scanf("%d", &x); int mask = 1; int t = x; while(t > 9){ // 判断x是几位数,让mask变成相对应位数 t /= 10; mask *= 10; } printf("x = %d, mask = %d", x, mask); do{ // 将x进行拆分 int d = x / mask; printf("%d", d); if( mask > 9){ // 判断如果不是最后一位即有空格输出 printf(" "); } x %= mask; mask /= 10; }while( mask > 0 ); return 0; }
-