You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

scanf格式字符串中'|'符号含义及缓冲区问题求助

关于scanf格式串中|的含义及你的代码问题解析

首先直接给结论:格式串里的|就是一个普通的匹配字符,没有任何特殊语法含义——你找不到官方规范提它,是因为它不属于scanf的特殊格式说明符,只是个需要输入流精确匹配的普通字符而已。

先拆解你的两段代码问题

第一段代码崩溃的原因

scanf("%[^a]%s",&szA);

这里的%[^a]是扫描集,会读取所有**不是'a'**的字符,直到碰到'a'或者输入结束;紧接着的%s会跳过空白符,读取后续的非空白字符序列。但你犯了一个致命错误:这两个格式都是往同一个数组szA里写,而且没有限制读取长度。

比如输入hello123aworld,%[^a]会把hello123全部写入szA,然后%s会接着把world往数组后面写——如果前面的内容已经占满了256字节的数组,后续写入就会直接触发缓冲区溢出,这就是程序崩溃的根源。

第二段代码"正常运行"的本质

scanf("%[^a]|%s",&szA);

这里的|是普通匹配字符,scanf的逻辑是:

  1. 先执行%[^a]读取非'a'字符到szA;
  2. 尝试从输入流中读取一个|字符:
    • 如果输入流里刚好有|,就跳过它,继续执行后面的%s;
    • 如果输入流里没有|(比如碰到了'a'或者输入结束),scanf会直接终止,后面的%s根本不会执行。

你觉得它"无论输入都正常",其实是因为大多数情况下,%[^a]结束后输入流里没有|,%s没被触发,避免了两次写入同一个数组导致的溢出。但这绝对不是"安全"——如果输入是aaaaaaaa|bbbbbbbb...(后面的b足够多),%s还是会往数组里写,照样会溢出崩溃,只是你测试的输入没触发而已。

解答你的几个疑问

  1. 为什么找不到|的官方规范?
    C标准里scanf的格式串规则很明确:除了以%开头的格式说明符、空白符(空白符会匹配任意数量的空白字符),其他所有字符都是普通匹配字符——要求输入流对应位置必须是这个字符,匹配成功就跳过,失败则终止scanf。|就是这类普通字符,所以官方不会单独为它写说明,就像不会单独说明@、#这些字符在格式串里的作用一样。

  2. 输入包含多个|时为什么还能运行?
    比如输入xyz||abc,%[^a]会读取xyz|(因为|不是'a'),然后scanf尝试匹配格式串里的|,刚好输入流里下一个字符就是|,匹配成功后跳过,接着%s读取abc。只要此时数组还有剩余空间,就不会崩溃;但如果输入的内容总长度超过255(要留一个字节存字符串结束符\0),照样会溢出。

  3. 格式串是否对应两个缓冲区?
    完全不是!你只传递了&szA这一个地址,所以%[^a]和%s都是往同一个数组里写的——这才是第一段代码崩溃的核心问题。第二段代码只是因为|的匹配逻辑,很多时候%s没执行,才没触发溢出,但本质上还是不安全的写法。

最后给你一个安全的写法建议

不管用不用|,scanf的%[^...]和%s都不会检查数组长度,缓冲区溢出风险极高。正确的做法是指定最大读取长度,比如:

// 留一个字节给'\0',所以最多读255个字符
scanf("%255[^a]", szA);

或者更推荐用fgets这类更安全的函数来读取输入,避免手动处理格式串的坑。

内容的提问来源于stack exchange,提问作者W.Perrin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:57:38