问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C语言scanf和printf函数详解:用法、占位符与常见问题解决

创作时间:
作者:
@小白创作中心

C语言scanf和printf函数详解:用法、占位符与常见问题解决

引用
CSDN
1.
https://blog.csdn.net/Serein627/article/details/145213325

本文详细介绍了C语言中scanf和printf函数的使用方法,包括基本用法、占位符、输出格式等核心内容,并特别讲解了scanf函数在使用过程中可能遇到的报错问题及其解决方法。

printf函数详解

基本用法

printf()函数的作用是将参数文本输出到标准输出设备(通常是屏幕)。它名字中的f代表format(格式化),表示可以定制输出文本的格式。

使用说明:

  • printf()不会在行尾自动添加换行符,运行结束后,光标停留在输出结束的地方。
  • 为了让光标移到下一行的开头,可以在输出文本的结尾添加一个换行符\n
  • 如果文本内部有换行,也是通过插入换行符来实现。

占位符

% + 占位符的类型:占位符的第一个字符一律为百分号%,第二个字符表示占位符的类型。

占位符的含义

printf可以在输出文本中指定占位符。所谓"占位符",就是这个位置可以用其他值代入。例如:

如上:There are %d apples\n是输出文本,里面的%d就是占位符,它用于打印整数。printf()的第二个参数就是替换占位符的值,因此%d的位置会被后面的参数10替换。%s用于打印字符串(同理)。

常见的占位符

printf()的占位符有许多种类,与C语言的数据类型相对应。常用占位符大全如下:

