提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
字符串的特点
字符串,最大的几个特点就是
- 常用字符数组储存
- 可以使用指针指向第一个元素地址直接打印,(解引用是一个字符)
- 结尾是 \0
- print() 传参直接传输地址就可以
通过调试,使用 strcpy 后 看看 arr1
在一群 #### 中有一个 \0 ,说明字符时用 \0来结尾的,我们的 printf 也是检测到 \0 就停止打印, strlen 也是统计到 \0 前的字符场长度
输入函数使用的原理
这里的输入函数主要是 getchar 和 scanf
是从缓冲区读取输入,而不是从键盘中直接读取
我们打键盘,所有的都会保留在缓冲区中,然后程序执行的时候读取缓冲区
这就往往会导致一个情况,如果你是连续输入,除了第一次输入,其他输入时缓冲区不是空缓冲区
为啥要有缓冲区?
为了让低速的输入输出设备和高速的用户程序能够协调工作,并降低输入输出设备的读写次数。
理解仅供参考
- 我们键盘输入,如果直接读取键盘,那么我们每一次输入都要让程序去和硬件打交道,cpu要切换模式,这里耗费的时间长
使用缓冲区,就减少了模式切换,大大提升效率 - 使用缓冲区,用户输入的全部输入到缓冲区中,如果是字符串,程序可以直接一次就把有效字符读取出来,如果使用读取键盘方式,用户输入一个字符,就要读取一个字符,要读取多次
getchar的使用
getchar
使用和使用原理
(int) getchar (void);
- 返回值是读取字符的 ascii 码值
- getchar在读取结束或者失败的时候,会返回EOF(定义为 -1)
当我们要读取字符时
ch = getchar()
用一个例子讲透getchar
写一个程序,输入一个密码,然后确认
- 第一个版本
这个例子是有问题的,问题在哪里嘞?
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main()
{
char ch = 0;
char arr[20];
printf("请输入密码:>");
scanf("%s", arr);
printf("请确认(Y/N)");
ch = getchar();
if (ch == 'Y')
{
printf("输入成功");
}
else
{
printf("输入失败");
}
return 0;
}
看看现象
输入密码之后,我还没来得及输入 Y/N 就直接输入失败了
为什么?
从原理看,我最后终止 scanf 输入是按了 回车键 但是 scanf 不会读取回车键 ,回车键就保留在缓冲区了
然后 getchar 读取缓冲区的回车键
直接输出 输入失败
- 第二个版本
清空回车键,但是用例还是有问题
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main()
{
char ch = 0;
char arr[20];
printf("请输入密码:>");
scanf("%s", arr);
printf("请确认(Y/N)");
getchar();
ch = getchar();
if (ch == 'Y')
{
printf("输入成功");
}
else
{
printf("输入失败");
}
return 0;
}
我加了一个没有返回的 getchar 最后结果是这样的
但是,如果我添加空格,就会出问题
同理,scanf 读取到字符串的时候 会停下来,之后getchar 清空,但是缓冲区中有 ’ 456’ 一个getchar 无法清空,最终直接输入失败
- 第三个版本
#include<stdio.h>
int main()
{
char password[20] = {0};
printf("请输入密码:>");
scanf("%s", password);
//把缓冲区中的内容全读走
while ( getchar() != '\n')
{
;
}
printf("请确认密码(Y/N):>");
int ch = getchar();
if (ch == 'Y')
{
printf("确认成功\n");
}
else
{
printf("确认失败\n");
}
return 0;
}
这个使用 while 将缓冲区中的剩余字符全部清空,应为 while 把执行写在条件中,所以 getchar 也把 回车 取出了
总结getchar
- 如何使用 getchar
- 缓冲区的概念
- 清空缓冲区的操作和原因
scanf的使用
scanf(“格式控制字符串”, 地址表列);
根据格式,放在指定地址中
格式控制常添加 %,如 %d,%f,%u,%c,%s 等
地址列表 常为变量地址 &a ,&b… 或者是字符串
常见使用
s | 字符串,读取到空白字符为止 |
---|---|
c | 下一个字符,包括空白字符也读取 |
d/u | d是 signed int ,u 是 unsigned int |
^character | 任何字符非 character 都读取,读到 character 为止 |
p | 指针 |
空白字符:
常见的就是空格和回车
连续读取
常见的有全部读取到数组中
int main()
{
char arr[20] = 0;
int i = 0;
while(scanf("%c",arr+i)!= EOF)
{
i++;
dosomething();//某些操作
}
}
值得注意的是,读取之后立刻就能够对读取的数据进行判断或者是其他操作
相对于先读取,再判断可以减少循环的次数
例子
要求:
链接:判断数组是不是有序数组
int main()
{
int n = 0;
int i = 0;
int arr[51] = { 0 };
int flag1 = 1;//升序,如果不升序了就换成0
int flag2 = 1;//降序,如果不是降序就是0
scanf("%d", &n);
for (i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
if(i>0)
{
if (arr[i - 1] > arr[i])
flag2 = 0;
else if (arr[i - 1] < arr[i])
flag1 = 0;
}
}
if ((flag1 + flag2) >= 1)
printf("sorted");
else
printf("unsorted");
return 0;
}
解析:
scanf 读取字符和字符串
scanf 读取字符
读取字符和 getchar 的时候是一样的,缓冲区有什么读什么,需要在使用的时候清理缓冲区
while ( getchar() != '\n')//清理字符串的模块
{
;
}
读取字符串
有空格
test
int main()
{
char s[20];
char ch;
gets(s);
printf("%s\n", s);
scanf("%s",s);
printf("%s\n", s);
while ((ch = getchar()) != '\n')
{
;
}
scanf("%[^\n]", s);
printf("%s\n", s);
scanf("%s\n", s);
printf("%s\n", s);
return 0;
}
结果
解析
有人可能会问,scanf不是要取地址吗?这里是字符数组,读取字符串会顺着将字符储存到相应的字符数组中
打印字符串直接使用地址就可以
总结scanf
- 注意字符和字符串输入的区别
- 清空缓冲区
- 连续读取和连续读取操作的方法