Well, IMHO this is due to the fact that scanf
doesn't force you to limit the input size, but fgets
does.
If you read the documentation and use scanf
properly, there really isn't much difference here:
char line[256];
scanf("%255[^\n]%*c",line); // %*c to remove trailing \n
fgets(line, 256, stdin)
Notice that I removed the leading space from the scanf
format string.
I'll go back to this later.
Both cases ensure we don't read more than we can.
But, from a safety perspective, we need to think:
fgets
force you to specify the size
- On
scanf
you need to remember to set array_capacity - 1
fgets
does this for you, so you actually pass in the capacity (or less)
- "Man what does this format even mean??"
It's easy to forget scanf details, and when you deal with a vast team with different backgrounds in programming, some people might have more trouble to write code with such details and might not even understand the format string.
So in general using fgets
is safer.
Now, regarding the leading space I removed.
When you use fgets
, you lose the ability to ignore whitespace
characters before input, so I had to remove that space to make both calls have almost the same result.
I guess we can't really say one way is "better" than the other, only that fgets
is more readable and will ensure that you remember to pass the size. Which is something you could also achieve by encapsulating the scanf
call into an input reading function that builds a format string correctly. Which would also enable you to skip leading whitespace
characters before reading.
EDIT
Just so it's clear, my point here is that from a safety perspective (as in "not writing outside the character array"), both solutions are valid since you can limit the number of characters read on both of them.
The code I displayed is purely to show that you can have it limited to a certain size, not that they have exactly the same effect.
As Andrew Henle said, the %*c
will indeed throw away one input character if the user provides more characters than the length we said to read.
But then again, that wasn't my point here and neither the question, IMHO.
I just put it there to be a bit closer to what fgets
does since fgets
removes the \n
from the buffer if the input isn't bigger than the amount you are trying to read.
The question asks about scanf
vs fgets
with no particular intent in mind, as far as I understood.
At least one wasn't described on the question.
There are a bunch of extra things you need to consider, depending on what the application need to do, of course.
Some considerations for fgets
:
- If your input has length
size - 1
, \n
will be left in the buffer
(which will mess up next character inputs...)
- If your input has length
< size
, \n
will be inserted on the string
(which you will need to remove, since you probably won't need it)
- If your input has length
> size
, remaining characters will be left on the buffer
(which you will need to treat that somehow too)
- If you change the array length, you need to update the size on the call
- Can't ignore whitespace before extracting data
Some considerations for scanf
:
- You need to decide if you can do the
%*c
safely or not
(Honestly, it's better to not use %*c
, but just have a leading space on the format string when reading characters, as you already are using on the code on your question)
- You need to remember to set
capacity - 1
on the width specifier
- If you change the array length, you need to update the
width
specifier
- Can ignore whitespace before extracting data
- Most people don't know format specifiers more complex than
%s
, %d
and such, so it might be hard for other people on the team to understand the code
- If the input length matches the
width
you specified, \n
will be left on the buffer
- If the input length is greater than
width
, remaining characters will be left on the input buffer too
Anyway, there's probably many more scenarios to consider, and many of them depend on the exact situation you need to deal with.
Both functions have their pros and cons.
I don't think either of them is inherently "bad", both of them require the user to use them correctly and treat some errors themselves, but, fgets
certainly has the advantage of forcing you to provide a length and being more readable.
I hope my point is clearer now.