当前位置: 首页 > article >正文

深入理解 C 语言中的 scanf、printf

在这里插入图片描述

欢迎来到我的:世界

希望作者的文章对你有所帮助,有不足的地方还请指正,大家一起学习交流 !


目录

  • 前言
  • 内容
    • scanf-格式化输入函数
    • printf-格式化输出函数
  • 总结

前言

在 C 语言编程中,输入输出(I/O)操作是基础且重要的部分。scanfprintffscanffprintfsscanf sprintf 是 C 语言中用于输入输出的六个核心函数。它们之间既有联系也有区别,理解它们之间的关系对于编写高效的 C 程序至关重要。本文将先详细介绍scanfprintf函数及其相互关系。


内容

scanf-格式化输入函数

scanf 是一个标准库函数,用于从标准输入(通常是键盘)读取格式化输入。
scanf函数功能是从stdin读取数据,并根据参数格式将其存储到附加参数所指向的位置。
它的原型如下:

int scanf ( const char * format, ... );
参数解释:
format:这是一个字符串参数,它指定了输入数据的格式。format 字符串可以包含以下几种元素:

  • 普通字符:在 scanf 函数执行时,输入中必须包含与这些普通字符相同的字符,否则匹配失败。例如,scanf(“%d,%d”, &a, &b); 要求用户输入的两个整数之间用逗号分隔。
  • 空白字符:包括空格、制表符和换行符。scanf 函数会忽略输入中的一个或多个连续的空白字符。例如,scanf(“%d %d”, &a, &b); 可以接受用户输入的两个整数之间用一个或多个空格、制表符或换行符分隔。
  • 格式说明符:用于指定输入数据的类型,常见的格式说明符如下:
    %d:用于读取十进制整数。
    %f:用于读取单精度浮点数。
    %lf:用于读取双精度浮点数。
    %c:用于读取单个字符。
    %s:用于读取字符串,遇到空白字符(空格、制表符、换行符)停止。
    %x 或 %X:用于读取十六进制整数。
    %o:用于读取八进制整数。

...:这是可变参数列表,它是一系列指针,指向用于存储输入数据的变量。这些指针的类型必须与 format 字符串中的格式说明符相匹配。
返回值 :scanf 函数返回成功匹配并赋值的输入项的数量。如果发生读取错误或到达文件末尾,返回 EOF
注意:在 C 语言里,EOF 是一个预定义的常量,它代表 “End Of File”(文件结束),
EOF<stdio.h> 头文件中被定义,其值通常是 -1。在代码里可直接使用,而无需额外定义。

scanf的格式说明符原型%[*][width][length]specifier,各部分用法如下:

  • specifier(说明符):决定提取字符类型、解释方式及对应参数类型。
    比如 i用于整数(可包含正负号,默认十进制,0前缀为八进制,0x前缀为十六进制);
    du用于十进制整数(d有符号,u无符号);
    o用于八进制整数(无符号);
    x用于十六进制整数(无符号);
    f、e、g用于浮点数 ;
    c用于字符;
    s用于字符串(遇空格停止);
    p用于指针地址;
    n用于记录已读取字符数;
    %用于匹配单个%字符
    注意:除n外,每个说明符至少要消耗一个输入字符,否则匹配失败。

*(可选):抑制赋值,读取数据但不存储。
width(可选):指定读取的最大字符数。
length(可选):指定数据大小,如h表示短整型,l表示长整型等。

举例说明:

int int_num;
printf("请输入一个整数(支持十进制、八进制0开头、十六进制0x开头):");
scanf("%i", &int_num);
printf("读取的整数是:%d\n", int_num);

输入:122 输出:122
输入:0122 输出:82
输入:0x122输出:290

// 读取十进制整数(d或u说明符示例)
int decimal_signed;
unsigned int decimal_unsigned;
printf("请输入一个有符号十进制整数和一个无符号十进制整数,用空格隔开:");
scanf("%d %u", &decimal_signed, &decimal_unsigned);
printf("有符号十进制整数:%d,无符号十进制整数:%u\n", decimal_signed, decimal_unsigned);

输入:-10 -10 输出:-10 4294967286

// 读取字符串(s说明符示例)
char str[50];
printf("请输入一个字符串:");
scanf("%s", str);
printf("读取的字符串是:%s\n", str);

输入:hello 输出:hello
输入:hello world 输出:hello

这时你会发现,怎么输出的结果和我预想的不一样?
使用·scanf("%s", str);读取字符串时,%s格式说明符会在遇到空白字符(如空格、制表符、换行符等)时停止读取。
当输入"hello world"时,scanf读到"hello"后面的空格就停止了,只将"hello"存储到了字符数组str中。
后续使用printf输出str时,自然就只输出了"hello"。
而剩下来的字符串“world”,就还会在缓冲区中,此时若再使用scanf读取一次,就可以将“world”读取出来了;如:

char str1[50];
char str2[50];
printf("请输入一个字符串:");
scanf("%s", str1);
scanf("%s", str2);
printf("读取的字符串是s1:%s\n", str1);
printf("读取的字符串是s2:%s\n", str2);

