详解C语言中的 %*s 和 %.*s
C 语言中的 %*s
和 %.*s
详解
在 C 语言中,%*s
和 %.*s
是 printf
和 scanf
函数家族(包括 sprintf
、sscanf
等)中用于格式化字符串的特殊格式说明符(Format Specifiers)。它们是 %s
(字符串格式)的变体,通过星号 *
和点号 .
引入动态宽度或精度控制。这些说明符允许更灵活地处理字符串输出和输入,广泛应用于格式化打印、日志记录和数据解析。
本文将从原理、用法、示例和注意事项四个方面详细解释 %*s
和 %.*s
,并区分 printf
(输出)和 scanf
(输入)的场景。内容基于 ANSI C 标准(C89/C99/C11),适用于 GCC/Clang 等编译器。
1. 原理概述
- 基本格式说明符
%s
:用于字符串。 - 在
printf
:打印字符串。 - 在
scanf
:读取字符串。 - 星号
*
的作用: - 在
printf
:表示宽度或精度由函数参数动态提供,而不是硬编码。 - 在
scanf
:表示忽略该字段(不存储输入)。 - 点号
.
的作用:指定精度(precision),控制字符串的最大长度。 - 结合
*
时,精度由参数提供。 - 语法结构:
%*s
:星号在宽度位置。%.*s
:星号在精度位置(点后)。- 可以组合,如
%*.*s
:宽度和精度都动态。
这些机制允许运行时控制格式,提高代码灵活性。例如,在循环中动态调整输出宽度。
2. %*s
的用法
在 printf
中的用法
- 原理:
*
替换宽度字段,宽度由额外 int 参数提供。字符串右对齐(默认),宽度不足时用空格填充。 - 格式:
%*s
(宽度为参数,精度无限制)。 - 示例:
#include <stdio.h>
int main() {
char *str = "hello";
printf("%*s\n", 10, str); // 输出: " hello"(右对齐,宽度 10)
printf("%*s\n", 3, str); // 输出: "hello"(宽度 3 < 长度 5,完整打印)
return 0;
}
- 输出:
hello hello
- 解释:第一个参数(10 或 3)是宽度,第二个是字符串。如果宽度 > 字符串长度,用空格填充(默认右对齐)。加
-
如%-*s
左对齐。
在 scanf
中的用法
- 原理:
*
表示“抑制赋值”(suppression),读取但不存储该字段。用于跳过输入部分。 - 格式:
%*s
(读取一个字符串,但丢弃)。 - 示例:
#include <stdio.h>
int main() {
char buf[20];
int num;
// 输入: "skip 123 keep"
sscanf("skip 123 keep", "%*s %d %s", &num, buf);
printf("num = %d, buf = %s\n", num, buf); // 输出: num = 123, buf = keep
return 0;
}
- 解释:
%*s
读取 “skip” 但不存储,接下来读取 123 到 num,”keep” 到 buf。
3. %.*s
的用法
在 printf
中的用法
- 原理:
.*
指定精度由 int 参数提供,控制打印的最大字符数(不包括 null 终止符)。 - 格式:
%.*s
(精度为参数,宽度无限制)。 - 示例:
#include <stdio.h>
int main() {
char *str = "hello";
printf("%.*s\n", 3, str); // 输出: "hel"(精度 3,只打印前 3 字符)
printf("%.*s\n", 10, str); // 输出: "hello"(精度 10 > 长度 5,完整打印)
return 0;
}
- 输出:
hel hello
- 解释:第一个参数(3 或 10)是精度,第二个是字符串。精度限制输出长度,超出部分截断。
- 组合
%*.*s
:
printf("%*.*s\n", 10, 3, "hello"); // 输出: " hel"(宽度 10,精度 3,右对齐)
在 scanf
中的用法
- 原理:
.
后精度指定最大读取字符数,*
表示忽略。 - 格式:
%.*s
(但通常用%ns
如%10s
,动态精度需*
后跟参数)。 - 注意:scanf 的精度直接用数字如
%10s
,动态用%*s
但无.
组合标准。实际中,%.*s
在 scanf 不标准支持,通常用固定数字。 - 示例(固定精度):
#include <stdio.h>
int main() {
char buf[10];
// 输入: "helloworld"
sscanf("helloworld", "%9s", buf); // 读取前 9 字符: "helloworl"
printf("%s\n", buf);
return 0;
}
- 动态精度:scanf 不直接支持
%.*s
,需用%*s
忽略或固定数字。替代:用fgets
手动解析。
4. 示例代码与验证
以下是一个综合示例,展示 printf
和 scanf
中的用法:
#include <stdio.h>
int main() {
// printf 示例
char *str = "C Language";
printf("宽度动态: %*s\n", 15, str); // 输出: " C Language"
printf("精度动态: %.*s\n", 5, str); // 输出: "C Lan"
printf("组合: %*.*s\n", 10, 3, str); // 输出: " C L"
// scanf 示例
char buf1[20], buf2[20];
int num;
// 输入字符串: "ignore 456 keep this"
sscanf("ignore 456 keep this", "%*s %d %s %s", &num, buf1, buf2);
printf("scanf: num=%d, buf1=%s, buf2=%s\n", num, buf1, buf2); // 输出: num=456, buf1=keep, buf2=this
return 0;
}
- 预期输出:
宽度动态: C Language
精度动态: C Lan
组合: C L
scanf: num=456, buf1=keep, buf2=this
您可以用 Dev-C++(前文教程)编译运行验证。
5. 注意事项
- 缓冲区溢出:
- scanf
%s
或%.*s
无边界检查,易溢出。推荐用%ns
如%19s
(n=缓冲-1)。 - 示例:char buf[10]; scanf(“%9s”, buf); 避免溢出。
- null 终止符:
- printf
%.*s
不添加 null,只打印指定字符。 - scanf 总是添加 null 终止符,确保缓冲足够。
- 宽度和精度组合:
- 宽度 > 精度:用空格填充。
- 精度 > 字符串长:完整打印。
- scanf 忽略:
%*s
读取到空格/换行,跳过一个词。- 多字段:用于解析固定格式输入(如 CSV)。
- 兼容性:
- ANSI C 支持
%*s
和%.*s
。 - 嵌入式系统:检查编译器(如 Keil)支持。
- Python format 类似:”{:10s}”.format(str) 或 “{:.3s}”.format(str)。
- 性能:
- 动态格式适合变量场景,但固定格式更快。
总结
%*s
:在 printf 中动态宽度;在 scanf 中忽略字段。%.*s
:在 printf 中动态精度;在 scanf 中限制长度(固定数字更常见)。- 应用:增强格式灵活性,适合日志、解析。
- 实践:用 Dev-C++ 测试示例,结合 Wireshark 抓包(如网络日志)验证字符串格式。
如果需要更多示例(如 scanf 动态精度替代)、结合 DHCP 的网络配置或 Python 模拟,请提供细节,我可扩展!