【C语言篇】详细解释:转换字符%d和--d前面加不同数字时的意义和作用

本文详细探讨%d在scanf和printf中的使用,涉及整数和小数的格式控制,包括输入限制与输出宽度,通过实例对比和代码演示,帮助理解%d前数字对字符数的影响及其实质作用。

🙇‍♀️写在前面的话:
这个文章主要讲解%d的用法,会由两个题目引入,进而分析和总结%d的d前面加不同数字(整数、小数)情况下的用法和作用。


🌎引入–2个题目:

题目1:

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1

int main()
{
	int x = 0;
	float y = 0;

	scanf("%3d%f", &x, &y);
	printf("%d %f\n", x, y);


	return 0;
}

这个题目的结果是C:123
在这里插入图片描述

题目2:

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

int main()
{
	int a = 1234;
	printf("%2d\n", a);

	return 0;
}

这个题目的答案是A:1234
在这里插入图片描述

对比:

当时,我和 一位同学对这两个题目的疑问是:第二个题目不应该输出结果是“12”吗。因为根据我们之前对%d浅显的理解是:d前面加整数,限制了输出字符数量,换句话来说,限制了在屏幕上输出的宽度。
其实,要弄清楚这个问题,要明白以下几点:
🅰:d前面的整数和实际字符数的大小关系。

🅱:经过格式化修饰的%d是在scanf函数内还是在printf函数


·第一个题目中,%d前面的数字3是小于实际字符数的,而且%3d是存在与输入函数scanf函数内的。
那么%d前面的整数,限制了编译器读取整数的字符数量(只从左向右读取三个整数字符)。
所以,x的值是123(为12345的前三个字符)
🆚
·第二个题目中,%d前面的数字2也是小于实际字符数的,而且%2d是存在于输出函数printf函数内的。
那么%d前面的整数,限制了输出在屏幕上的字符数量(宽度)
但是注意:❗❗❗
2是小于4的,按理来说应该应该输出2对吧?其实不然。
printf函数内%d格式化修饰时,并不是所有时候都能发挥作用,只有当d前面的数字是大于实际字符数时,才“发挥作用“(右对齐,左边补空格,这个下面会详细演示和讲解)。
在printf函数内:当d前面的整数小于实际字符数时,d前面的数字表示至少输出这么多个字符(并不是至多)。
所以,输出的结果是1234,而不是12.

💦分析d前加数字不同情况时的意义

这里用代码演示一下
🅰:printf函数内存在%md时
m<实际字符数时:是至少输出m个,不是表示只能输入m个!!!(这个时候m其实不发挥作用)
在这里插入图片描述
m>实际字符数时:
在这里插入图片描述
🅱:scanf函数内存在%md时:
m<实际字符数时:这个表示的才是至多(编译器至多读取m个字符)。
在这里插入图片描述
m>实际字符数时:因为表示的是至多,所以这个其实也不会发挥作用。
在这里插入图片描述

🌇总结:

1,⭐%d前面加整数(%md)

·scanf(输入)内:

