深入学习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, ...);
  1. format字符串中转换说明的数量 必须 与后续输入变量的数量相等,否则可能导致未定义行为(undefined behaviour)。
  2. 每个<转换说明, 输入变量> 对 必须是对应的,不应该为short型使用%d(64位平台),而应该使用%hd(专用于short)。编译器可能会提示你,也有可能程序会在运行过程中崩溃 来告诉你“哪里出了问题”。
  3. scanf() 需要传递变量地址,这很容易忽略(这不是你的问题,别太担心,因为这很容易与printf混淆)

五、奇怪的、不常用的、但很有意思的scanf()可选"修饰"符

  1. 最大字段宽度(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
    }
}
  1. %[ , 这是转换说明符(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~

posted @ 2024-11-23 14:18  hk416hasu  阅读(28)  评论(0编辑  收藏  举报