Related
It seems like there is problem in scanf_s
Here is my code.
#include <stdio.h>
#include "stack.h"
int main(){
int disk;
int hanoi[3][9];
char input[3] = { 0,0,0 };
int moveDisk;
for (int i = 0; i < 9; i++) {
hanoi[0][i] = i + 1;
hanoi[1][i] = 0;
hanoi[2][i] = 0;
}
printf("Insert the number of disks(1~9): ");
scanf_s("%d", &disk);
while (input[0] != 'q') {
printf("%3c %3c %3c\n", 'A', 'B', 'C');
for (int i = 0; i < disk; i++) {
printf("%3d %3d %3d\n", hanoi[0][i], hanoi[1][i], hanoi[2][i]);
}
scanf_s("%s", &input); //getting moving disk -- LOCATION OF ERROR
}
}
I have no idea how to solve this
No doubt you tried to use scanf() in the normal way and Visual Studio reported an error instructing you to use scanf_s()? It is not a direct replacement. For all %c, %s and %[ format specifiers you must provide two arguments - the target receiving the input, and the size of target (or strictly the number of elements).
In VS2019 even at /W1 warning level, it issues a clear explanation of the problem in this case:
warning C4473: 'scanf_s' : not enough arguments passed for format string
message : placeholders and their parameters expect 2 variadic arguments, but 1 were provided
message : the missing variadic argument 2 is required by format string '%s'
message : this argument is used as a buffer size
Don't ignore the warnings, and certainly don't disable them globally (/W0).
So in this case:
scanf_s("%s", input, sizeof(input) ) ;
again more strictly:
scanf_s("%s", input, sizeof(input)/sizeof(*input) ) ;
but the latter is really only necessary for wscanf_s (wide characters). In both cases you could use the _countof() macro, but it is Microsoft specific.
scanf_s("%s", input, _countof(input) ) ;
Note also the lack of an & before input. You don't need it for an argument that is already array or pointer. That is true of scanf() too.
Whilst there are arguments for using scanf_s() over scanf() (which is intrinsically more dangerous), it can just make life difficult if you are learning from standard examples or using a different toolchain. The simpler solution is just to disable the warning, and understand that it is unsafe:
You cited the line
scanf_s("%s", &input);
There are several things wrong with this line:
You are reading a string into a character array. This is an exception to the normal pattern for scanf, in that you do not need the &.
You are using the semistandard scanf_s, instead of the normal scanf. scanf_s is supposed to be "safer", but in order for it to provide its safetiness guarantees you have to call it differently than normal scanf, too. You have to tell it the size of the array you're reading the string into. Combined with #1 above, I believe a more correct call would be scanf_s("%s", input, 3);.
For most purposes, a string of size 3 would be far too small for reading a line of input from the user. Since in this case I guess you're only reading a "line" to give yourself an opportunity to hit RETURN before the program makes another trip through its loop, I guess it's okay.
As I mentioned, scanf_s is not quite Standard, so using it is a mixed bag. Pros: 1. It's allegedly safer. 2. Some people (including perhaps your instructor) will recommend always using it for that reason. Cons: 3. It's nut fully standard (it's an optional part of the standard) meaning that not all C compilers and libraries will support it. 4. Its calling patterns are necessarily quite different than normal scanf; it is not a drop-in replacement, so confusion is likely. (I'm not saying "don't use scanf_s", but you should be aware of its somewhat dubious status.)
If you want to read a line of input from the user before continuing, and if the line might be a "q" or something else, scanf (of any variety) might not be the best choice. In particular, %s wants to read a non-whitespace string, so if you just hit the Return key, it's going to keep waiting. This might or might not be a problem for you. (Or it might not be something you need to worry about right now; you may have bigger fish to fry.)
How can I solve this problem during debugging?
Run your program step by step using the debugger. Then when you get the exception, you've found the line causing it.
Restart your program and go up to the line where the exception will occur. That is stop on that line without execution it.
Then with the debugger, you can look at all variables and try to understand if their value is what you expect.
Does this answered your question?
BTW: The compiler should at least emitted some warnings. You really should first fix those warnings. If you have no warning, make sure you have turned on all warnings in the compiler options.
So I'm pretty new too C, and programming. I'm learning little by little. A friend of mine gave me a good idea for a first project. The goal is too allow the user too select a department (Phone Directory for a Casino), and once that department has been selected, it will show all the numbers for that department.
Eventually I want too add more too this, but for now, I'm going too keep it simple.
#include <stdio.h>
main()
{
printf("Please Select Department [Security, Food & Beverage, Hotel]\n");
char str[20];
gets(str);
if (gets(str) == 8)
{
printf("You have selected Security\n");
printf("218-760-1122, 218-760-5564\n");
}
else if (gets(str) == 5)
{
printf("You have Selected Hotel\n");
printf("218-443-9810");
}
else if (gets(str) == 15)
{
printf("You have selected Security\n");
printf("218-550-9818, 218-443-1231\n");
}
return 0;
}
The problem with this program is when I compile and build the program, it prints the "selection" part of the script, where get(str) is used. After I select which one I would like too use, it goes blank, and nothing is displayed, but the script itself is still running.
I'm really new too this, and I'm assuming its something too do with how I'm determining which department is selected, in the if/else statements. I cant seem too figure out what I'm doing wrong. I've tried different solutions, usually getting different results, the closest being by adding a semi-colon too the last line of the else if statement, which runs the script, skips the first two else statements, and prints the security code.
3 warnings pop up for lines 10, 16, and 22
warning: comparison between pointer and integer [enabled by default]
This is a learning experience for me, and I would appreciate any help that can be offered.
So, the first and most important error is already in the comments, but repeating it here for completeness:
Do not use gets()! It is broken and therefore removed from the current C standard. With gets(), you have no control over how much input is actually read, so any buffer can overflow (and attackers will use that). The replacement is fgets(), which has a size argument.
Note the same holds for things like scanf("%s", ...). If you ever need to use a conversion to a string with the scanf family of functions, make sure to read the documentation, it tells you how to use a field width.
Back to your code, assuming you use fgets() like you should:
char str[20];
fgets(str, 20, stdin);
if (fgets(str, 20, stdin) == 8)
{
You have two errors here:
You call fgets() twice. Each call will attempt to read a line of input and store it in str. That's not what you want.
fgets() doesn't return the length but a pointer to str if it was successfull. If it failed, it returns NULL
So, the code should start like this:
char str[256];
if (!fgets(str, 256, stdin))
{
fputs("Error reading input.", stderr);
return 1;
}
Only after that error check, you should examine the contents of str, ie, what the user actually entered.
Note I also increased buffer size. People might enter longer lines by accident and there's really no need to be that tight with bytes nowadays.
Finally, just checking the length probably isn't a good idea. Instead, include string.h and use the strcmp() function.
I need help on a problem on C programming. I was wondering if there is a way to have a user input a word to the console and it would display whatever you program it to. Heres a example of what I want to do
int Choice;
int Energy = 100;
printf ("Type 2817!\n");
scanf ("%s", &Choice);
if(Choice == 2817)
{
printf ("You have started the game\n\n\n\n\n\n\n\n\n\n\n");
}
else if(Choice == "Energy") //This isnt working in my compiler.
//Apparently the == sign is the underlined error
{
printf("%d", Energy);
}
So far I can only type numbers but I want to be able to type words and be able to use a command. SO basically I want to be able to type "Energy" and it will show the amount of energy you have (printf ("%d", Energy)
Please help,
Thank you for reading.
You have enter wrong datatype when comparing the choice (int type) with a string text. In C/C++ unlike dynamic programming language, you cannot compare int with strings without properly converting either of them. You use strcpy for comparing string and == for comparing int.
Then your programming will run.
I'm not sure what your program is trying to do, but let me concentrate on the few obviously incorrect lines.
First, in
int Choice;
scanf ("%s", &Choice);
you have the wrong type for Choice: it is "int" whereas it should be a static array of char (let's say char Choice[32]). In this case you also have to remove the "&" before "Choice" in the scant, so that the code becomes:
char Choice[32];
scanf ("%s", Choice);
Moreover, in
else if(Choice == "Energy") //This isnt working in my compiler.
you are trying to compare two strings with the operator "==". This does not work in C. You should use the function "strcmp" the following way:
#include<string.h>
[...]
else if(strcmp(Choice, "Energy")==0)
You'd even better use the following to prevent any buffer overflow
else if(strncmp(Choice, "Energy", 32)==0)
(replace 32 with the maximum number of elements in Choice)
Edit Note that you should change the first comparison too from
if(Choice == 2817)
to
if(strncmp(Choice, "2817", 32))
because Choice is not an int anymore...
The best way to approach this problem would to be to make Choice a string, then compare it to string values, or extract data from the string (for example with atoi) and assign it to other variables. C is a statically typed programming language, an int cannot become (or be compared to) a string because they are not of the same type. If you want arbitrary data from the user, the best way to approach that problem is probably to use a string. :-)
Read your input as array of chars
example
char bleh[250];
scanf ("%s", bleh);//or scanf ("%s", &bleh[0]);
and then use strcmp command to compare two arrays
strcmp(&bleh[0], "blabla"); //function will return 0 if two arrays match
hope this helps
"String" is const char *, you cannot compare using == in C++. You need to use strcmp to compare string.
strcmp returns zero if strings are same.
Few mistakes in the code.
use strcmp() to compare string.
Declare int Choice; to char Choice[50]
Remove & while reading string in scanf()
Comparing with if(Choice == 2817) is not correct. use if(strcmp(Choice , "2817") == 0)
If I have a program that is using scanf to get user inputted data:
scanf("%d",&val);
When I run it through lint I get warnings saying that scanf returns a int and it is not being assigned anywhere. What is the c practice to solve this problem? Do I type cast it as void?
(void)scanf("%d",&val);
The C best practice to solve this warnings is simple: Check the resultcode. The scanf function returns in the int if the operation has gone ok or not, and stores the error usually in errno.
The lack of resultcode checks usually results in big errors in certain situations.
The proper answer, of course, is to not ignore the return value. For a robust program, you should always (EDIT: usually) check that the return value indicates success, and fail gracefully when it does not.
Ideally you would do something like
int scanVal = scanf("%d", &val);
if (scanVal != 1) {
// wait a minute, scanf couldn't match input to a "%d"!
}
and then act on the output in a sensible manner.
Instead of silencing the lint message, just check the return value to make sure nothing has gone wrong. From the man page:
RETURN VALUE
These functions return the number of input items
successfully matched and assigned, which can be
fewer than provided for, or even zero in the event of an early
matching failure.
The value EOF is returned if the end of input is reached before
either the first successful conversion or a matching failure occurs. EOF is also >returned if a read error occurs, in which case the
error indicator for the stream (see ferror(3)) is set, and
errno is set indicate the error.
scanf returns the number of input items successfully matched and assigned. Compare its return value to the number you expect in order to detect errors.
while (scanf("%d",&val) != 1)
printf("Try again.\n");
Use
#pragma warning(disable:<warning number>)
scanf("%d",&val);
#pragma warning(default:<warning number>)
And your compiler will suppress that warning.
The best practice is to assign the return value of scanf to see if all (or how many) items were read. In this particular case, if it returns anything but 1, something went wrong (e. g. you wanted a number but the user is giving you unprintable characters) and you should handle it appropriately.
If you want your code to be robust in the presence of bad input, don't use scanf("%d", ...).
For most errors, scanf("%d", &val) will return 0 to indicate that it wasn't able to read an int value (for example, if the input is "foo" rather than "123").
But if the input is syntactically valid but out of range (for example 9999999999999999999), the behavior is undefined.
Most implementations will probably behave sanely, but I don't know how much consistency there is.
To read an int value safely, use fgets() (not gets()) followed by strtol(). (fgets() can have problems with very long input lines, but there are ways to deal with that.)
I want to know the disadvantages of scanf().
In many sites, I have read that using scanf might cause buffer overflows. What is the reason for this? Are there any other drawbacks with scanf?
Most of the answers so far seem to focus on the string buffer overflow issue. In reality, the format specifiers that can be used with scanf functions support explicit field width setting, which limit the maximum size of the input and prevent buffer overflow. This renders the popular accusations of string-buffer overflow dangers present in scanf virtually baseless. Claiming that scanf is somehow analogous to gets in the respect is completely incorrect. There's a major qualitative difference between scanf and gets: scanf does provide the user with string-buffer-overflow-preventing features, while gets doesn't.
One can argue that these scanf features are difficult to use, since the field width has to be embedded into format string (there's no way to pass it through a variadic argument, as it can be done in printf). That is actually true. scanf is indeed rather poorly designed in that regard. But nevertheless any claims that scanf is somehow hopelessly broken with regard to string-buffer-overflow safety are completely bogus and usually made by lazy programmers.
The real problem with scanf has a completely different nature, even though it is also about overflow. When scanf function is used for converting decimal representations of numbers into values of arithmetic types, it provides no protection from arithmetic overflow. If overflow happens, scanf produces undefined behavior. For this reason, the only proper way to perform the conversion in C standard library is functions from strto... family.
So, to summarize the above, the problem with scanf is that it is difficult (albeit possible) to use properly and safely with string buffers. And it is impossible to use safely for arithmetic input. The latter is the real problem. The former is just an inconvenience.
P.S. The above in intended to be about the entire family of scanf functions (including also fscanf and sscanf). With scanf specifically, the obvious issue is that the very idea of using a strictly-formatted function for reading potentially interactive input is rather questionable.
The problems with scanf are (at a minimum):
using %s to get a string from the user, which leads to the possibility that the string may be longer than your buffer, causing overflow.
the possibility of a failed scan leaving your file pointer in an indeterminate location.
I very much prefer using fgets to read whole lines in so that you can limit the amount of data read. If you've got a 1K buffer, and you read a line into it with fgets you can tell if the line was too long by the fact there's no terminating newline character (last line of a file without a newline notwithstanding).
Then you can complain to the user, or allocate more space for the rest of the line (continuously if necessary until you have enough space). In either case, there's no risk of buffer overflow.
Once you've read the line in, you know that you're positioned at the next line so there's no problem there. You can then sscanf your string to your heart's content without having to save and restore the file pointer for re-reading.
Here's a snippet of code which I frequently use to ensure no buffer overflow when asking the user for information.
It could be easily adjusted to use a file other than standard input if necessary and you could also have it allocate its own buffer (and keep increasing it until it's big enough) before giving that back to the caller (although the caller would then be responsible for freeing it, of course).
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
#define SMALL_BUFF 3
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Size zero or one cannot store enough, so don't even
// try - we need space for at least newline and terminator.
if (sz < 2)
return SMALL_BUFF;
// Output prompt.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
// Get line with buffer overrun protection.
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// Catch possibility of `\0` in the input stream.
size_t len = strlen(buff);
if (len < 1)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[len - 1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[len - 1] = '\0';
return OK;
}
And, a test driver for it:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
Finally, a test run to show it in action:
$ printf "\0" | ./tstprg # Singular NUL in input stream.
Enter string>
No input
$ ./tstprg < /dev/null # EOF in input stream.
Enter string>
No input
$ ./tstprg # A one-character string.
Enter string> a
OK [a]
$ ./tstprg # Longer string but still able to fit.
Enter string> hello
OK [hello]
$ ./tstprg # Too long for buffer.
Enter string> hello there
Input too long [hello the]
$ ./tstprg # Test limit of buffer.
Enter string> 123456789
OK [123456789]
$ ./tstprg # Test just over limit.
Enter string> 1234567890
Input too long [123456789]
From the comp.lang.c FAQ: Why does everyone say not to use scanf? What should I use instead?
scanf has a number of problems—see questions 12.17, 12.18a, and 12.19. Also, its %s format has the same problem that gets() has (see question 12.23)—it’s hard to guarantee that the receiving buffer won’t overflow. [footnote]
More generally, scanf is designed for relatively structured, formatted input (its name is in fact derived from “scan formatted”). If you pay attention, it will tell you whether it succeeded or failed, but it can tell you only approximately where it failed, and not at all how or why. You have very little opportunity to do any error recovery.
Yet interactive user input is the least structured input there is. A well-designed user interface will allow for the possibility of the user typing just about anything—not just letters or punctuation when digits were expected, but also more or fewer characters than were expected, or no characters at all (i.e., just the RETURN key), or premature EOF, or anything. It’s nearly impossible to deal gracefully with all of these potential problems when using scanf; it’s far easier to read entire lines (with fgets or the like), then interpret them, either using sscanf or some other techniques. (Functions like strtol, strtok, and atoi are often useful; see also questions 12.16 and 13.6.) If you do use any scanf variant, be sure to check the return value to make sure that the expected number of items were found. Also, if you use %s, be sure to guard against buffer overflow.
Note, by the way, that criticisms of scanf are not necessarily indictments of fscanf and sscanf. scanf reads from stdin, which is usually an interactive keyboard and is therefore the least constrained, leading to the most problems. When a data file has a known format, on the other hand, it may be appropriate to read it with fscanf. It’s perfectly appropriate to parse strings with sscanf (as long as the return value is checked), because it’s so easy to regain control, restart the scan, discard the input if it didn’t match, etc.
Additional links:
longer explanation by Chris Torek
longer explanation by yours truly
References: K&R2 Sec. 7.4 p. 159
It is very hard to get scanf to do the thing you want. Sure, you can, but things like scanf("%s", buf); are as dangerous as gets(buf);, as everyone has said.
As an example, what paxdiablo is doing in his function to read can be done with something like:
scanf("%10[^\n]%*[^\n]", buf));
getchar();
The above will read a line, store the first 10 non-newline characters in buf, and then discard everything till (and including) a newline. So, paxdiablo's function could be written using scanf the following way:
#include <stdio.h>
enum read_status {
OK,
NO_INPUT,
TOO_LONG
};
static int get_line(const char *prompt, char *buf, size_t sz)
{
char fmt[40];
int i;
int nscanned;
printf("%s", prompt);
fflush(stdout);
sprintf(fmt, "%%%zu[^\n]%%*[^\n]%%n", sz-1);
/* read at most sz-1 characters on, discarding the rest */
i = scanf(fmt, buf, &nscanned);
if (i > 0) {
getchar();
if (nscanned >= sz) {
return TOO_LONG;
} else {
return OK;
}
} else {
return NO_INPUT;
}
}
int main(void)
{
char buf[10+1];
int rc;
while ((rc = get_line("Enter string> ", buf, sizeof buf)) != NO_INPUT) {
if (rc == TOO_LONG) {
printf("Input too long: ");
}
printf("->%s<-\n", buf);
}
return 0;
}
One of the other problems with scanf is its behavior in case of overflow. For example, when reading an int:
int i;
scanf("%d", &i);
the above cannot be used safely in case of an overflow. Even for the first case, reading a string is much more simpler to do with fgets rather than with scanf.
Yes, you are right. There is a major security flaw in scanf family(scanf,sscanf, fscanf..etc) esp when reading a string, because they don't take the length of the buffer (into which they are reading) into account.
Example:
char buf[3];
sscanf("abcdef","%s",buf);
clearly the the buffer buf can hold MAX 3 char. But the sscanf will try to put "abcdef" into it causing buffer overflow.
Problems I have with the *scanf() family:
Potential for buffer overflow with %s and %[ conversion specifiers. Yes, you can specify a maximum field width, but unlike with printf(), you can't make it an argument in the scanf() call; it must be hardcoded in the conversion specifier.
Potential for arithmetic overflow with %d, %i, etc.
Limited ability to detect and reject badly formed input. For example, "12w4" is not a valid integer, but scanf("%d", &value); will successfully convert and assign 12 to value, leaving the "w4" stuck in the input stream to foul up a future read. Ideally the entire input string should be rejected, but scanf() doesn't give you an easy mechanism to do that.
If you know your input is always going to be well-formed with fixed-length strings and numerical values that don't flirt with overflow, then scanf() is a great tool. If you're dealing with interactive input or input that isn't guaranteed to be well-formed, then use something else.
Many answers here discuss the potential overflow issues of using scanf("%s", buf), but the latest POSIX specification more-or-less resolves this issue by providing an m assignment-allocation character that can be used in format specifiers for c, s, and [ formats. This will allow scanf to allocate as much memory as necessary with malloc (so it must be freed later with free).
An example of its use:
char *buf;
scanf("%ms", &buf); // with 'm', scanf expects a pointer to pointer to char.
// use buf
free(buf);
See here. Disadvantages to this approach is that it is a relatively recent addition to the POSIX specification and it is not specified in the C specification at all, so it remains rather unportable for now.
The advantage of scanf is once you learn how use the tool, as you should always do in C, it has immensely useful usecases. You can learn how to use scanf and friends by reading and understanding the manual. If you can't get through that manual without serious comprehension issues, this would probably indicate that you don't know C very well.
scanf and friends suffered from unfortunate design choices that rendered it difficult (and occasionally impossible) to use correctly without reading the documentation, as other answers have shown. This occurs throughout C, unfortunately, so if I were to advise against using scanf then I would probably advise against using C.
One of the biggest disadvantages seems to be purely the reputation it's earned amongst the uninitiated; as with many useful features of C we should be well informed before we use it. The key is to realise that as with the rest of C, it seems succinct and idiomatic, but that can be subtly misleading. This is pervasive in C; it's easy for beginners to write code that they think makes sense and might even work for them initially, but doesn't make sense and can fail catastrophically.
For example, the uninitiated commonly expect that the %s delegate would cause a line to be read, and while that might seem intuitive it isn't necessarily true. It's more appropriate to describe the field read as a word. Reading the manual is strongly advised for every function.
What would any response to this question be without mentioning its lack of safety and risk of buffer overflows? As we've already covered, C isn't a safe language, and will allow us to cut corners, possibly to apply an optimisation at the expense of correctness or more likely because we're lazy programmers. Thus, when we know the system will never receive a string larger than a fixed number of bytes, we're given the ability to declare an array that size and forego bounds checking. I don't really see this as a down-fall; it's an option. Again, reading the manual is strongly advised and would reveal this option to us.
Lazy programmers aren't the only ones stung by scanf. It's not uncommon to see people trying to read float or double values using %d, for example. They're usually mistaken in believing that the implementation will perform some kind of conversion behind the scenes, which would make sense because similar conversions happen throughout the rest of the language, but that's not the case here. As I said earlier, scanf and friends (and indeed the rest of C) are deceptive; they seem succinct and idiomatic but they aren't.
Inexperienced programmers aren't forced to consider the success of the operation. Suppose the user enters something entirely non-numeric when we've told scanf to read and convert a sequence of decimal digits using %d. The only way we can intercept such erroneous data is to check the return value, and how often do we bother checking the return value?
Much like fgets, when scanf and friends fail to read what they're told to read, the stream will be left in an unusual state;
In the case of fgets, if there isn't sufficient space to store a complete line, then the remainder of the line left unread might be erroneously treated as though it's a new line when it isn't.
In the case of scanf and friends, a conversion failed as documented above, the erroneous data is left unread on the stream and might be erroneously treated as though it's part of a different field.
It's no easier to use scanf and friends than to use fgets. If we check for success by looking for a '\n' when we're using fgets or by inspecting the return value when we use scanf and friends, and we find that we've read an incomplete line using fgets or failed to read a field using scanf, then we're faced with the same reality: We're likely to discard input (usually up until and including the next newline)! Yuuuuuuck!
Unfortunately, scanf both simultaneously makes it hard (non-intuitive) and easy (fewest keystrokes) to discard input in this way. Faced with this reality of discarding user input, some have tried scanf("%*[^\n]%*c");, not realising that the %*[^\n] delegate will fail when it encounters nothing but a newline, and hence the newline will still be left on the stream.
A slight adaptation, by separating the two format delegates and we see some success here: scanf("%*[^\n]"); getchar();. Try doing that with so few keystrokes using some other tool ;)
There is one big problem with scanf-like functions - the lack of any type safety. That is, you can code this:
int i;
scanf("%10s", &i);
Hell, even this is "fine":
scanf("%10s", i);
It's worse than printf-like functions, because scanf expects a pointer, so crashes are more likely.
Sure, there are some format-specifier checkers out there, but, those are not perfect and well, they are not part of the language or the standard library.