`sscanf(s, "%d %n", &i, &n)` 中的 n 代表什么?

9
该手册指出,sscanf的签名为:
sscanf(const char *restrict s, const char *restrict format, ...);

我在SO上看到了一个答案,其中使用了sscanf函数来检查输入是否为整数。

bool is_int(char const* s) {
    int n;
    int i;
    return sscanf(s, "%d %n", &i, &n) == 1 && !s[n];
}

看到!s[n],似乎表明我们要检查sscanf是否扫描到了终止字符\0。因此,我认为n表示当函数结束时,sscanf在字符串s中的索引位置。
但是变量i呢?它是什么意思?
编辑:
更明确地说,我看到sscanf的签名需要一个char *类型的指针作为第一个参数。第二个参数是格式说明符,以便它知道如何解析字符序列,接下来是尽可能多的转换说明符。现在我明白i用于保存解析出的整数。
由于只有一个格式说明符,我试图推断出n的功能。
我的上述假设对n正确吗?

3
这是 sscanf 存储解析值的 int 变量。 - Daniel Fischer
1
整数变量 i 的地址 (&)。 - Oded
3
请继续阅读手册页面下方的内容。 - evil otto
2
i 表示我需要一个更好的名称。 - Hans Passant
4个回答

11

看起来提问者已经得到了他的答案,但是由于我自己查找并运行代码,以下是我从《C语言口袋参考》(Herbert Shildt著,第二版)中scanf()部分找到的:

%n接收一个整数值,该值等于目前为止读取的字符数

至于返回值:

scanf()函数返回一个数字,该数字等于成功赋值的字段数量

sscanf()函数与此类似,只不过它将输入从提供的缓冲区参数(在这种情况下是s)中获取。"==1"测试确保只解析了一个整数,而!s[n]则确保解析后的整数之后的输入缓冲区良好地终止,或者确保字符串中真正只有一个整数。

运行此代码,像"32"这样的s值会给出"true"值(我们的系统上没有定义bool作为类型),但像"3 2"这样的s则会给出"false"值,因为在那种情况下,s[n]为"2",n的值为2("3 "被解析以创建该整数)。如果s是" 3 ",这个函数仍然返回true,因为所有的空格都被忽略了,n的值为3。

另一个示例输入"3m"会给出"false"值,这是你所期望的。


我认为可以通过提供更详细的答案来替换已经很好的答案,这样其他遇到同样问题的人就能更快地找到答案。非常感谢你的努力,伙计! - Aufwind

6

sscanf()的手册页上直接摘录:

转换说明

[...]

n

不期望任何输入字符;取而代之的是,已从输入中使用的字符数将通过下一个指针存储, 必须是指向int的指针。这不是一个转换,尽管可以使用*赋值抑制字符来抑制。 C标准说:“执行%n指令不会增加在执行完成时返回的赋值计数”,但勘误表似乎与此相矛盾。 可能明智的做法是不要对%n转换的效果对返回值产生任何假设。


谢谢,这正是我所需要的。现在你指出了相关位置,我明白了原意。 - Aufwind
是的,这对于一个刚接触 C 语言概念的初学者来说确实很多。 :-) - Aufwind
我更改了答案,因为有一个更详细的答案,没有冒犯之意。只是想确保其他有同样问题的人可以更快地找到最佳答案。但我并不比之前少感激,你已经得到我的+1。;-) - Aufwind

1
我想指出原始代码存在缺陷:
bool is_int(char const* s) {
    int n;
    int i;
    return sscanf(s, "%d %n", &i, &n) == 1 && !s[n];
} 

我将解释为什么,以及我将解释sscanf格式字符串。
首先是错误的:
给定输入“1”,即整数一,sscanf将1存储到i中。然后,由于后面没有空格,sscanf不会触及n。而n未初始化。因为sscanf将i设置为1,所以sscanf返回的值将为1,表示扫描了1个字段。由于sscanf返回1,表达式的一部分
sscanf(s, "%d %n", &i, &n) == 1

将为真。因此,&& 表达式的另一部分将被执行。由于 n 未初始化,s[n] 将访问某个随机的内存位置。

解释格式:

"%d %n"

尝试扫描一个可能是十进制数、整数或科学计数法数的数字。如果该数字是整数,则必须跟随至少一个空格。空格可以是空格符、\n、\t和某些其他不可打印字符。只有在后面跟随空格时,它才会将n设置为扫描到该点的字符数,包括空格。
这段代码可能是预期的内容:
    static bool is_int(char const* s) 
    {
        int i;
        int fld;
        return (fld = sscanf(s, "%i", &i)) == 1;
    }

    int main(int argc, char * argv[])
    {
        bool ans = false;

        ans = is_int("1");
        ans = is_int("m");

        return 0;
    }

这段代码基于以下条件:如果 s 是一个整数,那么 sscanf 将会扫描它,并且 fld 将恰好为 1。如果 s 不是一个整数,则 fld 将为零或 -1。如果存在其他内容,如单词,则为零;如果没有任何内容,只有空字符串,则为 -1。


更正:上述代码应该使用 %d 而不是 %i。我把这两个搞混了。 - Indinfer
1
断言“因为后面没有空格,sscanf不会触及n。”并不完全正确。格式“%d%n”并不强制要求空格,实际上它允许空格存在,这可能仍然是一个问题,如果你需要严格验证的话。使用“%d%n”来解析“123 ”(带有尾随空格)会通过测试,而“%d%n”则不会。 - jweyrich

0

变量i的意思是直到读取了一个整数值。

不过你想问什么?不是太清楚!代码将会(尝试)从字符串中读取整数到'i'。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接