在这里插入图片描述
所以说我们在使用scanf函数时需要注意很多:
比如:

遇空白字符停止读取
%s格式说明符在读取字符串时,一旦碰到空白字符(像空格、制表符、换行符)就会停止。所以,scanf无法读取包含空格的完整句子。

缓冲区溢出风险
scanf函数使用%s格式说明符读取字符串时,不会对输入字符串的长度进行检查,要是输入的字符串长度超出了目标数组的容量,就会引发缓冲区溢出问题,这可能导致程序崩溃或者产生安全漏洞。

格式说明符与变量类型匹配
scanf 函数中的格式说明符必须与要存储输入数据的变量类型相匹配,否则会导致未定义行为或数据读取错误。

匹配失败和错误处理
scanf 函数在匹配失败时会停止读取输入,并返回成功匹配并赋值的输入项的数量。可以根据返回值来判断输入是否成功。

字符输入的空格处理
当使用 %c 格式说明符读取字符时,scanf 不会自动跳过空白字符(空格、制表符、换行符)。如果之前的输入操作留下了空白字符在输入缓冲区,这些空白字符可能会被 %c 读取。例如:

int main() {
	int num;
	char str[100];

	printf("请输入一个整数: ");
	scanf("%d", &num);

	char ch = 0;
	printf("请输入一个字符: ");
	scanf("%c", &ch);

	printf("你输入的整数是: %d\n", num);
	printf("你输入的字符是: %c\n", ch);
	return 0;
}

这段代码就是典型的不注意缓冲区,
当你使用 scanf("%d", &num); 读取一个整数后,按下回车键,回车键产生的换行符 \n 会留在输入缓冲区中。接着执行 scanf("%c", &ch); 时,%c 格式说明符不会自动跳过空白字符(包括换行符),所以它会直接读取留在缓冲区中的换行符并赋值给 ch,而不会等待你输入新的字符。

解决方法:
1:使用 getchar() 消耗换行符
2:在 %c 前加空格

printf-格式化输出函数

函数原型:

int printf(const char *format, ...);

printf函数功能将由format指向的C字符串写入标准输出(stdout)。如果format包含格式说明符(以%开头的子序列),则格式化format之后的其他参数并将其插入到结果字符串中,以替换它们各自的说明符。
使用通俗易懂的话就是说:它的作用是按照指定格式将数据输出到标准输出设备(通常是显示器)
注意:stdout 会将数据输出到终端(如命令行窗口),不过,在 Unix/Linux 系统中,你可以借助重定向操作符(如>)把 stdout 的输出重定向到文件或者其他程序。

参数解释:

  • format:这是一个字符串,它规定了输出的格式,并且可以包含普通字符和格式说明符。
    普通字符:这些字符会原样输出。
  • 格式说明符:以 % 开头,后面跟着一个或多个字符,用于指定要输出的数据的类型和格式。
  • ...:这是可变参数列表,它包含了要按照 format 字符串中的格式说明符进行输出的数据。

格式说明符的原型%[flags][width].[precision][length]specifier ,其中最后的specifier字符最为关键,决定了对应参数的类型和解释方式。

格式说明符具体类型
整数类

d / i输出有符号十进制整数
u输出无符号十进制整数
o输出无符号八进制数
x / X分别以小写、大写形式输出无符号十六进制整数

浮点数类

f / F分别以小写、大写形式输出十进制浮点数
e / E分别以小写、大写的科学计数法(尾数 / 指数)形式输出
g / G根据数值情况,选择最短表示形式(%e或%f,%E或%F)输出
a / A分别以小写、大写形式输出十六进制浮点数

其他类型

c输出单个字符
s输出字符字符串
p输出指针地址
n不实际打印,需传入指向有符号整数的指针,将已写入的字符数存储到该指针指向的位置
%%%会向输出流写入一个%字符

子说明符(修饰符)用法
flags(标志)

-在指定字段宽度内左对齐,默认是右对齐
+强制在结果前加上正负号,正数也加
(space) 空格正数前加空格,负数前加负号
#与某些格式说明符(如a、A、e、E、f、F、g、G、o、x、X )一起使用时,强制输出包含小数点等特定格式
0在填充时用 0 而不是空格

width(宽度)
表示最少打印的字符数。若要打印的值短于该数,结果用空格填充;若长于该数,结果不截断。
可以用*指定,此时需要在格式化字符串前额外提供一个整数参数来指定宽度。
.precision(精度)
对于整数格式说明符(d、i、o、u、x、X ),指定最少打印的数字位数,不足则用前导 0 填充,值不会截断。
对于a、A、e、E、f、F说明符,指定小数点后的位数,默认是 6 位。
对于g、G说明符,指定最多打印的有效数字位数。
对于s说明符,指定最多打印的字符数,默认打印到字符串末尾。
同样可以用*指定,需在格式化字符串前额外提供一个整数参数来指定精度。

格式说明符示例举例说明

