• scanf、printf使用详解


    scanf

    函数原型

    #include 
    int scanf(const char *format, ...);
    
    • 1
    • 2

    参数

    format:字符串参数,包含三种类型。

    • 空白字符:它们与输入的零个或多个空白字符匹配,在处理中常被忽略
    int num1 = 0;
    int num2 = 0;
    // 此时 scanf 中有 7 个空白字符(空白字符不仅仅是空格,还可以是制表符、回车符)
    // 但在实际输入中输入 1 个或多个空白字符都是可以正确读取的
    // 至少要有一个空白字符是要分开两个数字,否则会被当成一个数字
    scanf("%d       %d", &num1, &num2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 格式代码:它们指定函数如何解释接下来的输入字符
    // 同样的二进制数,不同的解释就会有不同的结果
    // 8 位二进制数:1000 0001 
    // 有符号数:-1
    // 无符号数:129
    printf("%d\n", -1);
    printf("%u\n", -1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 其他字符:当其他字符出现在格式字符串时,下一个输入字符必须与它匹配

      • 如果匹配,该输入字符随后被丢弃

      • 如果不匹配,函数就不再读取而是直接返回

    int num1 = 0;
    int num2 = 0;
    // 如果在终端输入形式如下:12 31
    // 那么此时 scanf 会直接返回,并不会将两个数字读取到 num1、num2
    // 正确的输入形式应是:请输入第一个数字:12,请输入第二个数字:31
    // 这样 scanf 才能正确读取
    scanf("请输入第一个数字:%d,请输入第二个数字:%d", &num1, &num2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ...:表示一个可变长度的指针列表

    • 从输入转化而来的值逐个存储到这些指针参数所指向的内存
      • 由此可见,scanf 参数前面为什么要加 & 符号就比较清楚了
      • 如果没加 & 符号,将变量的值传递给函数,而 scanf 函数却把它解释为一个地址。当它被解引用将数据写入时,要么导致程序终止,要么导致一个不可预料的内存位置的数据被改写

    返回值

    格式化字符串到达末尾或者读取的输入不再匹配格式字符所指定的类型时,输入就停止。

    • 被转化的输入的数目作为函数的返回值
    • 如果在任何输入值被转换之前文件就已经到达末尾,函数就返回常量值 EOF
      • EOF 是常量值 -1,与 ASCII 中字符都不相同,这是为了避免二进制被错误解释为 EOF

    scanf 的格式控制

    格式字符串的形式为:

    %[可读取的最大宽度][限定符]类型

    %[width][modifiers]type

    width

    width(宽度,可选)是一个数值,用于指定当前读取操作中读取的最大字符数。

    int num = 0;
    // 如果在终端输入 1002,num 最多只能读取三个字符,所以 num 的值为 100
    scanf("%3d", &num);
    
    • 1
    • 2
    • 3

    modifiers

    格式代码hlLll
    d、i、nshortlonglong long
    o、u、xunsigned shortunsigned long
    e、f、gdoublelong double

    限定符的主要目的是为了指定参数的长度。如果整形参数比缺省的整形参数更短或更长,在格式代码中省略限定符就是一个常见的错误。浮点类型也是如此。

    如果省略了限定符,可能会导致一个较长变量只有一部分被初始化,或者一个较短变量的相邻内存也被修改。

    double dNum = 0.0;
    long double ldNum = 0.0;
    // 如果只使用 %f,那么就会导致有一部分没被初始化
    scanf("%lf", &dNum);
    scanf("%Lf", &ldNum);
    
    short int sNum = 0;
    // 如果只使用 %d,那么就会导致相邻内存被非法修改,这极有可能导致进程崩溃
    scanf("%hd", &sNum);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    各种数据类型占用空间大小

    C/C++ 并没有具体规定各种数据类型占用大小,只是给了一些限制,因此不同编译环境下结果可能不同,下表为 g++ 4.8.5 下的结果。

    数据类型占用内存(32位)占用内存(64位)
    char11
    unsigned char11
    short22
    unsigned short22
    int44
    unsigned int44
    long48
    unsigned long48
    float44
    double88
    long double1216

    type

    type(转换字符,必选)的内容及含义如下:

    转换字符参数类型含义
    cchar*读取单个字符。如果给出宽度,就读取这个数目的字符
    iint*根据前缀决定基数,可以是八进制(前缀 0)、十进制、十六进制(前缀 0x)有符号整数
    dint*十进制有符号整数
    u、o、xunsigned int*无符号整数。u 被解释为十进制、o 被解释为八进制、x 被解释为十六进制
    f、e、E、g、Gfloat*浮点型字面值常量。但小数点不是必须的,也可以使用指数形式(大小写没有区别)
    schar*读取一串非空字符串
    pvoid*读取一个指针(即内存地址)
    nint*到目前为止通过调用 scanf 函数从输入读取的字符数被返回,%n 转换的字符并不在计算在 scanf 函数的返回值之内,它本身不消耗任何输出
    %(无)两个 %% 就是 % 本身,与输入的 % 相匹配
    int num = 0;
    int num1 = 0;
    int num2 = 0;
    // 若在终端输入10   02
    // 那么 num 的值为 7,因为中间输入的三个空格也是 scanf 读取到的字符
    scanf("%d %d%n", &num1, &num2, &num);
    printf("%d\n", num);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意:当使用 %s 格式时,要小心缓冲区溢出的情况,最好在前面显式地控制读取的最大长度。

    缓冲区溢出:10 个 char 大小的数组,却读取了 20 个字符,多的这部分就是溢出的,这就有可能超出当前函数栈帧引发段错误,或修改了不该修改的内存地址。

    printf

    函数原型

    #include 
    int printf(const char *format, ...);
    
    • 1
    • 2

    参数

    • format:字符串参数,包含两种类型
      • 格式代码:指定转换的执行方式
      • 其他字符:原样逐字打印
    • ...:用于转换的参数列表

    返回值

    如果函数调用成功,返回值是实际打印的字符数(不包含表示字符串结束的 ‘\0’);

    如果函数调用失败,返回值是一个负数。

    printf 的格式控制

    格式字符串的形式为:

    %[标志][输出最小宽度][.精度][限定符]类型

    %[flags][width][.precision][modifiers]type

    flags

    flags 可同时出现多个,且无顺序要求。

    标志含义
    -指定被转换的参数在其字段内左对齐(默认是右对齐)
    +指定在输出的数前面加上正负号
    空格如果第一个字符不是正负号,则在其前面加上一个空
    0对于数值转换,当输出长度小于字段宽度时,添加前导 0 进行填充
    #指定下表的另一种输出形式
    用于#标志
    o保证产生的值以一个零开头
    x、X在非零值前加 0x 前缀(%X 则为 0X)
    e、E、f确保结果始终包含一个小数点,即使后面没有数字
    g、G确保结果始终包含一个小数点,即使后面没有数字。另外,后缀的 0 并不会从小数中去除

    width

    width(宽度,可选)是一个数值,用于指定要输出的最小字符数。

    转换后的参数输出宽度至少要达到这个数值。如果参数的字符数小于该数值,则在参数左边填充字符(默认是空格)。

    • 如果 flags 设置为 -,要求参数左对齐的话则在右边填充

    .precision

    .precision(.精度,可选),通过点号分隔字段的宽度和精度。

    • 对于字符串,它指定打印的字符的最大个数
    • 对于整数,它指定打印的数字的最小位数
      • 必要时可填充 0 以达到位数要求
    • 对于转换字符为 e、E 或 f,它指定小数点后打印的数字位数
      • 会发生进位
      • 必要时可填充 0 以达到位数要求
    • 对于转换字符为 g 或 G,它指定打印的有效数字位数
      • 会发生进位

    modifiers

    • hh 表示将相应的参数按 signed char/unsigned char 类型输出
    • h 表示将相应的参数按 short/unsigned short 类型输出
    • l 表示将相应的参数按 long/unsigned long 类型输出
    • ll 表示将相应的参数按 long long/unsigned long long 类型输出
    • L 表示将相应的参数按 long double 类型输出
      • 仅适用于浮点数说明符,上面仅适用于整数说明符

    type

    type(转换字符,必选)的内容及含义如下:

    转换字符参数类型含义
    cint参数被裁剪为 unsigned char 类型并作为字符打印
    d、iint参数作为有符号十进制整数打印
    u、o、x、Xunsigned int参数作为无符号值打印。u 使用十进制;o 使用八进制;x 或 X 使用十六进制(两者的区别是:x 使用小写字母,X 使用大写字母)
    e、Edouble参数以指数形式输出单、双精度浮点数。小数点后面的位数由精度字段决定,默认是 6(两者的区别是:打印时 e 的大小写)
    fdouble参数按照常规的浮点数形式打印。小数点后面的位数由精度字段决定,默认是 6
    g、Gdouble参数以 %f 或 %e 中较短的输出宽度输出单、双精度浮点数
    schar*打印一个字符串
    nint*不产生任何输出,到目前为止函数所产生的输出字符数目将被保存到对应的参数中
    %(无)打印一个 % 字符

    注:如果 % 后边的字符不是转换字符,则其行为没有定义。

  • 相关阅读:
    经验分析:数据可视化工具入门讲解,如何应用数据可视化
    期望E与高斯分布的期望
    VC++控制台程序隐藏窗口运行
    SPI总线协议
    OpenAI的组织形态、决策机制与产品构建
    【算法】堆排序
    ESP8266-Arduino编程实例-OLED显示DS18B20传感器数据
    使用 flex 弹性盒保持容器均分布局
    SpringBoot项目如何优雅的实现操作日志记录
    LeetCode每日一题:2251. 花期内花的数目(2023.9.28 C++)
  • 原文地址:https://blog.csdn.net/qq_40080842/article/details/126799764