2
votes

Hi have been doing research and I have found three most common different types of input inside a C console application:

  • scanf("%[^\n]s", *char): Pick up a string until it finds a break line. It is advised not to use it due to overflow buffer problems.
  • gets(*char): Pick up a string until it finds a break line. It is equivalent to the first. It is advised not to use it due to overflow buffer problems too.
  • fgets(*char, LENGTH, *FILE): Pick up a string of LENGTH characters, from the file FILE. It is the safest of the three and the most recommended.

Having said this, I will show an example (MCVE) of the use of the three functions and what is picking up them:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char hello[7]; //Will store word 'hello'; Length is 6 = 5+(1)+1 = strlen("hello")+(BreakLineChar)+NullTermination - BreakLineChar will appear (or not) depending input type
    int i;

    // SCANF()
    printf("Input HELLO: ");
    scanf("%[^\n]s",hello);
    fflush(stdin);
    printf("Length: %d\n", strlen(hello));
    for (i = 0; i < strlen(hello); i++)
    {
        printf("%i: %c\n", i, hello[i]);
    }
    printf("\n\n");

    // GETS()
    printf("Input HELLO: ");
    gets(hello);
    fflush(stdin);
    printf("Length: %d\n", strlen(hello));
    for (i = 0; i < strlen(hello); i++)
    {
        printf("%i: %c\n", i, hello[i]);
    }
    printf("\n\n");

    // FGETS()
    printf("Input HELLO: ");
    fgets(hello,sizeof(hello),stdin);
    fflush(stdin);
    printf("Length: %d\n", strlen(hello));
    for (i = 0; i < strlen(hello); i++)
    {
        printf("%i: %c\n", i, hello[i]);
    }
    printf("\n\n");

    return 0;
}


This code, have next output:

Input HELLO: hello
Length: 5
0: h
1: e
2: l
3: l
4: o


Input HELLO: hello
Length: 5
0: h
1: e
2: l
3: l
4: o


Input HELLO: hello
Length: 6
0: h
1: e
2: l
3: l
4: o
5:




Process returned 0 (0x0)   execution time : 5.757 s
Press any key to continue.


So, as we can see, three conclusions can be drawn from this:

  • scanf: Pick up user's input until '\n' char (does not pick up the character '\n'). So string hello would be:

       hello[0] = 'h'

       hello[1] = 'e'

       hello[2] = 'l'

       hello[3] = 'l'

       hello[4] = 'o'

       hello[5] = '\0'

  • gets: Equivalent to the point above. So string hello would be:

       hello[0] = 'h'

       hello[1] = 'e'

       hello[2] = 'l'

       hello[3] = 'l'

       hello[4] = 'o'

       hello[5] = '\0'

  • fgets: Pick up user's input until '\n' char (pick up the character '\ n' too). So string hello would be:

       hello[0] = 'h'

       hello[1] = 'e'

       hello[2] = 'l'

       hello[3] = 'l'

       hello[4] = 'o'

       hello[5] = '\n'

       hello[6] = '\0'


Are my conclusions correct? Any information to add?

Thank you.

2
It would be more interesting, I think, if you typed a longer string as input, like "supercalafragalisticexpialidocious" or something.Steve Summit
regarding: scanf("%[^\n]s",hello); This is wrong for two reasons. 1) to avoid a buffer overflow, always include a MAX CHARACTERS modifier that is 1 less than the length of the input buffer because this input format specifier always appends a NUL byte to the input. and 2) since the statement read all way to (But not including) the newline, the next character will NEVER be a 's'. Should always check the returned value to assure the operation was successful. Suggest: if( scanf("%6[^\n]", hello ) != 1 ) // handle erroruser3629249
Hi, @user3629249. Regarding first reason, it is avoided, if you see my answer to this question, you will see how I avoid it using C preprocessor for include a #define inside scanf clause. Regarding second reason, thank you, I will update my answer including this comprobation. Regards,JuMoGar
regarding: fflush(stdin); Per the C standard, this is undefined behavior. fflush() is only for output streams (regardless of what visual studio might allow)user3629249
Oh, ok, Then, for clean stdin buffer... which function should be used?JuMoGar

2 Answers

1
votes

Your conclusions are correct. However, of the three input methods that you reference only fgets is safe:

  • scanf into a string without a limit can overflow an input buffer; see a fix below.
  • gets is deprecated, because it could not possibly be made safe of buffer overflow (why?).

To fix scanf specify the size of your buffer, and leave space for null terminator:

scanf("%6[^\n]", hello);

Note that s at the end is not necessary when you are using [^\n] format specifier.

0
votes

In this answer I will make a compilation of all the answers obtained, and of the research work that I continue doing (I will update the answer as I get new information):

  • gets is insecure, one against it is buffer overflow
  • Most safe method for inputting by console is fgets, but it have a contra: This command reads '\n' character too. To solve this we can do the following:

    #include <stdio.h>
    #include <string.h>
    #define MAX_SIZE_STRING 5
    
    int main(void)
    {
        char string[MAX_SIZE_STRING+1]; // MAX_SIZE_STRING + 1 due to a string needs '\0' char at the end
        int i;
    
        printf("Input a string: ");
        fgets(string, MAX_SIZE_STRING, stdin);
    
        // Remove '\n' character inside the string:
        string[strcspn(string, "\n")] = '\0';
    }
    
  • scanf is a very powerful function, which is necessary to know it thoroughly to use it well. Speaking of scanf, I'm going to show an example of how to use scanf which controls everything you need (size, when stop reading, avoid bufferOverflow,...):

    #include <stdio.h>
    #include <string.h>
    #define MAX_SIZE_STRING 5
    #define STR_(X) #X
    #define STR(X) STR_(X)
    
    int main(void)
    {
        char string[MAX_SIZE_STRING+1]; // MAX_SIZE_STRING + 1 due to a string needs '\0' char at the end
        int i;
    
        printf("Input a string: ");
        scanf(" %" STR(MAX_SIZE_STRING) "[^\t\n]%*[^\n]", string);
        // EXPLANATION OF THE COMMNAD:
        // First blanck-space: It is used for empty string. If user entries only blank-spaces, it will no stored in the var until user input any char
        // STR(TAM_MAX): Use of preprocessor for adding a max length to the input
        // [^\t\n]: Pick up until find a '\t' or a '\n'
        // So, at the end, this command is equal to: scanf(" %5[^\t\n]s", string);
        // %*[^\n] remove end/break line (\n) so, by this way, buffer overflow is avoied 
    }