作为一个初学者,只知道使用scanf的时候要用取地址符,但是,其中还有许多问题。
1.使用scanf的时候可以不用取地址符&吗?
单个字符、整数、小数、对应的格式控制符分别是 %c、%d、%f。
此时都需要用取地址符。
那么字符串呢?假设在我们不想用gets()。
此时定义一个字符串char url[30];
scanf(“%s”, url);
此时没有用取地址符,那么可不可以用取地址符呢?
scanf() 读取数据时需要的是数据的地址,整数、小数、单个字符都要加&
取地址符,这很容易理解;
但是对于此处的 url 字符串,我们并没有加 &,这是因为,字符串的名字会自动转换为字符串的地址,所以不用再多此一举加 & 了。
当然,你也可以加上,这样虽然不会导致错误,但是编译器会产生警告。
2.缓冲区的scanf
下面用\r表示回车。
int a, b;
scanf("%d", &a);
scanf("%d", &b);
printf("a=%d, b=%d\n", a, b);
输入1\r2\r,此时正常输出,那么看下面这个。
int a = 1, b = 2;
scanf("a=%d", &a);
scanf("b=%d", &b);
printf("a=%d, b=%d\n", a, b);
输入a=1\r,此时,按下回车键,只有第一个 scanf() 成功读取了数据,第二个 scanf() 没有给予我们执行的机会,程序直接运行。
原因来自第一个 scanf() 执行完后,将 1 赋值给了 a,缓冲区中只剩下一个换行符\n
;到了第二个 scanf(),发现缓冲区中有内容,但是又不符合控制字符串的格式,于是尝试忽略这个空白符。
究其根本为当控制字符串不是以格式控制符 %d、%c、%f 等开头时,空白符就不能忽略了,它会参与匹配过程,本例中第二个 scanf() 的开头并不是格式控制符,而是以b
字符,所以不会忽略换行符。
如果我们换一种输入方式呢?
输入a=1 b=2\r
会发现结果和上面一致,第二个 scanf() 读取再次失败。
原因是开头的空格不能忽略,因此又和控制字符串不匹配,所以只能读取失败了
此时输入a=1b=2\r
不要让两份数据之间有空白符,会得到我们想要的结果。
最后我们得出结论:只有当控制字符串以格式控制符开头时,才会忽略换行符。
3.使用 scanf() 清空缓冲区
scanf("%*[^\n]"); scanf("%*c");
第一个 scanf() 将逐个读取缓冲区中\n
之前的其它字符,% 后面的 * 表示将读取的这些字符丢弃,遇到\n
字符时便停止读取。
此时,缓冲区中尚有一个\n
遗留,第二个 scanf() 再将这个\n
读取并丢弃,这里的星号和第一个 scanf() 的星号作用相同。
由于所有从键盘的输入都是以回车结束的,而回车会产生一个\n
字符,所以将\n
连同它之前的字符全部读取并丢弃之后,也就相当于清除了输入缓冲区。
int a = 1, b = 2;
scanf("a=%d", &a);
scanf("%*[^\n]"); scanf("%*c"); //在下次读取前清空缓冲区
scanf("b=%d", &b);
printf("a=%d, b=%d\n", a, b);
输入a=100\rb=200\r
输出a=100, b=200。
输入a=100b=200\rb=300\r
输出a=100, b=300。
此篇文章来自大一再学C时通过C语言中文网对scanf的深入。
4.用scanf如何代替gets
%s 控制符会匹配除空白符以外的所有字符,所以无法将多个单词存放到一个字符串中,因为单词之间就是以空格为分隔的,%s 遇到空格就读取结束了。
scanf存在一种匹配方式,[]
包围起来的是需要读取的字符集合。
再使用连接符,scanf() 支持使用连字符-
来表示一个范围内的字符。
%[a-z]
表示读取 abc…xyz 范围内的字符,也即小写字母;
%[A-Z]
表示读取 ABC…XYZ 范围内的字符,也即大写字母;
%[0-9]
表示读取 012…789 范围内的字符,也即十进制数字。
%[a-zA-Z0-9]
表示读取所有的英文字母和十进制数字;
int main(){
char str[30];
scanf("%[a-zA-Z0-9]", str);
printf("%s\n", str);
return 0;
}
此时就完美替代了gets()。
此篇文章来自大一再学C时通过C语言中文网对scanf的深入。