Why to use fgets() over scanf() in C?
Last Updated :
22 Feb, 2023
Any time you use an *f function, whether it be printf, scanf, or their derivatives (fprintf, fscanf, etc…), you are doing more things than you might realize. Not only are you reading (or writing) something, but-and here’s the problem- you are interpreting it. The format string can be thought of as kind of an ‘eval’ function like you would see if you program in Lisp. So the problem with simply reading input from the user and echoing it back out is that a malevolent actor can simply insert a function pointer or executable code and voila, you are no longer in control.
Advantage of using scanf(): The user doesn’t need to know the size of the stack, which is the starter code is one-hundred bytes. That’s kind of good, although anyone can just sit there trying longer and longer input strings until BufferOverflow error occur. Ideally, you would just write a script that automatically tries increasing string sizes and reads the exit code of the program. Once it detects an error, you simply work your way back to a close estimate of the stack. Any modern operating system using memory address randomization to make the whole process of hijacking an application harder, but it’s by no means impossible.
fgets() over scanf(): fgets function is short for file-get-string. Remember that files can be pretty much anything on *nix systems (sockets, streams, or actual files), so we can use it to read from standard input, which again, is also technically a file. This also makes our program more robust, because as mentioned in the source, simply adjust the code to read from the command line, a file, or stdin, without much trouble. Crucially, the fgets function also allows us to specify a specific buffer length, thereby disallowing any buffer overflow attacks. If you look at the full code below, you’ll notice that a default buffer size has been defined as a macro. Remember that C can’t use ‘const int’ variables to initialize an array properly. You can hack it using variable-length arrays (VLA’s), but it’s not ideal, and I strongly recommend against it. So while in C++ we would normally use literally anything else, here we do use preprocessor macros, but keep in mind that C and C++ have vastly different capabilities when it comes to their static type checking, namely that C++ is good over C. Hence fgets() method can actually Handle Errors during I/O. So the code to actually read the user input is as follows:
C
char * inputBuffer = malloc ( sizeof ( char ) * DEFAULT_BUFFER_SIZE);
memset (inputBuffer, NUL, DEFAULT_BUFFER_SIZE);
char * result = NULL;
while (result == NULL) {
result = fgets (inputBuffer, DEFAULT_BUFFER_SIZE, stdin);
if (inputBuffer[ strlen (inputBuffer) - 1] != '\n' ) {
ErrorInputStringTooLong();
result = NULL;
}
}
|
As expected, we dynamically allocate a buffer of predetermined size. Dynamically allocating it as opposed to stack-allocating it gives us another layer of protection against stack smashing. Note: We are explicitly zeroing out the memory in the inputBuffer. C does nothing for you, and malloc is no exception. The call to malloc returns a pointer to the first memory cell of the requested memory, but the values in every single one of those cells are unchanged from before the call. For all intents and purposes, they are garbage, so we zero them out. Note also that by zeroing out, we are automatically giving ourselves the null terminator, although the fgets function does actually append a null to the end of the input string, provided there is enough room in the buffer.
When the user inputs too long string: You’ll notice that I check to make sure the last read value was not a new line. If that is true, this means that the user passed in an input string that was too long. To fix this, we need to set our ‘result’ variable to NULL so we cycle through the loop again, but we also need to clear out the input buffer. Otherwise, the program will simply read from the old input, which has not yet been used, rather than prompting the user for additional input. To handle this, I supply two additional functions.
C
static inline void ErrorInputStringTooLong()
{
fprintf (stderr, "[ERROR]: The input was too long , please try again.\n");
ClearInputBuffer();
}
static inline void ClearInputBuffer()
{
char c = NULL;
while ((c = getchar ()) != '\n' && c != EOF) {
}
}
|
Note: Usually, to clear the input buffer, one would just call fseek(stdin, 0, SEEK_END); but it doesn’t seem to work on every platform. Hence, the above-described method is used. Note: if fgets returns an error, you should call ferror() to figure out what went wrong.
Similar Reads
How to Read Data Using sscanf() in C?
In C, sscanf() function stands for "String Scan Formatted". This function is used for parsing the formatted strings, unlike scanf() that reads from the input stream, the sscanf() function extracts data from a string. In this article, we will learn how to read data using sscanf() function in C. Examp
2 min read
When to Use Enum Instead of Macro in C?
In C programming, both enums (short for enumeration) and macros are used for defining symbolic names associated with specific values. However, there are situations where using an enum is more advantageous than using macros. In this article, we will learn when to use an enum instead of a macro in C.
6 min read
Getting started with C
C language is a popular programming language that was developed in 1970 by Dennis Ritchie at Bell Labs. The C programming language was developed primarily to build the UNIX operating system. It is widely used because it is simple, powerful, efficient, and portable. Features of C Programming Language
5 min read
How to write in a file using fputs() in C
fputs() is a function declared in stdio.h header file. It is used to write the contents of the file. The function takes 2 arguments. The first argument is a pointer to the string which is to be written and the second argument is the pointer of the file where the string is to be written. It returns 1
2 min read
%n in scanf() in C with Example
In C, %n is a special format specifier. In the case of printf() function the %n assign the number of characters printed by printf(). When we use the %n specifier in scanf() it will assign the number of characters read by the scanf() function until it occurs. Key points: It is an edit conversion code
2 min read
Effect of adding whitespace in the scanf() function in C
In this article, we will discuss the scenarios regarding adding white space before or after the format specifier in a scanf() function in C Programming Language. Adding a whitespace character in a scanf() function causes it to read elements and ignore all the whitespaces as much as it can and search
3 min read
Interesting Facts in C Programming | Set 2
Below are some more interesting facts about C programming: 1. Macros can have unbalanced braces: When we use #define for a constant, the preprocessor produces a C program where the defined constant is searched and matching tokens are replaced with the given expression. Example: [GFGTABS] C #include
2 min read
All forms of formatted scanf() in C
C language has standard libraries that allow input and output in a program. The stdio.h or standard input-output library in C has methods for input and output. scanf(): The scanf() method n C Language, reads the value from the console as per the type specified. It returns an integer type, the number
7 min read
Convert String to int in C
In C, we cannot directly perform numeric operations on a string representing a numeric value. We first need to convert the string to the integer type. In this article, we will discuss different ways to convert the numeric string to integer in C language. Example: Input: "1234"Output: 1234Explanation
6 min read
C Program To Print Your Own Name
Printing your own name means displaying your name on the computer screen. In this article, we will learn how to print your own name using a C program. Examples Input: name = "Rahul"Output: RahulExplanation: The program prints "Rahul" to the screen. Input: name = "Vikas"Output: VikasExplanation: The
4 min read