文章目录
c语言是结构化的程序设计语言~
顺序结构 分支结构/选择结构 循环结构
生活中所有事都是这3种结构的组合而已
1.什么是语句?
- 表达式语句
- 函数调用语句
- 控制语句
- 复合语句
- 空语句
c=a+b;//表达式语句
printf("%d\n", strlen("中国"));//函数调用语句
if//switch控制语句
; //空语句
控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有九种控制语句
- 条件判断语句也叫分支语句:if语句、switch语句;
- 循环执行语句:do while语句、while语句、for语句;
- 转向语句:break语句、goto语句、continue语句、return语句。
2.分支语句
if语句
if语句
- if(表达式)
语句;
什么是真,什么是假
c语言规定:0就是假,非0就是真 - if(表达式)
语句1;
else
语句2; - if(表达式1)
语句1;
else if(表达式2)
语句2;
else
语句3;
int main()
{
int age = 0;
scanf("%d", &age);
/*if (age >= 18)
{
printf("成年\n");
}*/
/*if (age >= 18)
{
printf("成年\n");
}
else
{
printf("未成年");
}*/
if (age < 18)
{
printf("少年\n");
}
else if(age>=18&&age<40)
{
printf("青年\n");
}
else if (age >= 40 && age < 60)
{
printf("壮年\n");
}
else if (age >= 60 && age < 90)
{
printf("老年\n");
}
else
{
printf("老寿星\n");
}
}
if(18<=age<40)
18<=12 为假 0
0小于40恒成立
运行逻辑有错误,语法上没错,逻辑上不满足要求
如果条件成立,要执行多条语句,应该使用代码块。
int main()
{
if(表达式)
{
语句列表1;
}
else
{
语句列表2;
}
return 0;
}
代码规范问题
//关于悬空else
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if(a == 1)
if(b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
输出结果是什么呢?
什么也不输出,第一个if都没进去
else与最近的if配对,与怎么样对齐没啥关系
//适当的使用{}可以使代码的逻辑更加清楚。
//代码风格很重要
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if(a == 1)
{
if(b == 2)
{
printf("hehe\n");
}
}
else
{
printf("haha\n");
}
return 0;
}
特别烂的代码风格
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
if书写形式对比
//代码1
if (condition) {
return x;
}
return y;
差的代码,可读性差
//等价于代码2
if(condition)
{
return x;
}
else
{
return y;
}
//代码3
int num = 1;
if(num == 5)
{
printf("hehe\n");
}
//num==5 容易写成num=5
换一种写法
//代码4
int num = 1;
if(5 == num)
{
printf("hehe\n");
}
//如果写错的时候 5=a编译是不通过的
//5==a 与a==5等价
写好类似于5==a的代码
练习
输出1-100的奇数
//法一
int main()
{
int i = 0;
while (i <= 100)
{
if (i % 2 == 1)
{
printf("%d ",i);
}
i++;
}
return 0;
}
//法二
int main()
{
int i = 1;
while (i <= 100)
{
printf("%d ", i);
i += 2;
}
return 0;
}
//非常高效
switch
多分支语句转化为switch case比较好
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期天\n");
break;
}
return 0;
}
switch 后面必须是整型表达式
如果 float day=0;
则编译不会通过
case 后面必须是**整型常量表达式**
case 1.0 报错
switch()括号内表达式
switch()语句的括号内的表示式必须是整型,它要与case 里的值呼应.
字元型,只有字元常量可以,例如 ‘a’ ‘b’ ‘6’ '\r’实际上是整型.
布林型 – 真,假, 实际上是 0, 1
enum 型也可以, 因为它模拟0,1,2,3…
可以时char,long,int但不能是float
#include <stdio.h>
//switch代码演示
int main()
{
int day = 0;
scanf("%d", &day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("weekday\n");
break;
case 6:
case 7:
printf("weekend\n");
break;
}
return 0;
}
break语句的实际效果是把语句列表划分为不同的分支部分
真正实现分支依靠的是break
default
当 switch 表达式的值并不匹配所有 case 标签的值时,这个 default 子句后面的语句就会执行。
所以,每个switch语句中只能出现一条default子句。
利用好default 可以提示用户输入了错误的值
default:
printf("请输入1-7的数值\n");
break;
defualt的顺序随意放置
编程好习惯,default后面也加上break
练习
#include <stdio.h>
int main()
{
int n = 1;
int m = 2;
switch (n)
{
case 1:
m++;
case 2:
n++;
case 3:
switch (n)
{//switch允许嵌套使用
case 1:
n++;
case 2:
m++;
n++;
break; //跳出case3这一层选择
}
case 4:
m++;
break;
default:
break;
}
printf("m = %d, n = %d\n", m, n);
return 0;
}
输出结果 5 3
break只能跳出一层选择
3.循环语句
while
#include <stdio.h>
int main()
{
while (1)
{
printf("呵呵");
}
return 0;
}
//死循环打印呵呵
ctrl+c可以终止死循环
while(表达式)
循环语句;
break/continue
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 10)
{
if (5 == i)
continue;
printf("%d ", i);
i++;
}
return 0;
}
结果为1 2 3 4
i=5时,跳过了continue后面的代码,也跳过了i++的机会
然后重新去判断,i恒等于5,后面的语句一直被跳过,
因此只打印出了1 2 3 4
while中的break是用于永久终止循环的。
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,而是直接跳转到while语句的判断部分。进行下一次循环的入口判断
getchar/putchar
Read a character from a stream (getc, getwc), or get a character from stdin (getchar, getwchar).
stdin标准输入,从键盘获取
int getchar( void );
返回类型是int
Return ValueEach of these functions returns the character read. To indicate an read error or end-of-file condition, getc and getchar return EOF
//getchar读取失败时返回EOF,即-1
putchar-输出一个字符
Writes a character to a stream (putc, putwc) or to stdout (putchar, putwchar).
stdout标准输出–屏幕
int putchar( int c );
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//getchar
//EOF
//转到定义发现EOF就是-1
//#define EOF (-1)
int main()
{
//获取的字符,为什么用int接收?
//'a'-97,字符本质是ASCII表示
//getchar读取失败时返回EOF,即-1
//-1是整型,需要4字节空间才能储存
//所以如果用char ch就不合适了
int ch = getchar();
//printf("%c\n", ch);
putchar(ch);
return 0;
}
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF)
{
putchar(ch);
}
return 0;
}
ctrl+Z可以输出^Z,EOF文件结束标志
缓冲区问题
int main()
{
char input[20] = { 0 };
printf("请输入密码:>");
scanf("%s", input);//abcdef
//数组名本身就是地址,不需要&
printf("请确认密码(Y/N):>");
int ch = getchar();
if (ch == 'Y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
return 0;
}
为什么出问题呢?
scanf其实去看输入缓冲区是否有东西
如果没有,就等待
当键盘敲下abcdef时
输入缓冲区里面是abcdef\n,此时scanf直接拿走abcdef
输入缓冲区中剩余\n
然后getchar又去输入缓冲区中拿东西,直接拿走\n
ch = \n
出问题原因就是输入缓冲区有东西对getchar造成干扰
再用一个getchar清理掉输入缓冲区中的内容就行
int main()
{
char input[20] = { 0 };
printf("请输入密码:>");
scanf("%s", input);//abcdef
getchar();//拿走\n
printf("请确认密码(Y/N):>");
int ch = getchar();
if (ch == 'Y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
return 0;
}
请输入密码:>abcdef hehe
请确认密码(Y/N):>确认失败
如果输入时有空格,又出错了
此时输入缓冲区有abcdef hehe\n
scanf("%s",…)读取字符串时,遇到空格就结束了
缓冲区中还有hehe\n
一个getchar只能拿走一个h
考虑用循环
清理缓存区
int tmp = 0;
while ((tmp = getchar()) != '\n')
{
;//空语句
}
int main()
{
char input[20] = { 0 };
printf("请输入密码:>");
scanf("%s", input);//abcdef
//getchar();//拿走\n
//清理缓存区
int tmp = 0;
while ((tmp = getchar()) != '\n')
{
;//空语句
}
printf("请确认密码(Y/N):>");
int ch = getchar();
if (ch == 'Y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
return 0;
}
#include <stdio.h>
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < ‘0’ || ch > ‘9’)//非数字字符
continue;
putchar(ch);
}
return 0;
}
//这个代码的作用是:只打印数字字符,跳过其他字符的、
(ch = getchar())要注意加()
注意优先级比较
ch = getchar() != EOF 这样写就错了
如果输入一个4
getchar来获取时,是理解成字符4,即’4’
for循环
int main()
{
int i = 1;//初始化
while (i <= 10)//判断
{
printf("%d ", i);
i++;//调整部分
}
return 0;
}
//代码过长,3个部分分离,容易出错
引出for 循环
for(表达式1; 表达式2; 表达式3)
循环语句;
初始化,条件判断,调整
#include <stdio.h>
int main()
{
int i = 0;
//for(i=1/*初始化*/; i<=10/*判断部分*/; i++/*调整部分*/)
for(i=1; i<=10; i++)
{
printf("%d ", i);
}
return 0;
}
break/continue
#include <stdio.h>
int main()
{
int i = 0;
for(i=1; i<=10; i++)
{
if(i == 5)
break;
printf("%d ",i);
}
return 0;
}
输出
1 2 3 4
#include <stdio.h>
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (i == 5)
continue;
printf("%d ", i);
}
return 0;
}
输出
1 2 3 4 6 7 8 9 10
continue跳过continue后面的代码
for循环控制变量
- 不可在for 循环体内修改循环变量,防止 for 循环失去控制。
- 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。
#include <stdio.h>
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
i = 0;//改变了循环变量
printf("hehe");
}
return 0;
}
死循环,循环控制发生了变化
//前闭后开的写法
for(i=0; i<10; i++)
{}
//两边都是闭区间
for(i=0; i<=9; i++)
{}
<10 与 <=9 效果一样
但建议前闭后开
10就具有某种意义
循环10次…
for循环变种
int main()
{
for (;;)
{
printf("hehe\n");
}
return 0;
}
//死循环
for循环中的初始化部分,判断部分,调整部分是可以省略的
中间的判断部分如果省略,意味着恒为真,就构成了死循环
如果条件允许,不建议省略for循环的3个表达式,
省略了可能带来不可预测的问题
int main()
{
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
printf("hehe\n");
}
}
return 0;
}
9个hehe
int main()
{
int i = 0;
int j = 0;
for (; i < 3; i++)
{
for (; j < 3; j++)
{
printf("hehe\n");
}
}
return 0;
}
只有3个hehe了
内层循环执行完3次后,j依旧是3
出内层循环时,i++ i=2了,再进内层循环,但此时没有初始化j,
j依旧是3,不满足循环条件
循环结束…
循环变量也可以用多个变量控制
int main()
{
int x, y;
for (x = 0, y = 0; x < 2 && y < 5; ++x, y++)
{
printf("hehe\n");
}
return 0;
}
一道笔试题
//请问循环要循环多少次?
#include <stdio.h>
int main()
{
int i = 0;
int k = 0;
for (i = 0, k = 0; k = 0; i++, k++)//k=0是赋值而不是判断
//== 写成了赋值=
k++;
return 0;
}
0次
新的语言 如go语言等 就只有for循环了
go语言Google开发的
do while
int main()
{
int i = 1;
do
{
printf("%d ", i);
i++;
} while (i <= 10);
return 0;
}
do while 语句循环体至少执行一次
break continue
#include <stdio.h>
int main()
{
int i = 10;
do
{
if (5 == i)
break;
printf("%d\n", i);
} while (i < 10);
return 0;
}
输出
1 2 3 4
#include <stdio.h>
int main()
{
int i = 1;
do
{
if(5 == i)
continue;
printf("%d\n", i);
i++;
}while(i<10);
return 0;
}
1 2 3 4 然后死循环了
屏幕上一直在闪,表示死循环了
练习
1.计算n的阶乘
输入n
产生1-n的数
打印
int main()
{
int n = 0;
scanf("%d", &n);
int i = 0;
int ret = 1;
for (i = 1; i <= n; i++)
{
ret *= i;
}
printf("%d\n", ret);
return 0;
}
2.计算 1!+2!+3!+……+10!
int main()
{
int n = 0;
scanf("%d", &n);
int i = 0;
int j = 0;
int ret = 1;
int sum = 0;
for (j = 1; j <= n; j++)
{
for (i = 1; i <= j; i++)
{
ret *= i;
}
printf("%d\n", ret);
sum += ret;
ret = 1;//ret要reset为1
}
//printf("%d\n", ret);
printf("sum = %d\n", sum);
return 0;
}
结果
10
1
2
6
24
120
720
5040
40320
362880
3628800
sum = 4037913
ret 每次应该从1开始
for (i = 1,ret = 1; i <= j; i++)
ret=1放到for里初始化更易懂
2层for循环,时间效率O(N^2)
ret* 1
ret* 1 * 2
ret* 1 * 2 * 3
…
优化一下
只需一个for循环就行
int main()
{
int n = 0;
scanf("%d", &n);
int j = 0;
int ret = 1;
int sum = 0;
for (j = 1; j <= n; j++)
{
ret *= j;
printf("%d\n", ret);
sum += ret;
}
printf("sum = %d\n", sum);
return 0;
}
3. 在一个有序数组中查找具体的某个数字n。
暴力查找
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;
int i = 0;
for (i = 0; i < 10; i++)
{
if (arr[i] == k)
{
printf("找到了,下标是%d\n", i);
break;
}
}
if (i == 10)
{
printf("找不到\n");
}
return 0;
}
二分查找/折半查找
#include<stdio.h>
//有序数组找某个数字
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
printf("找到了,下标是%d\n", mid);
break;
}
}
if (left > right)
{
printf("找不到\n");
}
return 0;
}
4. 编写代码,演示多个字符从两端移动,向中间汇聚。
演示效果:
hello bit!!!
################
h##############!
…
hello bit!!!
#include<stdio.h>
#include<string.h>
#include<windows.h>
int main()
{
//char arr[] = "abc";
//[a b c \0]
// 0 1 2
// 3 - 1 = 2
char arr1[] = "hello bit!!!!!!!";
char arr2[] = "################";
int left = 0;
//int right = sizeof(arr1) - 2;
//sizeof 要-2 因为包含了\0
int right = strlen(arr1) - 1;
//strlen计算时不包含\0
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
printf("%s\n", arr2);
Sleep(500);//睡眠函数-单位是ms
system("cls");//执行系统命令,清理屏幕
left++;
right--;
}
printf("%s\n", arr2);
return 0;
}
5.模拟用户登录情景,
模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误,则退出程序。)
#include<stdio.h>
#include<string.h>
int main()
{
int i = 0;
char password[20] = "";
//假设密码"123456"
for (i = 0; i < 3; i++)
{
printf("请输入密码:>");
scanf("%s", password);
//password是数组名,本来就表示地址
//if (password == "123456")
//{
//比较2个字符串是否相等,不能使用==
//用strcmp string compare
//}
//返回0,说明2个字符串相等
//也可能会返回小于0,大于0的数字
if (strcmp(password, "123456") == 0)
//string compare
{
printf("登陆成功\n");
break;
}
else
{
printf("密码错误\n");
}
}
if (i == 3)
{
printf("3次密码均错误,退出程序\n");
}
return 0;
}
正常来说,密码是加密后放到数据库里的
BC33.计算平均成绩
float avg = 0.0f;要加上f才表示float,不加则默认double
%.1f表示保留一位小数
小数在内存中很难精确保存,一般都会有误差
BC19 方向输出一个四位数
求最大公约数
辗转相除法
用大数对小数求余,若余数为0,则除数为最大公约数。若余数不为0,将此余数作为除数,小数作为被除数,重新求余,直到余数为0为止。此时的最大公约数为余数。例如:27和6 27%6=3. 6%3=0. 所以最大公约数为3.
最小公倍数=2数相乘除最大公约数
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
int c = 0;
scanf("%d %d", &a, &b);
c = a % b;//不管大除小还是小除大都行
//假设a>b就行,即使a<b也会进入循环,如何把b赋给a
int x = a;
int y = b;
while (c)
{
a = b;
b = c;
c = a % b;
}
printf("最大公约数为:%d 最小公倍数为:%d\n", b, x * y / b);
return 0;
}
//最直接的方法:
int main()
{
int m = 0;
int n = 0;
scanf("%d %d", &m, &n);
//求最大公约数
int ret = 0;
//1. 求m和n 较小值,假设就是最大公约数
if (m > n)
ret = n;
else
ret = m;
//一个一个数尝试就行
while (1)
{
if (m % ret == 0 && n % ret == 0)
{
break;
}
ret--;
}
//打印
printf("%d\n", ret);
return 0;
}
打印闰年
能被4整除且不能被100整除或者能被400整除
int main()
{
int year = 0;
for (year = 1000; year <= 2000; year++)
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
printf("%d ", year);
}
}
return 0;
}
打印素数
素数也叫质数
只能被1和它本身整除的数字就是素数
试除法:
判断y是不是素数,
拿2 –> i-1的数字去试除
int main()
{
int y = 0;
for (y = 100; y <= 200; y++)
{
//判断y是不是素数
//拿2~y-1的数字去试除y就行
int n = 0;
int flag = 1;//假设y是素数
for (n = 2; n < y; n++)
{
if (y % n == 0)//n能整除y
{
flag = 0;//y不是素数就立刻跳出
break;
}
}
if (flag == 1)
printf("%d ", y);
}
return 0;
}
//优化:
//n<=sqrt(y)且偶数也不可能是素数
#include <math.h>
#include<stdio.h>
int main()
{
int y = 0;
int count = 0;
for (y = 101; y <= 200; y+=2)//+=2p
{
int n = 0;
int flag = 1;//假设y是素数
for (n = 2; n <= sqrt(y); n++)
{
if (y % n == 0)
{
flag = 0;//y不是素数
break;
}
}
if (flag == 1)
{
printf("%d ", y);
count++;
}
}
printf("\ncount = %d\n", count);
return 0;
}
以上方法都是试除法
6.猜数字游戏
//猜数字游戏
//随机生成一个1-100数字
//电脑会告诉玩家猜大还是猜小了
//猜对了,恭喜玩家并告知猜的次数
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void menu()
{
printf("*********************************\n");
printf("********* 1.play ***********\n");
printf("********* 0.exit ***********\n");
printf("*********************************\n");
}
//猜数字的过程
//time 返回值是time_t 其实是int的重定义
//而srand接收值是unsigned int
//为了生成随机数,需要一个随机数???
//只需要给srand传经常发生变化的值就行,每一秒时间都会产生数字
// 时间戳 当前时间和计算机起始时间差值 19700101
//生成随机数rand函数 0~RAND_MAX
//#define RAND_MAX 0x7fff,32767
void game()
{
int guess = 0;
int count = 0;
int r = rand() % 100 +1;// 0 ~ 99+1
//猜数字
while (1)
{
printf("猜数字\n");
scanf("%d", &guess);
if (guess < r)
{
printf("猜小了\n");
count++;
}
else if (guess > r)
{
printf("猜大了");
count++;
}
else
{
count++;
printf("恭喜你猜对了,猜了%d次\n", count);
break;
}
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//设置随机数生成器,不需要频繁使用srand,执行一次就行
//time函数可以获得时间戳
do
{
//打印菜单,不管三七二十一先打印菜单,do while更合适
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
写算法写出来的随机数,都是伪随机数
真正的随机数是无规律的,而代码写出来的都是有规律的
time从参数是指针,如果不想传参数就写个NULL 而srand的参数却是unsigned int 需要把time转换为unsigned int
go to
#include<stdio.h>
int main()
{
again:
printf("hehe\n");
printf("haha\n");
goto again;
//goto next;也行,但next需要在同一代码块内
return 0;
}
死循环打印hehe haha
goto会乱跳,打乱程序的执行流程,代码可控性降低,风险提高
goto不是个好东西,可能会带来意想不到的错误,慎用
理论上讲,goto语句是可以不存在的
那goto语句有啥用呢?
最常见的用法就是终止程序在某些深度嵌套的结构的处理过
程。
for(...)
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
…
error:
if(disaster)
// 处理错误情况
多层循环这种情况使用break是达不到目的的。它只能从最内层循环退出到上一层的循环。
如何替换goto?
关机程序
//只要程序启动,倒计时60s关机,如果60s内输入:我是猪 就取消关机 如果不取消,就倒计时关机
//shutdown Windows提供的关机命令
//shutdown -s -t 60 60秒后关机
//shutdown -a 取消关机
//system是一个库函数,专门用来执行系统命令
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 6000");
again:
printf("请注意,你的电脑在100分钟内关机,如果输入:我是猪,就取消关机\n");
scanf("%s", input);
//判断
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
}
else
{
goto again;
}
return 0;
}
用while替代
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main()
{
char input[20] = { 0 };
system("shutdown -s -t 6000");
while (1)
{
printf("请注意,你的电脑在100分钟内关机,如果输入:我是猪,就取消关机\n");
scanf("%s", input);
//判断
if (strcmp(input, "我是猪") == 0)
{
system("shutdown -a");
break;
}
}
return 0;
}
死循环
go to
while
for