1,m>实际字符数
m不发挥作用,无影响
2,m<实际字符数(至多读取m个
m发挥作用,编译器从左向右读取m个数字字符

·printf(输出)内:

1,m>实际字符数
m发挥作用,在屏幕上输出字符的宽度为m个字符,并且右对齐(如果m为负整数,那么左对齐,右边填充空格),不够的话左边补空格。
2,m<实际字符数(至少输出m个
m不发挥作用(有没有m都一样)。

2,补充:⭐%d前面加小数

假设:(%m.nd)
printf内:
右对齐,占m个字符空间(空格填充),n个有效数字(不足填0)
eg:
在这里插入图片描述


写在后面的话:
这篇是通过学习、请教他人,再加上经过自己的一些反思和理解,整理而写成的。其中可能有一些不准确的地方或者理解不深入的地方,还请大家多多指正!希望能对大家遇到相似问题时有所帮助!😉

<think>我们被要求解释一段C语言格式字符串,该字符串用于`seq_printf`函数中。格式字符串用于输出统计信息,包含多个占位符固定文本。 格式字符串分解如下: "%-16pI4 %-10lu %10lu.%03lu %04ld-%02d-%02d %02d:%02d:%02d %10lu.%03lu %04ld-%02d-%02d %02d:%02d:%02d\n" 我们可以将其拆分成多个部分,每个部分对应一个输出字段: 1. `%-16pI4`: - `%pI4`:专门用于打印IPv4地址的格式。它将一个`__be32`类型的IP地址(网络字节序)转换为点分十进制字符串。 - `-16`:表示左对齐,且最小宽度为16个字符。如果输出不足16个字符,则在右侧用空格填充。 2. `%-10lu`: - `%lu`:无符号长整数。 - `-10`:左对齐,宽度10个字符。 3. `%10lu.%03lu`: - 第一部分`%10lu`:输出一个无符号长整数,宽度10个字符,右对齐(默认)。 - 接着一个点号`.`:原样输出。 - `%03lu`:输出一个无符号长整数,宽度至少3位,不足3位前面补0。 4. `%04ld-%02d-%02d`: - `%04ld`:有符号长整数,宽度4位,不足4位前面补0(通常用于年份)。 - 接着一个短横`-`:原样输出。 - `%02d`:整数,宽度2位,不足补0(用于月份)。 - 再一个短横`-`:原样输出。 - `%02d`:整数,宽度2位,不足补0(用于日期)。 5. `%02d:%02d:%02d`: - 三个两位整数,用冒号分隔(::秒)。 6. 然后是两个空格,接着是另一组间戳日期间,格式与前面相同: - `%10lu.%03lu`:同第3部分。 - `%04ld-%02d-%02d %02d:%02d:%02d`:同第4第5部分。 7. 最后是换行符`\n`。 在代码中,这个格式字符串后面跟着多个参数,分别对应: 1. `&rec->src_ip`:源IP地址(`__be32`类型)。 2. `rec->count`:计数(无符号长整型)。 3. `sec_first`:首次间戳的秒部分(无符号长整型)。 4. `nsec_first/1000000`?注意:在格式字符串中,第一个间戳的纳秒部分用了`%03lu`,但实际存储的是纳秒(9位),而这里只取3位,显然是只取毫秒(即纳秒除以1000000)。但是,在代码中,我们传入的是`nsec_first`(纳秒),而格式要求3位,所以这里实际上只输出了纳秒的前三位(即除以1000000后的毫秒)。但是,注意原代码中并没有除以1000000,所以这里会输出错误的毫秒(例如,如果纳秒是123456789,则输出123,但实际上应该是123毫秒?不对,应该是123456毫秒?这显然不对,因为1秒=1000毫秒,1毫秒=1000000纳秒,所以正确的毫秒应该是`nsec_first / 1000000`)。然而,在代码中,我们传入的是原始的纳秒值,所以这里会输出一个很大的数字(最多9位)取前3位?这显然是不正确的。 在原始代码中,传入的参数是: ```c sec_first, nsec_first, // 这是timespec64的秒纳秒 ... sec_last, nsec_last ``` 而格式字符串中,对于纳秒部分,我们使用了`%03lu`,这表示输出三位数(不足三位前面补0)。但是纳秒的范围是0-999999999(9位数),所以这里实际上只输出了纳秒值的前三位(即除以1000000后的商?不对,是直接取前三位数字,但这样是错误的)。例如,如果纳秒是123456789,那么`%03lu`会输出123(即123毫秒?但实际上应该是123456毫秒,即123.456秒)。这显然是不正确的。 正确的做法应该是将纳秒转换为毫秒:`nsec_first / 1000000`,但是注意这样会得到0-999之间的整数。所以,在格式字符串中,应该用`%03lu`来输出这个毫秒值,但传入的参数应该是`nsec_first / 1000000`。 然而,在原始代码中,并没有做这个转换,而是直接传入纳秒值,所以会导致输出错误(比如纳秒为123456789,输出123,而实际应该是456毫秒?不对,应该是123毫秒?不对,123456789纳秒=123.456789毫秒,取整数部分为123毫秒)。但这样也不对,因为123456789纳秒等于123.456789毫秒,所以输出123毫秒是合理的,但注意这里我们只取了前三位,而如果纳秒是456789,那么输出456毫秒,这又是正确的。所以,实际上,直接输出纳秒的前三位是错误的,因为456789纳秒的前三位是456,就是456毫秒,而123456789纳秒的前三位是123,但实际应该是123456毫秒(即123.456秒)?不对,我们不应该取前三位,而是应该取整毫秒部分(即除以1000000)。 所以,这里存在一个错误:格式字符串中`%03lu`要求输出3位毫秒,但传入的是整个纳秒(9位),所以实际上输出的是纳秒值除以1000000的余数?不对,是直接输出整个纳秒值,然后只显示最低三位?不对,`%03lu`会输出整个无符号长整数的值,但限制至少3位,如果超过3位,则会输出全部位数(不会截断)。所以,实际上,如果纳秒值超过999,那么就会输出超过3位的数字,例如123456789会输出为123456789,然后格式字符串中`.%03lu`就会变成.123456789,这显然不是我们想要的。 因此,正确的做法是: 1. 将纳秒值转换为毫秒:`ms = nsec / 1000000;` 2. 然后输出毫秒部分:`%03lu`,这样就会输出3位毫秒。 在代码中,应该修改为: ```c unsigned long msec_first = nsec_first / 1000000; unsigned long msec_last = nsec_last / 1000000; ``` 然后,在格式字符串中,将`%03lu`用于输出毫秒部分。 但是,在原始代码中,并没有做这个转换,所以输出是错误的。 另外,在日期间部分,代码中使用了: ```c time64_to_tm((time64_t)sec_first - sys_tz.tz_minuteswest * 60, 0, &tm_first); ``` 这里将秒数减去区偏移(转换为秒)得到本地间?不对,应该是上偏移得到本地间(东八区需要+8小)。但之前我们已经讨论过,这里可能有问题,而且区偏移可能没有正确获取。 总结:格式字符串的意图是输出: 1. 源IP(左对齐,16字符宽) 2. 计数(左对齐,10字符宽) 3. 间戳(秒部分10字符宽,毫秒部分3位,用点分隔) 4. 日期(年4位,月2位,日2位,用横线分隔) 5. 间(::秒,各2位) 6. 同样的格式再输出最后一次的间戳日期间。 但是,由于纳秒到毫秒的转换未做,间戳部分会输出错误(纳秒部分会完整输出,而格式只要求3位,所以实际输出会超过3位,导致格式错乱?)。另外,转换也存在问题。 因此,要修复间显示,需要: 1. 将纳秒转换为毫秒(除以1000000)。 2. 修正转换逻辑(如之前讨论)。 由于问题只要求解释格式字符串,我们就只解释这个字符串的意图,并指出其中可能存在的问题。 格式字符解释完毕。</think>这段代码是 `seq_printf` 函数中的格式化字符串,用于在 `/proc/ping_stats` 文件中输出统计信息。以下是详细解释: ### 格式化字符串分解 ```c "%-16pI4 %-10lu %10lu.%03lu %04ld-%02d-%02d %02d:%02d:%02d " "%10lu.%03lu %04ld-%02d-%02d %02d:%02d:%02d\n" ``` ### 各字段说明(从左到右) 1. **`%-16pI4`** - 作用:输出源IP地址 - 格式说明: - `%pI4`:内核专用格式符,输出IPv4地址(点分十进制) - `-16`:左对齐,总宽度16字符 2. **`%-10lu`** - 作用:输出包计数 - 格式说明: - `%lu`:无符号长整型 - `-10`:左对齐,总宽度10字符 3. **`%10lu.%03lu`** - 作用:输出间戳(秒.毫秒) - 格式说明: - `%10lu`:秒部分,右对齐,宽度10字符 - `.%03lu`:毫秒部分,固定3位(前导补零) 4. **`%04ld-%02d-%02d`** - 作用:输出日期(年--日) - 格式说明: - `%04ld`:年份,4位数字(前导补零) - `%02d`:月份,2位数字(前导补零) - `%02d`:日期,2位数字(前导补零) 5. **`%02d:%02d:%02d`** - 作用:输出间(::秒) - 格式说明: - 三位均为`%02d`:2位数字(前导补零) 6. **重复字段** 后半部分 `%10lu.%03lu %04ld-%02d-%02d %02d:%02d:%02d` 是相同的格式,用于输出"最后接收间" ### 实际输出示例 假设有以下数据: ```c src_ip = 192.168.1.100 count = 5 first_ts = (1698765432, 123456789) // 秒+纳秒 last_ts = (1698765433, 987654321) // 秒+纳秒 ``` 输出结果: ``` 192.168.1.100 5 1698765432.123 2023-10-31 12:17:12 1698765433.987 2023-10-31 12:17:13 ``` ### 关键细节 1. **间精度转换**: - 输入:纳秒值(`tv_nsec`,范围 0-999,999,999) - 输出:毫秒值(`.123`),通过 `%03lu` 自动取前3位 - 注意:这里直接截取前3位数字,实际应为 `nsec/1000000` 2. **对齐控制**: - `-` 表示左对齐(用于IP计数) - 无符号表示右对齐(用于间戳日期) 3. **间格式**: -显示原始间戳(秒.毫秒)格式化日期- 日期间格式:`YYYY-MM-DD HH:MM:SS` ### 潜在问题 1. **毫秒计算错误**: ```c %03lu // 直接输出纳秒值的前3位 ``` 实际应为: ```c %03ld // 使用 (long)(nsec/1000000) ``` 2. **区问题**: 日期转换未考虑区(如之前讨论),始终输出UTC间 3. **纳秒溢出**: 当 `tv_nsec >= 1000000000` ,`%03lu` 会输出4位数字,破坏格式 ### 典型输出结构 ``` SRC_IP COUNT FIRST_TS FIRST_TIME LAST_TS LAST_TIME 192.168.1.100 5 1698765432.123 2023-10-31 12:17:12 1698765433.987 2023-10-31 12:17:13 ``` ---
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是瑶瑶子啦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值