int main() {
	int int_num = 123;
	unsigned int uint_num = 456;
	float float_num = 3.1415926;
	char char_num = 'A';
	char str[] = "Hello";
	int* ptr = &int_num;

	// 整数输出
	printf("有符号十进制整数: %d\n", int_num);
	printf("无符号十进制整数: %u\n", uint_num);
	printf("无符号八进制数: %o\n", uint_num);
	printf("小写十六进制整数: %x\n", uint_num);
	printf("大写十六进制整数: %X\n", uint_num);

	// 浮点数输出
	printf("小写十进制浮点数: %f\n", float_num);
	printf("大写十进制浮点数: %F\n", float_num);
	printf("小写科学计数法: %e\n", float_num);
	printf("大写科学计数法: %E\n", float_num);
	printf("最短表示形式(小写): %g\n", float_num);
	printf("最短表示形式(大写): %G\n", float_num);
	printf("小写十六进制浮点数: %a\n", float_num);
	printf("大写十六进制浮点数: %A\n", float_num);

	// 其他类型输出
	printf("单个字符: %c\n", char_num);
	printf("字符串: %s\n", str);
	printf("指针地址: %p\n", (void*)ptr);

	return 0;
}

在这里插入图片描述

对于flags示例:

#include <stdio.h>

int main() {
    int num = 123;
    float f_num = 3.14;

    // 左对齐
    printf("左对齐示例: |%-10d|\n", num);
    // 强制显示正负号
    printf("强制显示符号示例: |%+d|\n", num);
    // 正数前加空格
    printf("正数加空格示例: |% d|\n", num);
    // 特定格式输出(以十六进制为例)
    printf("特定格式示例: |%#x|\n", num);
    // 用0填充
    printf("0填充示例: |%05d|\n", num);

    // 浮点数相关
    printf("浮点数0填充示例: |%08.2f|\n", f_num);

    return 0;
}

在这里插入图片描述

对width(宽度)实例:

int main() {
	int num = 123;
	char str[] = "abc";

	// 指定宽度
	printf("指定宽度示例(整数): |%5d|\n", num);
	printf("指定宽度示例(字符串): |%10s|\n", str);

	// 使用*指定宽度
	int width = 8;
	printf("使用*指定宽度示例: |%*d|\n", width, num);

	return 0;
}

在这里插入图片描述

.[precision](精度实例):

int main() {
	int int_num = 12;
	float float_num = 3.1415926;
	char str[] = "HelloWorld";

	// 整数精度
	printf("整数精度示例: |%05d|\n", int_num);
	// 浮点数精度
	printf("浮点数精度示例: |%.2f|\n", float_num);
	// 字符串精度
	printf("字符串精度示例: |%.5s|\n", str);

	// 使用*指定精度
	int precision = 3;
	printf("使用*指定精度示例: |%.*s|\n", precision, str);

	return 0;
}

在这里插入图片描述

在使用printf函数时,还需注意:

格式说明符与参数的匹配
类型匹配:printf函数的格式说明符必须与对应的参数类型相匹配。如果不匹配,可能会导致未定义行为,输出结果可能是错误的,甚至可能引发程序崩溃;

缓冲区问题
缓冲区刷新:printf 函数通常会使用缓冲区来提高性能。在某些情况下,输出可能不会立即显示在屏幕上,而是先存储在缓冲区中。可以使用 fflush(stdout); 函数来强制刷新标准输出缓冲区,确保输出立即显示。例如:

性能问题
频繁调用:频繁调用 printf 函数会影响程序的性能,因为每次调用都涉及到系统调用和缓冲区管理。如果需要输出大量数据,建议先将数据存储在一个字符串中,然后一次性输出。


总结


到了最后:感谢支持

------------对过程全力以赴,对结果淡然处之


http://www.kler.cn/a/591164.html

相关文章:

  • 《算法笔记》9.2小节——数据结构专题(2)->二叉树的遍历 问题 D: 二叉树遍历
  • 受控组件非受控组件
  • 新造车不再比拼排名,恰是曲终人散时,剩者为王
  • 【大语言模型知识】Transformer架构概述
  • LLVM学习-- 构建和安装
  • redis的典型应用 --缓存
  • 自定义捕捉与处理信号的底层逻辑
  • mkdir /path/aa/bb与mkdir -p /path/aa/bb的区别
  • 案例5_3: 6位数码管静态显示
  • Maven | 站在初学者的角度配置
  • 【写作科研化】LongWriter: Unleashing 10,000+ Word Generation From Long Context LLMs
  • Hard Disk Sentinel:您的硬盘健康“全科医生”,守护数据安全的智能管家
  • 我爱学算法之——滑动窗口攻克子数组和子串难题(上)
  • [从零开始学习JAVA] Stream流
  • HTML5 Canvas 的俄罗斯方块游戏开发实践
  • 2023华东师范大学计算机复试上机真题
  • 验证码reCAPTCHA 打码平台
  • 基于Python的金融领域AI训练数据抓取实战(完整技术解析)
  • Java中的label与assert语句
  • 软考 中级软件设计师 考点知识点笔记总结 day05