深入学习scanf()的使用方法
众所周知,C语言的scanf()位于<stdio.h>,用于输入数据,但一直以来我都对它的使用方式似懂非懂,今天我们来详细聊聊它。
scanf意为“scan format”, 即格式化输入,原型如下:
int scanf(const char *format, ...);
一、基础使用方式
#include <stdio.h>
int main() {
int num = 0;
scanf("%d", &num); // 一定注意要使用&传递指针,以在scanf()中修改num的值,这是特别易错点
printf("%d\n", num); // 注意printf() 与 scanf()的调用差异,也很容易错
return 0;
}
二、scanf()的返回值
成功时,scanf()会返回成功匹配与赋值的个数; 如果遇到EOF,会返回EOF,
/***
* brief : check the return-val of scanf();
* input-1 : 0 1 2
* output-1: 3
* conclusion: On success, scanf() will return the number of input items successfully matched and assigned.
* input-2 : <EOF>
* output-2: -1
*/
void return_val() {
int num = 0;
int ret = 0;
ret = scanf("%d%d%d", &num, &num, &num); // input-1 : 0 1 2
printf("%d\n", ret); // output-1: 3
}
关于EOF:
// EOF定义在stdio.h中,如下
/* The value returned by fgetc and similar functions to indicate the
end of the file. */
#define EOF (-1)
三、scanf()的工作方式
阅读man scanf
,我们可以完全了解scanf()的工作方式,但是它很难读,而这就是我写博客的意义。
stdio.h中定义了输入与输出的缓冲区(可以阅读源码),我们输入的字符就会先进入这个缓冲区。
scanf()类似于一个字符串最长优先匹配函数,会尽可能去获取最长的、符合当前需要的数据;
而且它会跳过前导空格。
#include <stdio.h>
/**
* brief : 显示scanf()如何工作
* input : 10.3 5 6
* output:
* i = 10
* j = 0.300000
* k = 5
* explaination:
* scanf()首先尝试匹配一个整数,它依次看到字符'1', '0', '.',然后由于字符'.'是不应该出现在整数中的,因此它返回10
* 然后它尝试匹配一个float,它依次看到字符'.','3',' ', 由于空格字符不会出现在小数中, 因此它返回0.3
* 最后它尝试匹配一个整数,它会跳过前导空格,看到字符'5',' ',由于整数没有空格,因此它返回5
* 输入缓冲区中还有字符'6', 这将是下一次scanf()开始的字符
*/
int main() {
int i, k; float j;
scanf("%d%f%d", &i, &j, &k); // 10.3 5 6
printf("i = %d\nj = %f\nk = %d\n", i, j, k);
return 0;
}
再来一个例子:
int a = 0, b = 0;
scanf("%d/%d", &a, &b); // input: 3/5
printf("%d, %d", a, b); // output: 3, 5
那么它期望的输入中,必须包含字符'/', 否则scanf()会匹配失败。
四、scanf()使用注意事项
int scanf(const char *format, ...);
- format字符串中转换说明的数量 必须 与后续输入变量的数量相等,否则可能导致未定义行为(undefined behaviour)。
- 每个<转换说明, 输入变量> 对 必须是对应的,不应该为short型使用%d(64位平台),而应该使用%hd(专用于short)。编译器可能会提示你,也有可能程序会在运行过程中崩溃 来告诉你“哪里出了问题”。
- scanf() 需要传递变量地址,这很容易忽略(这不是你的问题,别太担心,因为这很容易与printf混淆)
五、奇怪的、不常用的、但很有意思的scanf()可选"修饰"符
- 最大字段宽度(the maximum field width):限制了输入字符的数量。如果达到这个最大值,那么scanf()将返回。前导空格不计。
/*
* brief : test %width and sscanf()
* input : NULL(don't need input)
* output: 5 14 893 0 0
* conclusion: the "width" limit the "upper" of input, and when no input, it will assign 0
*/
void test_2() {
char str[10] = "514893";
int arr[5] = {0};
sscanf(str, "%1d%2d%3d%1d%1d", &arr[0], &arr[1], &arr[2],
&arr[3], &arr[4]); // input from the string "514893"
for (int i = 0; i < 5; i++) {
printf("%d\n", arr[i]); // output: 5 14 893 0 0
}
}
- %[ , 这是转换说明符(Conversion Specification)之一,地位等同于%d, %f, %s这种,但我之前没用过
/*
* brief : test %[
* input : Ai51c6
* output: Ai
* conclusion: it only accept alpha character, and return when meet others
*/
void test_3() {
char str[10] = {0};
scanf("%[a-zA-Z]", str); // input : Ai51c6
printf("%s\n", str); // output: Ai // 这是因为碰到字符'5',它不属于a-z或A-Z区间,因此返回.
}
3.'*'可以忽略赋值(Assignment-Suppression), 即scanf()会去读取,但不会将它存储在变量中
/*
* brief : check the Assignment-Suppression *
* input : 1 2
* output: 2
* conclusion: the * tells scanf() to read the input but not store it in a variable
*/
void test_6() {
int num = 0;
scanf("%*d %d", &num); // input : 1 2
printf("%d\n", num); // output: 2
}
over~