占位符
含义
%a
十六进制浮点数,字母输出为小写
%A
十六进制浮点数,字符输出为大写
%c
字符 (char)
%d
十进制整数 (int)
%e
使用科学计数法的浮点数,指数部分的e为小写
%E
使用科学计数法的浮点数,指数部分的E为大写
%i
整数,基本等同于%d
%f
小数(包含float类型和double类型)
%g
6个有效数字的浮点数。整数部分一旦超过6位,就会自动转为科学计数法,指数部分的e为小写
%G
等同于%g,指数部分的E为大写
%hd
十进制 short int 类型
%ho
八进制 short int 类型
%hx
十六进制 short int 类型
%hu
unsigned short int 类型
%ld
十进制 long int 类型
%lo
八进制 long int 类型
%lx
十六进制 long int 类型
%lu
unsigned long int 类型
%lld
十进制 long long int 类型
%llo
八进制 long long int 类型
%llx
十六进制 long long int 类型
%llu
unsigned long long int 类型
%Le
科学计数法表示的 long double 类型浮点数
%Lf
long double 类型浮点数
%n
已输出的字符串数量。该占位符本身不输出,只将值存储在指定变量之中
%o
八进制整数
%p
指针(用来打印地址)
%s
字符串
%u
无符号整数(unsigned int
%x
十六进制整数
%zd
size_t 类型
%%
输出一个百分号

输出格式

printf()可以定制占位符的输出格式。

限定宽度

printf()允许限定占位符的最小宽度。例如:

  • %5d表示这个占位符的宽度至少为5位。如果不满5位,对应的值的前面会添加空格。
  • 输出的值默认是右对齐,即输出内容前面会有空格。
  • %05d表示这个占位符的宽度至少为5位。如果不满5位,对应的值的前面会用0补齐。
  • 如果希望改成左对齐,在输出内容后面添加空格,可以在占位符的%的后面插入一个-号。
  • 可以限定最小宽度,printf()当长度超过时则不会受限。
  • 对于小数,这个限定符会限制所有数字的最小显示宽度,是所有数字。

限定小数位数

输出小数时,有时希望限定小数的位数。举例来说,希望小数点后面只保留两位,占位符可以写成%.2f。限定小数位数可以与限定宽度占位符,结合使用。此外,最小宽度和小数位数这两个限定值,都可以用*代替,通过printf()的参数传入。

总是显示正负号

默认情况下,printf()不对正数显示+号,只对负数显示-号。如果想让正数也输出+号,可以在占位符的%后面加一个+

#include <stdio.h>
int main()
{
    printf("%+d\n", 12); // 输出 +12 
    printf("%+d\n", -12); // 输出 -12 
    return 0;
}

%+d可以确保输出的数值,总是带有正负号。

输出部分字符串

%s占位符用来输出字符串(默认是全部输出)。

%.[m]s指定输出的长度,其中[m]代表一个数字,表示所要输出的长度。

现在我们用代码演示一下:

#include <stdio.h>
int main()
{
    printf("%s\n", "abcdef");//输出abcdef
    printf("%.3s\n", "abcdef");//输出abc
    return 0;
}

scanf函数详解

scanf函数是C语言中用于从标准输入设备(通常是键盘)读取数据的函数。将变量的值输出在屏幕上时可以使用printf函数。

scanf报错的原因及解决办法

接下来先做个引入:以下内容只针对Visual Studio 2022(以下简称VS2022)。

scanf函数报错的原因

error C4996: ‘scanf’: This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

(这句话大概是说:“scanf是不安全的,需要用scanf_s来替换 或者 使用_CRT_SECURE_NO_WARNINGS 才能避免报错”)

我们先来说说为什么VS2022认为scanf函数是不安全的,举个栗子:

这里先用_CRT_SECURE_NO_WARNINGS这一种方法作为例子演示(如何使用该方法稍后详细解释)

补充一下上述例子中的数组:c语言中我们定义数组是不允许数据越界的,比如上面的数组 arr[5]其中5就是arr这个数组的界限,我们在给这个数组输入数据的时候不能超过5个,但是我在输入了abcdef 6个字母后,它竟然给我们打印出来了,这在c语言中显然是不合法的,所以编译器也给我们报了个错误提醒我们。这就是为什么VS2022认为scanf函数是不安全的原因。

scanf报错的解决办法

在了解VS2022中使用scanf函数报错的原因之后,我们就来介绍一下针对scanf函数报错的解决方法

(1)使用scanf_s替换

  • scanf_s是vs提供的一个函数,但是这个函数的使用和scanf是有区别的
  • scanf_s既然是vs提供的,其他的编译器并不认识这个函数。
  • 如果代码中使用了scanf_s函数,代码就存在跨平台性问题
    ‼️当前建议大家不要使用scanf_s,踏实使用scanf

(2)使用 _CRT_SECURE_NO_WARNINGS(推荐)

1.就在当前的代码所在的.c文件的第一行加上:#define _CRT_SECURE_NO_WARNINGS
( 问题:只要使用scanf函数就得加上这句代码,挺麻烦)
2.一劳永逸:
效果:新建的.c文件中的第一行,默认就加上:#define _CRT_SECURE_NO_WARNINGS 1
// 其实在VS上新建.c文件的时候,是拷贝newc++file.cpp这个文件的
// 如果能在newc++file.cpp这个文件的第一行加上:#define _CRT_SECURE_NO_WARNINGS 1
// 新建文件中就有了
👉第一步:复制 #define _CRT_SECURE_NO_WARNINGS
👉第二步:打开 此电脑 ,在右上角搜索newc++file.cpp
👉第三步:选中文件右击鼠标打开文件所在的位置(注意打开后不要关掉)
👉第四步:将newc++file.cpp 复制到桌面然后右击鼠标在记事本编辑,粘贴第一步的那句话
👉第五步:Ctrl+S保存后关掉记事本,将桌面的newc++file.cpp拖回文件所在的位置,选择替换目标中的文件
Tip:到这里,大功告成‼️之后每次创建源文件的时候,第一行都会自动给我们加上这句话了

基本用法

scanf的原型定义在头文件stdio.h。使用这个函数之前,必须在源代码头部引入这个头文件。

scanf()函数用于读取用户的键盘输入。程序运行到这个语句时,会停下来,等待用户从键盘输入。

用户输入数据、按下回车键后,scanf()就会处理用户的输入,将其存入变量。

  • 第一个参数是一个格式字符串,里面会放置占位符,告诉编译器如何解读用户的输入,需要提取的数据是什么类型。
  • C语言的数据都是有类型的,scanf()必须提前知道用户输入的数据类型,才能处理数据。
  • 其余参数就是存放用户输入的变量,格式字符串里面有多少个占位符,就有多少个变量。

变量前面必须加上&运算符(指针变量除外),因为scanf()传递的不是值,而是地址,即将变量i的地址指向用户输入的值。如果这里的变量是指针变量(比如字符串变量),那就不用加&运算符。

scanf()处理数值占位符时,会自动过滤空白字符,包括空格、制表符、换行符等。

用户输入的数据之间,有一个或多个空格/使用回车键将输入分成几行,都不影响scanf()解读。

scanf()处理用户输入的原理:用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读。解读用户输入时,会从上一次解读遗留的第一个字符开始,直到读完缓存,或者遇到第一个不符合条件的字符为止。

现在我们用代码来演示一下:

#include <stdio.h>
int main()
{
    int x;
    float y;
    
    //用户输入"    -13.45e12# 0"
    scanf("%d", &x);
    printf("%d\n", x);//输出 -13
    scanf("%f", &y);
    printf("%f\n", y);//输出449999994880.000000
    return 0;
}

浮点数在内存中有可能无法精确保存

  • scanf()读取用户输入时,%d占位符会忽略起首的空格,从-处开始获取数据,读取到-13停下来,因为后面的.不属于整数的有效字符。这就是说,占位符%d会读到-13
  • 第二次调用scanf()时,就会从上一次停止解读的地方,继续往下读取。这一次读取的首字符是.,由于对应的占位符是%f,会读取到.45e12,这是采用科学计数法的浮点数格式。后面的#不属于浮点数的有效字符,所以会停在这里

scanf()可以连续处理多个占位符,so~ 上面的例子也可以写成下面这样:

int main()
{
    int x;
    int y;
    //用户输入"    -13.45e12# 0"
    scanf("%d%f", &x, &y);
    return 0;
}

scanf的返回值

  1. scanf()的返回值是一个整数,表示成功读取的变量个数
  2. 如果没有读取任何项,或者匹配失败,则返回0
  3. 如果在成功读取任何数据之前,发生了读取错误或者遇到读取到文件结尾,则返回常量EOF(-1)
    EOF — end of file 文件结束标志

现在我们一起来观察以下四种运行结果

第一种:按照scanf()格式字符串给出的预定格式输入4个值,返回值为4,结果正常
第二种:前3个值按照预定格式输入,但第4个值前有“,”,不属于整数的有效字符,会停在这里,不能正常读取第4个值,因此第4个值 d = 0,并且 scanf() 的返回值是3
第三种:在正常输入前两个值后,按Ctrl+Z提前结束输入,此时只读取到两个值,scanf()的返回值是2
第四种:一个数字都不输入,直接按3次Ctrl+Z,scanf() 的返回值是-1,也就是EOF

那我想就有人要问了,scanf() 的返回值到底有什么用呢这里来小科普一下:

  • scanf函数的返回值一般在竞赛的OJ题目,笔试的OJ题目中比较常见
  • OJ – online judge – 在线判题
#include <stdio.h>
int main()
{
    int a = 0;
    int b = 0;
    while (scanf("%d%d", &a, &b) == 2)//或者用while (scanf("%d%d",&a,&b)!=EOF)
    {
        int r = a + b;
        printf("%d\n", r);
    }
    //10 20
    //-10 20
    //-10 -20
    //0 3
    //....
    //多组数据测试
    return 0;
}

占位符

scanf的占位符和printf的占位符基本一致,这里做些补充:

  • %[ ]:在方括号中指定一组匹配的字符(比如%[0-9]),遇到不在集合之中的字符,匹配将会停止
  • 除了%c以外,都会自动忽略起首的空白字符
  • %c不忽略空白字符,总是返回当前第一个字符,无论该字符是否为空格
  • 如果要强制跳过字符前的空白字符,可以写成scanf(" %c", &ch),即%c前加上一个空格,表示跳过零个或多个空白字符

现在在%c前面加上一个空格:

占位符%s的规则:从当前第一个非空白字符开始读起,直到遇到空白字符(即空格、换行符、制表符等)为止。

  • %s不会包含空白字符,所以无法用来读取多个单词,除非多个%s一起使用
  • scanf()遇到%s占位符,会在字符串变量末尾存储一个空字符\0
  • scanf读取空白字符的特殊写法: %[^\n]s --> 一直读取,直到遇到\n
  • scanf()将字符串读入字符数组时,不会检测字符串是否超过了数组长度— VS会提示scanf函数不安全
  • 为防止:储存字符串时,超过数组的边界,so~ 使用 %s 占位符时,应该指定输入字符串的最长长度
  • %[m]s:其中的 [m] 是一个整数,表示读取字符串的最大长度,后面的字符将被丢弃

#include <stdio.h>
int main()
{
    char arr[5]={0};
    scanf("%4s",arr); //%4s:为\0留出1一个空间
    printf("%s\n",arr);
    return 0;
}

赋值忽略符

有时,用户的输入可能不符合预定的格式

scanf函数的第一个参数中的格式一定要和输入的格式匹配才可以正确读取数据

应该有人会想到,既然用户输入时在年月和月日之间都要加上字符( / - 空格),那就直接在scanf()的第一个参数中加上字符让它读取就好了

但是上述方式虽然能解决问题,但是并不是很好的办法,为了接收两个字符( / - 空格 ),在格式串里专门搞了两个字符%c、%c,并且创建了两个变量ch1、ch2,但是它们并没有什么用。下面有个更好的办法

为避免用户输入其他格式导致scanf()解析数据失败,scanf()提供了一个赋值忽略符*

只要把*加在任何占位符的百分号后面,就表示该占位符没有对应的变量,不会返回值,解析后将被丢弃

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号