sscanf的奇怪行为

时间:2014-02-21 12:45:18

标签: c scanf

我发现了一些奇怪的事情。以下是代码示例:

...
char *start = strchr(value, '(');
if(start)
{
    char buf[LEN];
    memset(buf, 0, LEN);
    int num = sscanf(start, "(%s)", buf);
    if(num)
    {
        buf[strlen(buf) - 1] = '\0';
        sprintf(value, "%s", buf);
    }
...

如果值为“(xxx)”,例如,此操作后值将为“xxx”。
但如果值是“([34] xx {4,7} | 1234567890)”那么值将是“[34] xx {4,7}”。
谁能解释一下呢?

P.S。这是ARM平台。

3 个答案:

答案 0 :(得分:2)

int num = sscanf(start, "(%s)", buf);

此处,sscanf在遇到start指向的缓冲区中的空格时返回。输入字符串中有一个空格:

"([34]xx{4,7}| 1234567890)"
              ^ space here

scanf返回成功匹配和分配的输入项数。在此,它将返回1num的值为1。接下来,您将buf块中的此语句覆盖if中的最后一个字符。

buf[strlen(buf) - 1] = '\0';

这解释了你的程序的输出。现在,关于代码的一些事情:

您无需执行memset(buf, 0, LEN);。只需执行char buf[LEN] = {0};这将使用空字节填充数组。

sscanf不会检查缓冲区buf的数组范围,您正在编写sscanf正在从start读取的字符串。如果buf的大小不够,sscanf将尝试在缓冲区buf之外的内存中写入。由于非法内存访问,这将导致未定义的行为甚至程序崩溃。您应该以{{1​​}}的格式字符串给出字段宽度,以防止缓冲区溢出。

sscanf

格式字符串#define STRINGIFY(s) #s // preprocessor command # stringifies the token s #define XSTRINGIFY(s) STRINGIFY(s) #define LEN 10 // max buffer length without the null byte // inside a function char buf[LEN + 1]; // +1 for the null byte const char *format = "(" XSTRINGIFY(LEN) "%s)"; // "(%10s)" int num = sscanf(start, format, buf); 中的10表示最多"(%10s)"个字符存储在10指向的缓冲区中,然后是空字节{{1}最后会自动添加。因此,buf块中不需要以下内容:

\0

事实上,这样做会覆盖if中的最后一个字符,因为buf[strlen(buf) - 1] = '\0'; // overwrites the last char before null byte in buf. 不计算空字节。

答案 1 :(得分:1)

sscanf与%s一起使用,遇到空格时会终止。这就是你输出“[34] xx {4,7}”而不是预期行为的原因

答案 2 :(得分:1)

  

格式字符串由一系列指令组成,这些指令描述了如何处理输入字符序列。如果指令处理失败,则不再读取其他输入,并返回scanf()。 “失败”可以是以下任何一种:输入失败,意味着输入字符不可用或匹配失败,这意味着输入不合适(见下文)。

在您的情况下,sscanf匹配起始(,然后解析下一个令牌%s,该令牌消耗数据直到第一个空白字符。 sscanf然后无法匹配),这意味着解析停止。成功读取并分配了一个令牌,因此返回值为1

请注意,使用scanf时,无法检测在分配的最后一个令牌之后发生的匹配失败。