Using %s in C correctly – very basic level

For both *printf and *scanf%s expects the corresponding argument to be of type char *, and for scanf, it had better point to a writable buffer (i.e., not a string literal).

char *str_constant = "I point to a string literal";
char str_buf[] = "I am an array of char initialized with a string literal";

printf("string literal = %s\n", "I am a string literal");
printf("str_constant = %s\n", str_constant);
printf("str_buf = %s\n", str_buf);

scanf("%55s", str_buf);

Using %s in scanf without an explcit field width opens the same buffer overflow exploit that gets did; namely, if there are more characters in the input stream than the target buffer is sized to hold, scanf will happily write those extra characters to memory outside the buffer, potentially clobbering something important. Unfortunately, unlike in printf, you can’t supply the field with as a run time argument:

printf("%*s\n", field_width, string);

One option is to build the format string dynamically:

char fmt[10];
sprintf(fmt, "%%%lus", (unsigned long) (sizeof str_buf) - 1);
...
scanf(fmt, target_buffer); // fmt = "%55s"

EDIT

Using scanf with the %s conversion specifier will stop scanning at the first whitespace character; for example, if your input stream looks like

"This is a test"

then scanf("%55s", str_buf) will read and assign "This" to str_buf. Note that the field with specifier doesn’t make a difference in this case.

Leave a Comment