Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the n stand for in `sscanf(s, "%d %n", &i, &n)`?

Tags:

c

The man page states that the signature of sscanf is

sscanf(const char *restrict s, const char *restrict format, ...);

I have seen an answer on SO where a function in which sscanf is used like this to check if an input was an integer.

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

Looking at !s[n] it seems to suggest that we check if sscanf scanned the character sequence until the termination character \0. So I assume n stands for the index where sscanf will be in the string s when the function ends.

But what about the variable i? What does it mean?

Edit:

To be more explicit: I see the signature of sscanf wants a pointer of type char * as first parameter. A format specifier as seconf parameter so it knows how to parse the character sequence and as much variables as conversion specifiers as next parameters. I understand now that i is for holding the parsed integer.

Since there is only one format specifier, I tried to deduce the function of n.

Is my assumption above for n correct?

like image 243
Aufwind Avatar asked Nov 02 '12 16:11

Aufwind


3 Answers

Looks like the op has his answer already, but since I bothered to look this up for myself and run the code...

From "C The Pocket Reference" (2nd Ed by Herbert Shildt) scanf() section:

%n Receives an integer of value equal to the number of characters read so far

and for the return value:

The scanf() function returns a number equal to the number of the number of fields that were successfully assigned values

The sscanf() function works the same, it just takes it's input from the supplied buffer argument ( s in this case ). The "== 1" test makes sure that only one integer was parsed and the !s[n] makes sure the input buffer is well terminated after the parsed integer and/or that there's really only one integer in the string.

Running this code, an s value like "32" gives a "true" value ( we don't have bool defined as a type on our system ) but s as "3 2" gives a "false" value because s[n] in that case is "2" and n has the value 2 ( "3 " is parsed to create the int in that case ). If s is " 3 " this function will still return true as all that white space is ingored and n has the value of 3.

Another example input, "3m", gives a "false" value as you'd expect.

like image 187
n0741337 Avatar answered Nov 02 '22 05:11

n0741337


Verbatim from sscanf()'s man page:

Conversions

[...]

n

Nothing is expected; instead, the number of characters consumed thus far from the input is stored through the next pointer, which must be a pointer to int. This is not a conversion, although it can be suppressed with the * assignment-suppression character. The C standard says: "Execution of a %n directive does not increment the assignment count returned at the completion of execution" but the Corrigendum seems to contradict this. Probably it is wise not to make any assumptions on the effect of %n conversions on the return value.

like image 28
alk Avatar answered Nov 02 '22 06:11

alk


I would like to point out that the original code is buggy:

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

I will explain why. And I will interpret the sscanf format string.

First, buggy:

Given input "1", which is the integer one, sscanf will store 1 into i. Then, since there is no white space after, sscanf will not touch n. And n is uninitialized. Because sscanf set i to 1, the value returned by sscanf will be 1, meaning 1 field scanned. Since sscanf returns 1, the part of the expression

sscanf(s, "%d %n", &i, &n) == 1

will be true. Therefore the other part of the && expression will execute. And s[n] will access some random place in memory because n is uninitialized.

Interpreting the format:

"%d %n"

Attempts to scan a number which may be a decimal number or an integer or a scientific notation number. The number is an integer, it must be followed by at least one white space. White space would be a space, \n, \t, and certain other non-printable characters. Only if it is followed by white space will it set n to the number of characters scanned to that point, including the white space.

This code might be what is intended:

    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;
    }

This code is based on, if s is an integer, then sscanf will scan it and fld will be exactly one. If s is not an integer, then fld will be zero or -1. Zero if something else is there, like a word; and -1 if nothing is there but an empty string.

like image 32
Indinfer Avatar answered Nov 02 '22 06:11

Indinfer