C语言
分支结构
需求:判断一个数是偶数还是奇数
#include <stdio.h>
int main()
{
int num = 5;
// 双分支:if..else
if (num % 2 == 0) printf("%d是偶数!\n");
else printf("%d是奇数!\n");
// 双分支:三目运算符
// 方式1
(num % 2 == 0) ? printf("%d是偶数!\n") : printf("%d是奇数!\n");
// 方式2
printf("%s\n", (num % 2 == 0) ? "偶数" : "奇数");
// 方式3
char* p = (num % 2 == 0) ? "偶数" : "奇数";
printf("%d是%s!\n", num, p)
}
多分支
-
if..else.. 嵌套(不推荐)
-
if..else if....else
-
switch..case..
循环结构
代码在满足某种条件的前提下,重复执行,就叫做循环结构
分类
-
无限循环:其实就是死循环,程序设计中要谨慎使用。
-
有限循环:循环限定循环次数(for)或者终止循环的条件(while, do..while)
构成
-
循环条件:如循环次数或者循环的出口。
-
循环体:需要重复执行的代码(也就是 {} 包裹的内容)
特点:先判断,后执行,如果条件不满足,循环体一次都不执行。 代表:while、for
while 循环
【1】循环变量;
while (【2】循环条件)
{
【3】循环语句;
【4】更新循环变量;
}
注:
-
如果循环语句是单语句,可以省略 {}
while (循环条件) 循环单语句;
while (循环条件)
循环单语句;
-
有些特殊场景下,循环体语句糅合到了循环条件中,所以省略掉循环体
while (循环条件); // int i = 1; while(i++ <= 5); printf("%d\n",i);// 7
注:
-
循环条件的返回值必须是逻辑值(使用非 0 表示真,使用 0 表示假,C 语言底层使用 1 表示真)。C99 版本引入了一个 stdbool.h,可以使用 true 或者 false 来表示真假,这里的 true 和 false 实际上就是定义的两个符号常量,true 的值是 1,false 的值 0。
-
{} 包起来的内容整体称之为循环体。
-
我们要在循环体中控制循环条件的变化,也可以在循环条件中控制循环条件的变化,否则产生死循环。
执行过程:先判断循环条件,若为真则执行循环体,之后再次判断,直到条件为假。特点是先判断,后执行,循环体语句有可能一次都不执行。
例:
求 1~100 的累加和(例如:1+2+3+4+..+100)
-
创建一个变量 sum=0,用来接收累加和
-
创建一个循环变量 i,用来表示计算数,给一个默认值 1,每次循环的时候 i++
-
在循环中,使用 sum +=i(sum=sum+i),完成累加和运算
-
同时我们要限定循环的条件,也就是 i 的范围:i<=100
第 1 次:sum +=i=0+1=1 第 2 次:sum +=i=1+2=3 第 3 次:sum +=i=3+3=6
#include <stdio.h>
int main(int argc, char* argv[])
{
// 需求:求1~100的累加和
// 创建一个变量sum=0,用来存储累加和,因为参与计算,所以必须有初始值
int sum = 0;
// 创建一个循环变量并初始化
int i = 1;
// 通过一个循环生成1~100的自然数,并实现累加和运算
while (i <= 100) // ()循环条件,限制循环变量的变化,循环的出口限制
{
// 累加和运算
sum += i;
// 更新循环变量,逼近出口
i++; // 等价于i = i + 1 i+=1 ++i
}
printf("1~100的累加和是%d\n", sum);
return 0;
}
运行结果:1~100 的累加和是 5050
例:
求 1~100 以内的偶数和 分析:
-
创建一个变量 sum=0,用来存储累加的偶数和
-
创建一个循环变量 i=2,存储参与运算的自然数(最小的偶数是 2)
-
创建一个循环,设置循环条件 i<=100
-
在循环体内,需要用一个 if 校验 i 是否是偶数,如果满足条件,就实现偶数和的计算:sum +=i
-
在循环体的最后一行,需要更新循环变量,作用是逐渐逼近循环的出口
-
循环结束后,才能输出 sum。
#include <stdio.h>
int main(int argc, char* argv[])
{
// 创建一个变量sum=0,用来存储累加的偶数和
int sum = 0;
// 创建一个循环变量i=2,存储参与运算的自然数(最小的偶数是2)
int i = 2;
// 创建一个循环,设置循环条件i <= 100
while (i <= 100)
{
// 在循环体内,需要用一个if校验i是否是偶数,如果满足条件,就实现偶数和的计算:sum +=i
if (i % 2 == 0)
sum += i;
// 在循环体的最后一行,需要更新循环变量,作用是逐渐逼近循环的出口
i++;
// if (i % 2 == 0) sum += i; i++; // 等价于上面代码
}
// 循环结束后,才能输出sum。
printf("1~100以内的偶数和是%d\n", sum);
return 0;
}
运行结果:1~100 以内的偶数和是 2550
例:
通过键盘录入一个整数,判断这个整数是否是水仙花数。 分析: 拆分一个 n 位的数,比如拆分为个位、十位、百位... 要求这个 n 位数的每一位的 n 次幂相加等于这个数本身: 比如一个三位数:个位的 3 次幂 + 十位的 3 次幂 + 百位的 3 次幂 = 这个数本身
#include <stdio.h>
#include <math.h>
int main(int argc, char* argv[])
{
int num, originalNum, n = 0, sum = 0;
// 从控制台录入一个数
printf("请输入一个整数:");
scanf("%d", &num);
// 用一个变量记录这个整数
originalNum = num;
// 计算数字的位数
while (num != 0)
{
num /= 10;
n++; // 统计位数
}
// 恢复原始数据
num = originalNum;
// 计算各位数的n次幂之和
while (num != 0)
{
// 将需要的位数拆出来
int digit = num % 10;
// 计算这个数的n次幂,并相加
sum += pow(digit, n); // pow(运算数,运算数的n次方)
// 将需要拆除的位保留在个位
num /= 10;
}
// 校验并输出
if (sum == originalNum)
printf("%d 是水仙花数。\n", originalNum);
else
printf("%d 不是水仙花数。\n", originalNum);
return 0;
}
注:
gcc demo06.c -lm //math.h 中有些函数属于 GNU 扩展函数,不是 C 语言标准,需要指定链接 解释:-lm 意思是链接数学库,数学库中有一些函数不是 C 语言标准定义的,属于其他扩展库,C 语言标准库是自动链接的,但是扩展库不会自动链接,需要我们指定链接。
运行效果: 请输入一个整数:131 131 不是水仙花数。 请输入一个整数:1 是水仙花数。 请输入一个整数:153 153 是水仙花数。 请输入一个整数:9474 9474 是水仙花数。 请输入一个整数:370 370 是水仙花数
while 的特殊写法
while(1) // 死循环
for循环
for 循环能实现的,while 循环也可以实现,for 循环可以看做是 while 循环的一种特殊写法,for 循环胜在结构清晰,非常适合于直到循环次数的循环。
for (【1】循环变量; 【2】循环条件; 【4】更新循环变量)
{
【3】循环语句;
}
如果循环体语句是单语句,也是可以省略 {}
for (【1】循环变量; 【2】循环条件; 【4】更新循环变量)【3】循环语句;
for (【1】循环变量; 【2】循环条件; 【4】更新循环变量)
【3】循环语句;
注:
-
() 中可以仅保留两个;;,举例:for (;;),此时这种写法就是死循环的一种体现。
-
循环变量:需要赋初始值,循环变量可以是单个,也可以是列表,多个循环变量之间使用逗号分隔
// 循环变量是单个
for (int i = 0;..;..) {..}
// 循环变量是多个
for (int i = 0,j = len -1;..;..){..}
3.循环条件:用来限制循环的次数,循环条件支持任意表达式(常用的有关系表达式,逻辑表达式)
for (..;i < 10 && j >= 0;..){..}
4.更新循环变量:支持列表,列表使用逗号分隔,这里可以使用赋值表达式
for (..;..;i++,j--){..}
5.执行顺序:①②③④→②③④→②③④→...→②
执行过程:先求解表达式 1,再判断表达式 2,若为真则执行循环体,再求解表达式 3,之后再次判断表达式 2,直到为假。特点是先判断,后执行,循环体语句有可能一次都不执行。
例:
计算 1~100 以内的偶数和
#include <stdio.h>
/**
* 使用while实现
*/
int while_test()
{
int sum = 0;
// 循环变量(需要初始化)
int i = 2;
// 循环条件
while (i <= 100)
{
// 循环语句
if (i % 2 == 0) sum += i;
// 更新循环变量
i++;
}
// 打印输出
printf("1~100以内的偶数和是%d\n", sum);
return 0;
}
/**
* 使用for实现(不推荐)
*/
int for_test()
{
int sum = 0;
// 循环变量(需要初始化)
int i = 2;
// 循环条件
for (;i <= 100;)
{
// 循环语句
if (i % 2 == 0) sum += i;
// 更新循环变量
i++;
}
// 打印输出
printf("1~100以内的偶数和是%d\n", sum);
return 0;
}
/**
* 使用for实现(推荐)
*/
int for_test2()
{
int sum = 0;
// 循环条件
for (int i = 2; i <= 100; i++)
{
// 循环语句
if (i % 2 == 0) sum += i;
}
// 打印输出
printf("1~100以内的偶数和是%d\n", sum);
return 0;
}
int main(int argc, char* argv[])
{
while_test();
for_test();
for_test2();
return 0;
}
例:
用户通过键盘输入一个整数 n,求 n 的阶乘。例如:n=4,n 的阶乘 123*4
#include <stdio.h>
// 数学库对应的头文件
#include <math.h>
int main(int argc, char* argv[])
{
// 创建一个变量,用来存储计算数
unsigned long n;
// 创建一个变量,用来存储乘积 size_t 是C语言中 unsigned long的别名
size_t r = 1;
// 通过控制台,给n赋值
printf("请输入一个整数:\n");
scanf("%lu", &n);
// 通过for循环计算
for (int i = 1; i <= fabs(n); i++) r *= i;
printf("1~%lu的乘积结果是%lu\n", (size_t)fabs(n), r);
return 0;
}
注:
阶乘计算,如果乘积超过变量存储的范围,会溢出,结果计算可能不准确。
for 实现死循环
for(表达式1;;表达式3) {..}
for(表达式1;;) {..}
for(;;表达式3) {..}
for(;;) {}
for(表达式1;表达式2;) {}
循环实现的三要素
-
循环变量的初始化,举例:int i=2
-
循环条件,举例:i <= 100
-
循环变量的更新,举例:i++
例:
斐波拉契数列指的是是符合一定规则的数列,举例:1,1,2,3,5,8... 斐波拉契数列的特点:第 3 个数等于前两个数之和,最开始的第 1,2 个数是固定的,都是 1
#include <stdio.h>
int main(int argc, char* argv[])
{
// 创建一个变量,存储第1,2个数,默认都是1
int f1 = 1, f2 = 1;
// 定义循环变量
int i = 1; // 每一次循环产生2个数,也就是循环10次
// 通过循环产生数列
for (; i <= 10; i++)
{
// 输出当前两个数列
printf("%-12d%-12d\n", f1, f2); // 1,1
// 计算得到新的f1,f2
f1 += f2; // f1 = f1 + f2; // 第1次:f1 = 1+1 = 2 ..
f2 += f1; // f2 = f2 + f1; // 第1次:f2 = 1 + 2 = 3 ..
}
return 0;
}
运行结果: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
练习
求 1~max_value 以内的所有水仙花数,max_value 的范围自己指定。
直到型循环的实现
特点:先执行,后判断,不管条件是否满足,至少执行一次。 代表:do..while
do..while 循环
【1】循环变量;
do
{
【2】循环体语句;
【3】更新循环变量;
} while(【4】循环条件);
注:
-
循环条件的返回值必须是逻辑值(0 和非 0(计算机返回 1))
-
{} 包起来的内容称之为循环体
-
我们要在循环体中控制循环条件的变化,否则会产生死循环。
执行过程:先执行循环体语句,然后判断循环条件,若为真则再次执行循环体,直到条件为假。特点是先执行,后判断,不管条件是否满足,循环体语句至少执行一次
例:
求 1~100 以内的偶数和
-
创建一个变量,用来存储偶数和 sum=0
-
创建一个循环变量,i=2
-
创建一个 do..while 循环,在循环体中,校验 i%2==0,如果满足,就实现偶数和计算 sum +=i
-
计算完成,在 do..while 循环体的末行,更新循环变量,使其逼近出口
-
限制性循环的出口:i <= 100(i>100,循环结束)
-
循环结束,打印输出 sum 的值
#include <stdio.h>
int main(int argc, char* argv[])
{
// 创建一个变量,用来存储偶数和sum=0
int sum = 0;
// 创建一个循环变量,i=2
int i = 2;
// 创建一个do..while循环,在循环体中,校验i%2==0,如果满足,就实现偶数和计算sum +=i
do
{
if (i % 2 == 0) sum += i;
// 计算完成,在do..while循环体的末行,更新循环变量,使其逼近出口
i++;
} while (i <= 100);
// 限制性循环的出口:i <= 100(i>100,循环结束)
// 循环结束,打印输出sum的值
printf("1~100以内的偶数和是%d\n", sum);
return 0;
}
例:
用 C 语言编写简单猜数字游戏代码。游戏规则是程序随机生成一个 1 到 10 之间的数字,玩家通过输入猜测数字,程序会提示猜测是太大还是太小,直到玩家猜中为止。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* argv[])
{
// 创建变量number(随机数),guess(用户猜测的数字),count(计数)
int number, guess, count = 0;
// 设置随机种子
srand(time(NULL));
// 生成随机数 1~10之间的随机数 rand() % 最大范围整数,生成的实际数的范围 0 ~ 最大范围整数 - 1
number = rand() % 10 + 1;
// 设计头
printf("----------------------\n");
printf("| 猜数字小游戏v1.0 |\n");
printf("----------------------\n\n");
printf("我已经想了一个1~10之间的整数,你猜猜是多少?\n");
// 猜数字,猜不对,就一直猜,猜到对为止
do
{
printf("请输入你的猜测:\n");
// 接收scanf的返回值
int result = scanf("%d", &guess);
// 对非法输入进行校验(比如:我们要求输入数值,但是不小心输入了非数值,此时就是非法输入)
if (result != 1)
{
// 如果输入的不是数字,就是非法的,也就是输入的内容类型跟scanf提供的格式化符号不匹配
// 每次重新输入前,需要清空输入缓冲区,重新从外部设备获取输入到缓冲区
while (getchar() != '\n'); // getchar() 类似于scanf("%c",c);
printf("请输入数字!\n");
continue; // 这次循环,跳过不算
}
// 计算
count++;
// 校验
if (guess > number) printf("太大了,再猜一次!\n");
else if (guess < number) printf("太小了,再猜一次!\n");
else
{
printf("恭喜你,猜对了!\n");
printf("你一共猜了%d次!\n", count);
}
} while (guess != number); // 等价于 guess > number || guess < number
return 0;
}
----------------------
| 猜数字小游戏v1.0 |
----------------------
我已经想了一个1~10之间的整数,你猜猜是多少?
请输入你的猜测:
a
请输入数字!
请输入你的猜测:
5
太大了,再猜一次!
请输入你的猜测:
3
太小了,再猜一次!
请输入你的猜测:
4
恭喜你,猜对了!
你一共猜了3次!
goto(了解)
goto 标签(label)
标明目标代码的位置,是一个不加 "" 的字符串
例:
求 1~100 以内的偶数和
#include <stdio.h>
int main(int argc, char* argv[])
{
// 创建一个变量,用来存放偶数和
int sum = 0;
// 创建一个循环变量
int i = 2;
// 定义goto标签,名字自己取,符合标识符命名即可
loop:
// 过滤偶数
if (i % 2 == 0) sum += i; // 偶数和计算
i++; // 更新循环变量
// 循环条件
if (i <= 100) goto loop; // 标签loop由goto触发
printf("1~100以内的偶数和是%d\n", sum);
return 0;
}
注:
-
可读性:goto 语句会破坏代码的结构和可读性,使得代码难以理解和维护。因此,应尽量避免使用。
-
替代方案:大多数情况下,可以使用循环、条件语句、函数等结构来替代 goto 语句,使代码更加清晰和易于管理。
-
嵌套限制:虽然 goto 语句可以跨函数跳转(即跳转到另一个函数中的标签),但这种用法是不合法的,并且会导致编译错误。goto 语句只能在同一函数内部跳转。
-
错误处理:在某些情况下,goto 语句可以用于错误处理,例如从嵌套的多层循环中跳出。但即使在这种情况下,也应谨慎使用,并考虑是否有更好的替代方案。
总结 虽然 goto 语句在 C 语言中是合法的,并且有时可能看起来很方便,但过度使用或不当使用会导致代码质量下降。因此,建议尽量避免使用 goto 语句,而是采用更结构化和可维护的编程方法。
循环结构的典型应用场景
-
求累和:举例:1+2+3..+100 的和,sum=0
-
求累积:举例:123..*100 的积,result=1
-
求均值:举例:(1+2+3...+100)/100 的值
-
求极值:举例:12,34,56,67 中的最大值、最小值
-
元素遍历:常用于数组元素的遍历。
-
...
基础算法模型
-
累和 定义一个变量(sum),并赋初值为 0; 该变量累加(+=)每一个数据项(i); 当访问完每一个数据项,此时该变量的取值就是累加和的结果。
-
累乘 定义一个变量,并赋初值为 1; 用该变量累乘(*=)每一个数据项; 当访问完每一个数据项,此时该变量的取值就是累乘的结果。
-
极值(多应用于数组) 定义一个变量,并赋初值为第一个数据项; 从第二个数据项开始,依次于该变量进行比较,如果大于 / 小于该变量,则将当前数据项的数据赋值给该变量。 当访问完每一个数据项,此时该变量的取值就是求极值的结果。
循环的嵌套
3 种循环(while、for、do..while)可以相互嵌套的。在前一个循环结构中又嵌套一个循环结构
例:
while()
{
do
{
...
}while();
while()
{
...
}
}
do
{
for(;;)
{
...
while()
{
...
}
}
}while();
例:
九九乘法表
我们发现九九乘法表整体其实就是一个 9 行 9 列的直角三角形 同时发现:每一行显示的列的数量不超过所在行,第 1 行 1 列,第 2 行 2 列... 假定:行用 i 表示,列用 j 表示,i 和 j 的关系:j <=i 在实现的时候,我们发现需要同时控制行和列的变化,在编程中,行列式需要通过 for 双层嵌套实现(双重 for 循环)。
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("--- 九九乘法表 ---\n");
// 外层循环:生成行,1~9
for (int i = 1; i <= 9; i++)
// 内层循环:生成列,1~(j <= i)
for (int j = 1; j <= i; j++)
// 生成算式
printf("%dx%d=%d\t", j, i, j*i);
// printf("*"); // 替换成这行代码,输出下直角三角形
// 每一行所有列生成完毕,一定要换行
printf("\n");
printf("\n");
return 0;
}
--- 九九乘法表 ---
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
1x4=4 2x4=8 3x4=12 4x4=16
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
例:
求 100~200 之间的所有的素数(素数又被称作质数)
什么是素数:只能被 1 和自身整除的数叫做素数或者质数。
#include <stdio.h>
int main(int argc, char* argv[])
{
// 创建一个变量,存放100~200之间的自然数
int num = 100;
// 定义一个循环变量i
int i;
// 定义标志位,1-素数,0-非素数
int flag;
// 外层循环:生成100~200之间的自然数
for (; num <= 200; num++)
{
// 每一个num生成的时候,默认其为素数
flag = 1;
// 内层循环:我们让num 对 2~num/2 |(num -1)之间的数进行整除,一旦出现一次整除的情况,我们认定其为非素数
for (i = 2; i < num / 2; i++)
{
// 测试整除的情况
if (num % i == 0)
{
flag = 0; // 非素数
// 循环结束,只要出现一次能整除的情况,就说明这个数不是素数。break是提前结束了循环
break;
}
}
// 输出校验结果
if (flag) // 等价于 flag == 1
printf("%-4d", num);
}
printf("\n");
return 0;
}
101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199