目录
Getchar && Putchar:字符输入 &&字符输出 函数
1.C语言,是结构化的程序设计语言:
顺序结构
选择结构 if switch
循环结构 while for do.while
2.什么是控制语句?
控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有九种控制语句,可分成以下三类:
1. 条件判断语句也叫分支语句:if语句、switch语句;
2. 循环执行语句:do while语句、while语句、for语句;
3. 转向语句:break语句、goto语句、continue语句、return语句。
3.分支语句(选择语句)
if语句
单分支(只有if一种情况,没有else)
例:
int main()
{
int age = 0;
scanf("%d", &age);
if (age >= 18)
printf("成年\n");
return 0;
}
1.这种就是单分支,如果年龄小于18岁,那么什么事情都不会发生
2.if紧接着的()里,判断的是真假。若age>=18则为真,否则,就为假。
当为真时,才会接着有下面的语句。
3.0为假,非0为真。ps:-1也算真。
双分支(有if和else的情况):
例:
int main()
{
int age = 0;
scanf("%d", &age);
if (age >= 18)
printf("成年\n");
else
printf("未成年\n");
return 0;
}
这种就是双分支
多分支(有if else.if 和else构成)
例:
int main()
{
int age = 0;
scanf("%d", &age);
if (age < 18)
{
printf("青少年\n");
printf("好好学习\n");
}
else if (age >= 18 && age < 30)
{
printf("青年\n");
}
else if (age >= 30 && age < 50)
{
printf("中年\n");
}
else if (age >= 50 && age < 80)
{
printf("中老年\n");
}
else if (age >= 80 && age < 100)
{
printf("老年\n");
}
else
{
printf("老寿星\n");
}
return 0;
}
注意:
1.
注意:
1.如果我这样写,便是错误的:
if(30<=age<50)
这种写法是的意思是:假设我10岁,先执行30<=age这个语句
很明显是假。假则为0。
然后再去判断 0<50 是否为真,很明显是真。
所以会执行接下来的语句。
所以这样写是错误的。
2.
2.在if语句后,默认只能跟一条语句。倘若我这样写,便会报错:
if(age<18)
printf("青少年\n");
printf("好好学习\n");
这样便会报错,因为不能直接仍两条语句。若想执行两条语句,得带上大括号。
if(age<18)
{
printf("青少年\n");
printf("好好学习\n");
}
一个大括号,就是一个代码块
3.悬空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;
}
结果是? haha?
实际上什么都不会打印。
1.else总是与他最近的if相匹配的,会让人误以为是与第一个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;
}
1.注意,if 和else 合起来叫一条语句。
合适的风格可以让你的代码更加清晰明了。
if书写形式的对比:
1.
if (condition)
{
return x;
}
return y;
这个语句的意思其实就是 条件成立就返回x,否则就返回y。
但是这种语句语法没问题,但可读性较差,容易让人产生歧义。
例:
int test()
{
int a = 5;
if (a) //a=5,为真,返回1
return 1;
return 0;
}
int main()
{
int ret = test();
printf("ret = %d\n", ret);
return 0;
}
结果是 ret=1
int test()
{
int a = 0; //0为假,所以范围为0
if (a)
return 1;
return 0;
}
int main()
{
int ret = test();
printf("ret = %d\n", ret);
return 0;
}
结果是ret =0
不过这种写法太容易产生歧义,建议写成:
if(condition)
{
return x;
}
else
{
return y;
}
这样的形式
int test()
{
int a = 5;
if (a) //a=5,为真,返回1
{
return 1;
}
else
{
return 0;
}
}
int main()
{
int ret = test();
printf("ret = %d\n", ret);
return 0;
}
2.
很明显,虽说没有报错,但是结果错误了
因为他是将5重新赋值给num。
所以这里得写成 if(num==5)
为了避免忘记漏写一个=号,建议把常量写在左边:
int main()
{
int num = 1;
if (5 == num)
printf("hehe\n");
return 0;
}
如果少写了一个=号,便会报错,便于找出错误
Switch语句:常常用于多分支的情况
例如:
switch(整型表达式) //只能是整型的,浮点型都不行。switch后面必须是整型
{
语句项;
}
case语句:
case 整形常量表达式: //case后面也必须是整形
语句;
int main()
{
int day = 0;
scanf("%d", &day);//3
switch (day)
{
case 1:
printf("星期1\n");
break;
case 2:
printf("星期2\n");
break;
case 3:
printf("星期3\n");
break;
case 4:
printf("星期4\n");
break;
case 5:
printf("星期5\n");
break;
case 6:
printf("星期6\n");
break;
case 7:
printf("星期天\n");
break;
}
switch决定的是入口。break决定出口。
switch()里面的值,决定我从哪里开始执行。
如果没有break,将会一直安顺序执行下去
switch还有一种用法,例如:
int main()
{
int day = 0;
scanf("%d", &day);//3
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
default:
printf("输入错误\n");
break; //即便是最后一个break最好也别省略
}
return 0;
}
default的作用是:
1.当与所有case的值都不能够匹配的时候,就会走到default。
2.每个switch语句中只能出现一条default子句。
3.他没有任何顺序的要求,放前放后都可以。但是放前面时,注意不要漏掉break。
4.可加可不加,加上其实更完善。
例:
#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;
}
case 4:
m++;
break;
default:
break;
}
printf("m = %d, n = %d\n", m, n);
return 0;
}
答案是 5 3。
循环语句
While
For
Do.while
While:
因为1恒为真,所以会执行
语法形式:
while(表达式)
循环语句;
当while后面的表达式结果为真,就进入循环。直到表达式为假时,循环停止。
例1:
int main()
{
int a = 0;
while(a<=10)
{
printf("%d",a)
a++
}
return 0;
}
例2:
int main()
{
int i = 1;
while (i<=10)
{
if (5 == i)
break;
printf("%d ", i);
i++;
}
return 0;
答案是 1 2 3 4
break 直接终止整个循环
例3:
int main()
{
int i = 1;
while (i<=10)
{
if (5 == i)
continue;
printf("%d ", i);
i++;
}
return 0;
结果是:1 2 3 4 死循环
continue,跳过本次continue后面的代码,直接跳到下一次循环开始判断的地方,进入下一次判断,再走下去。
Getchar && Putchar:字符输入 &&字符输出 函数
1.getchar()函数不带任何参数,他从输入队列中返回下一个字符。
例如:ch=getchar()
与 scanf(“%c",&ch); 效果一致。
2.putchar()函数是打印他的参数。把之前赋给ch的值作为字符打印出来:
例如: putchar(ch)
与 printf(”%c",ch); 效果一致
3.读取一个字符,从一个流或标准输入(键盘上)。
4.返回类型是int。
5.返回正常的话是读取到的那一个字符。
7.返回的值通常介于0-127,这些值对应标准字符集。所以无论哪种情况,-1都不对应任何值。
6.如果读取错误,或者遇到文件结束标志时,返回的是EOF(END OF FILE)。
EOF就是一个常量-1
int main()
{
int ch = getchar();// 因为返回值是int,所以定义最好是int,而不是char
//如果读的是a,返回的就是a的mscii码值。
putchar(ch); //打印这个字符
return 0;
}
接下来,我们来看看这段代码:
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != EOF) //-1
putchar(ch);
return 0;
}
因为没有一个字符可以让ch的返回值为-1,所以就会一直循环下去
这样就有点像多组输入了。
若想要他停下来,按ctrl+Z(可能要多按几下)就能停下来,能让他返回一个eof
←如图
例2.
为什么什么都没让我输入,就直接返回确认失败呢?
1.scanf与getchar 与键盘之间,还有一个缓冲区的概念。
2.scanf看缓冲区什么都没有,就等待键盘输入。输入的东西就存到缓冲区。
当我输入 123456,就相当于给缓冲区里放了 1 2 3 4 5 6 \n
然后 1 2 3 4 5 6,就被我放到passward了。缓冲区里还剩下一个\n
3.然后轮到getchar,他看缓冲区还有一个\n,于是就直接读走了。
所以我们要清除缓冲区。
解决方法一:
但是他有缺陷,就是密码不能有空格
一旦有空格,立马显示失败。
因为scanf如果用%s读取的话,只会读取空格之前的,碰到空格就觉得读取完毕了。这样缓冲区就又多了一些东西
方法二:写while循环
把getchar读取的字符放到tmp里,这样缓冲区就没有\0了,这样便能完美的运行了。
例2:
#include <stdio.h>
int main()
{
char ch = '\0';
while ((ch = getchar()) != EOF)
{
if (ch < ‘0’ || ch > ‘9’)
continue;
putchar(ch);
}
return 0;
}
这个代码的作用是只打印数字字符,跳过其他字符。因为跟ascii值有关
For循环
For语句中的break和continue与while中的break和continue,有何区别?
大家看这个代码执行的结果是什么?
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (5 == i)
break;
printf("%d ", i);
}
return 0;
结果是 1 2 3 4
可见,当执行到break的时候,直接结束此循环。
我们再来看这个代码:
int main()
{
int i = 0;
for (i = 1; i <= 10; i++)
{
if (5 == i)
continue;
printf("%d ", i);
}
return 0;
结果是 1 2 3 4 6 7 8 9 10
没有5
说明for中的continue的作用和while中的也是一致,跳过本次后面的代码,直接回到判断部分,然后由于i++在前面,所以不会造成死循环
int main()
{
int i = 0;
for (i = 1; i <= 10; )
{
if (5 == i)
continue;
i++
printf("%d ", i);
}
return 0;
这样写,就会造成死循环
For语句的循环控制变量
1.循环变量的修改尽量在for()里进行修改,不要放到循环体内进行条件,如上例所示,就容易出现bug。
2.建议for语句的循环控制变量的取值采用“前闭后开区间”写法。
如果写成这样
他的可读性就下降了。
但是最好根据实际情况进行条件:比如说打印100-200的数,就最好写成:
一些For的变种
看这个代码
#include <stdio.h>
int main()
{
for(;;)
{
printf("hehe\n");
}
]
return 0;
他会一直死循环的打印hehe
初始化,判断部分,调整都可以省略。
但如果For循环的判断部分省略了,就意味着恒为真。差不多就像是 for(;1;) 一样
例2:这个打印了多少hehe?
int main()
{
int i = 0;
int j = 0;
for(i=0; i<10; i++)
{
for(j=0; j<10; j++)
{
printf("hehe\n");
}
}
return 0;
}
打印了100个hehe
这个又打印了多少hehe呢?
int main()
{
int i = 0;
int j = 0;
for(; i<10; i++)
{
for(; j<10; j++)
{
printf("hehe\n");
}
}
return 0;
}
只会打印10个hehe
为什么呢?
因为当打印了十个hehe的时候,j已经从0到9了了,当i开始执行的时候,再进入j的时候,已经不符合条件了,所以不打印了。
循环变量也可以用两个变量来控制,例如:
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++;
return 0;
}
因为K=0,压根就不会进入循环,0为假,而不是死循环,=是赋值
DO.WHILE循环
上来直接就开始do,后来才开始判断要做几次,无论如何我都会循环一次。
do.while中的continue和break
break:
由此可见,跳出循环。
continue:由此可见 1234 死循环。
说明continue也是跳过了本次循环后面的代码,直接回到了while()判断部分。
Go.to语句
int main()
{
again:
printf("hello");
goto again;
return 0;
}
1.直接可以跳到我的标签上。
2.可用可无,他的优点就是可以往前跳也可以往后跳,但是不能跨函数跳。
3.最常用的就是跳出多重循环
for (...)
for (...)
{
for (...)
{
if (disaster)
goto error;
}
}
…
error :
if (disaster)
#include<stdlib.h>
int main()
{
int i = 0;
printf("3600秒后关机\n");
system("shutdown -s -t 3600");
printf("\n我不想关机了的话,输入:我要取消\n");
char arr1[20] = { 0 };
int p = 0;
start:
scanf("%s", arr1);
if (strcmp(arr1,"我要取消")==0)
{
system("shutdown -a");
}
else
{
printf("重新输入\n");
goto start;
}
}
/*
#include<stdlib.h>
int main()
{
int i = 0;
printf("3600秒后关机\n");
system("shutdown -s -t 3600");
printf("\n我不想关机了的话,输入:我要取消\n");
char arr1[20] = { 0 };
int p = 0;
while (1)
{
scanf("%s", arr1);
if (strcmp(arr1, "我要取消") == 0)
{
system("shutdown -a");
break;
}
else
{
printf("重新输入\n");
}
}
}
*/
练习:
1. 计算 n的阶乘。
2. 计算 1!+2!+3!+……+10!
3. 在一个有序数组中查找具体的某个数字n。(二分查找)
4. 编写代码,演示多个字符从两端移动,向中间汇聚。
5. 编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则
提示登录成,如果三次均输入错误,则退出程序。
练习题(2):
int main()
{
int i = 0;
int sum = 0;
int sum1 = 1;
for (i = 1; i <= 10; i++)
{
sum1 = sum1 * i;
sum = sum + sum1;
}
printf("%d", sum);
return 0;
}
答案:4037913
练习题(3):有序数组中查找具体的某个数字n (二分查找)
若int arr[10]={1,2,3,4,5,6,7,8,9,10}
求数字K=7的下标?
1.如果我写个循环,从第一个开始一个一个找,也可以,但是就不满足有序了,效率也不高。
2.所以我们最好写成,折半查找
3.10个数,我们先找中间的数,看要找的数,是大于这个数,还是小于这个数,再确定范围,然后再进行折半,再次判断。每次查找一次,范围缩小一半,这样的速度就会很快,效率就会大大提高。
4.前提要求比较苛刻,必须是有序的。
先确定left right两个下标,一步一步逼近要找的值
int main()
{
int a = 0;
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("输入你要查找的数字>");
scanf("%d", &a);
int left = 0;
int right = 9;
while (left<=right)
{
int mid = (left + right) / 2;
if (arr[mid] < a)
{
left = mid + 1; //因为中间的不是要找的数,所以+1,继续找
}
else if (arr[mid] > a)
{
right = mid - 1;
}
else
{
printf("恭喜您,找到了,下标为:%d", mid);
break;
}
}
if (left > right)
{
printf("找不到了");
}
return 0;
}
思考与改进:
1.下标还可以如何表示
2.如果我要求的数特别大,两个数一加,就超过了int的范围,该怎么办?
1):
我们可以这样表示:
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
left = sz-1;
2):
错误思路:2分之a+2分之b 这样会丢失小数
正确思路:
假若我有两栋大楼,一栋高2米,另一栋高1.8米,如何才能让两栋楼一样高?
我们可知,其中一栋楼比另一栋,高出0.2米,我们将多出的这0.2米分一半,高的一份,矮的一份
这样就成了 1.9米 && 1.9米。现在这两个高度,其实就是我们要找的平均值→
→
所以,刚刚算平均值
int mid = (left + right) / 2;
便可以写成:
int mid =left+(left-right)/2;
练习4):演示多个字符从两端移动,向中间汇聚。
int main()
{
char arr1[] = "HELLO WORLD";
char arr2[] = "###########";
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
right--;
left++;
printf("%s\n", arr2);
}
return 0;
}
思考:我如何在一行内展示出效果呢?
#include<string.h>
#include<Windows.h>
int main()
{
char arr1[] = "HELLO WORLD";
char arr2[] = "###########";
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right)
{
arr2[left] = arr1[left];
arr2[right] = arr1[right];
right--;
left++;
printf("%s\n", arr2);
Sleep(1000); //等待1s
system("cls"); //清屏
}
printf("%s\n", arr2);
return 0;
}
练习题(5)编写代码实现,模拟用户登录情景,并且只能登录三次。(只允许输入三次密码,如果密码正确则提示登录成功,如果三次均输入错误,则退出程序:
//字符串不能用=,要用strcmp (String compare)
#include<string.h>
int main()
{
int i = 0;
char password [20] = { 0 };
//假设密码abcdefg
for (i = 0; i < 3; i++)
{
printf("请输入密码:");
scanf("%s", password);
if (strcmp(password,"abcdefg")==0)
{
printf("密码正确\n");
break;
}
else
{
if (i<2)
{
printf("密码错误,重新输入\n");
}
}
}
if (i == 3)
{
printf("三次都错了,再见");
}
return 0;
}
*思考:猜数字游戏