I have snprintf
and it can avoid a buffer overflow, but why there is no function called snscanf
?
Code:
int main()
{
char * src = "helloeveryone";
char buf1[5];
sscanf(src,"%s",buf1); // here is a array out of bounds
}
So, I think a snscanf
is also needed. Why do we have only have snprintf
?
The snprintf function returns an integer value that equals, in magnitude, the number of characters written to the area addressed by dest . If the value returned is negative, then either the maxlen character limit was reached or some other error, such as an invalid format specification, has occurred.
The snprintf is a predefined library function of the stdio. h header file, which redirects the output of the standard printf() function to other buffers. The snprint() function is used to format the given strings into a series of characters or values in the buffer area.
Warning: The sprintf function can be dangerous because it can potentially output more characters than can fit in the allocation size of the string s . Remember that the field width given in a conversion specification is only a minimum value. To avoid this problem, you can use snprintf or asprintf , described below.
The controversial (and optional) Annex K to C11 adds a sscanf_s
function which takes an additional argument of type rsize_t
(also defined in Annex K) after the pointer argument, specifying the size of the pointed-to array. For better or worse, these functions are not widely supported. You can achieve the same results by putting the size in the conversion specifier, e.g.
char out[20];
sscanf(in, "%19s", out);
but this is awkward and error-prone if the size of the destination object may vary at runtime (you would have to construct the conversion specifier programmatically with snprintf
). Note that the field width in the conversion specifier is the maximum number of input characters to read, and sscanf
also writes a terminating null byte for %s
conversions, so the field width you pass must be strictly less than the size of the destination object.
There's no need for an snscanf()
because there's no writing to the first buffer argument. The buffer length in snprintf()
specifies the size of the buffer where the writing goes to:
char buffer[256];
snprintf(buffer, sizeof(buffer), "%s:%d", s, n);
The buffer in the corresponding position for sscanf()
is a null-terminated string; there's no need for an explicit length as you aren't going to write to it (it's a const char * restrict buffer
in C99 and C11).
char buffer[256];
char string[100];
int n;
if (sscanf(buffer, "%s %d", string, &n) != 2)
...oops...
In the output, you are already expected to specify the length of the strings (though you're probably in the majority if you use %s
rather than %99s
or whatever is strictly appropriate):
if (sscanf(buffer, "%99s %d", string, &n) != 2)
...oops...
It would be nice/useful if you could use %*s
as you can with snprintf()
, but you can't — in sscanf()
, the *
means 'do not assign scanned value', not the length. Note that you wouldn't write snscanf(src, sizeof(buf1), "%s", buf1)
, not least because you can have multiple %s
conversion specifications in a single call. Writing snscanf(src, sizeof(buf1), sizeof(buf2), "%s %s", buf1, buf2)
makes no sense, not least because it leaves an insoluble problem in parsing the varargs list. It would be nice to have a notation such as snscanf(src, "%@s %@s", sizeof(buf1), buf1, sizeof(buf2), buf2)
to obviate the need to specify the field size (minus one) in the format string. Unfortunately, you can't do that with sscanf()
et al now.
Annex K of ISO/IEC 9899:2011 (previously TR24731) provides sscanf_s()
, which does take lengths for character strings, and which might be used as:
if (sscanf_s(buffer, "%s %d", string, sizeof(string), &n) != 2)
...oops...
(Thanks to R.. for reminding me of this theoretical option — theoretically because only Microsoft has implemented the 'safe' functions, and they did not implement them exactly as the standard requires.)
Note that §K.3.3 Common definitions <stddef.h>
says: '... The type is rsize_t
which is the type size_t
.385)' (and footnote 385 says: 'See the description of the RSIZE_MAX macro in <stdint.h>.
' That means that in fact you can pass size_t
without needing a cast — as long as the value passed is within the range defined by RSIZE_MAX
in <stdint.h>
. (The general intention is that RSIZE_MAX
is a largish number but smaller than SIZE_MAX
. For more details, read the 2011 standard, or get TR 24731 from the Open Standards web site.)
In sscanf(s, format, ...)
, the the array of characters scanned is a const char *
. There is no writing to s
. The scanning stops when s[i]
is NUL. Little need for an n
parameter as an auxiliary limit to the scan.
In sprintf(s, format, ...)
, the array s
is a destination. snprintf(s, n, format, ...)
insures that data is not wriiten to s[n]
and beyond.
What would be useful is a flag extension to sscanf()
conversion specifiers so a limit could easily specified at compile time. (It can be done in a cumbersome fashion today, below, with a dynamic format or with sscanf(src,"%4s",buf1)
.)
// This is a proposed idea for C. - Not valid code today.
sscanf(src, "%!s", sizeof(buf1), buf)
Here !
would tell sscanf()
to read a size_t
variable for the size limit the upcoming string. Maybe in C17?
Cumbersome method that works today.
char * src = "helloeveryone";
char buf1[5];
char format[1+20+1+1];
sprintf(format, "%%" "%zu" "s", sizeof(buf1) - 1);
sscanf(src, format, buf1);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With