Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can a null character be embedded in a conversion specifier for scanf?

Tags:

c

scanf

Perhaps I'm misinterpreting my results, but:

#include <stdio.h>

int
main(void)
{
    char buf[32] = "";
    int x;
    x = scanf("%31[^\0]", buf);
    printf("x = %d, buf=%s", x, buf);
}
$ printf 'foo\n\0bar' | ./a.out
x = 1, buf=foo

Since the string literal "%31[^\0]" contains an embedded null, it seems that it should be treated the same as "%31[^", and the compiler should complain that the [ is unmatched. Indeed, if you replace the string literal, clang gives:

warning: no closing ']' for '%[' in scanf format string [-Wformat]

Why does it work to embed a null character in the string literal passed to scanf?

-- EDIT --

The above is undefined behavior and merely happens to "work".

like image 467
William Pursell Avatar asked Feb 02 '21 15:02

William Pursell


People also ask

Does scanf read null character?

A... | Hacker News. Unexpected places where you can use null bytes: gets, fgets and scanf("%s"). All three will read and store null bytes into your string from the input, and keep going: gets and fgets only terminate at a newline character and scanf only terminates at whitespace (which doesn't include the null byte).

Does scanf stop at null?

Yes, it possible because '\0' is a non-white-space. scanf() will consider that is the end of the string. So %s can match a empty string. You can use the m specifier to allocate a corresponding buffer to hold the input.

Can you print a null character in C?

I could look for '\r' or '\n' or my own chosen end character within putch(), but this is why we have '\0' in C. Hi, No, it is not possible to do this without modifying printf(). The null character is only used to signify the end of a string, and should not be printed.

Does scanf add null terminator in C?

The scan terminates at whitespace. A null character is stored at the end of the string, which means that the buffer supplied must be at least one character longer than the specified input length. %c : Scan a character (char). No null character is added.


1 Answers

First of all, Clang totally fails to output any meaningful diagnostics here, whereas GCC knows exactly what is happening - so yet again GCC 1 - 0 Clang.

And as for the format string - well, it doesn't work. The format argument to scanf is a string. The string ends at terminating null, i.e. the format string you're giving to scanf is

scanf("%31[^", buf);

On my computer, compiling the program gives

% gcc scanf.c
scanf.c: In function ‘main’:
scanf.c:8:20: warning: no closing ‘]’ for ‘%[’ format [-Wformat=]
    8 |     x = scanf("%31[^\0]", buf);
      |                    ^
scanf.c:8:21: warning: embedded ‘\0’ in format [-Wformat-contains-nul]
    8 |     x = scanf("%31[^\0]", buf);
      |                     ^~

The scanset must have the closing right bracket ], otherwise the conversion specifier is invalid. If conversion specifier is invalid, the behaviour is undefined.

And, on my computer running it,

% printf 'foo\n\0bar' | ./a.out
x = 0, buf=

Q.E.D.

like image 178