-1
#include <stdio.h>
#include <math.h>

int testprime (int x )
{
    int i, root ;
    if (x == 2) {
        return 1 ;
    }
    if (x%2 == 0) {
        return 0 ;
    }
    root = sqrt (x) ;
    for (i = 3 ; i < root ; i ++) {
        if (x%i == 0 ) {
            return 0 ;
        }
    }
    return 1 ;
}

int main ( )
{
    int n ,  root ;
    printf ("Enter the testing number\n") ;
    //gets (n) ;
    scanf ("%d", &n) ;
    if (n <= 0) {
        printf ("Error \n") ;
    }
    if (n == 1)
        printf ("Unity number is %d\n",n) ;
    if (0 == testprime(n) )
        printf ("isn't a prime number\n") ;
    else {
        printf ("%d is a prime number \n",n) ;
    }
    return 0 ;
}

here, when I use scanf , I find the desired output . But when I use gets() function , it comes out as a garbage .. where is the problem ? I wrote the gets() function as a comment here to point where i am facing problem .

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 5
    Side note: [Why is the gets function so dangerous that it should not be used?](https://stackoverflow.com/q/1694036/12149471) – Andreas Wenzel Aug 23 '21 at 18:00
  • @Ashikul Islam gets is not a standard C function opposite to scanf.:) – Vlad from Moscow Aug 23 '21 at 18:01
  • 5
    For one, `gets` is no longer part of the official languages standard library. It should not be used (and in truth, never should have been). Second, `gets` would be used to read *strings*, not `int`. – WhozCraig Aug 23 '21 at 18:01
  • 5
    Have you looked (e.g., in documentation) at what `scanf("%d", &n)` does -- in partcular that `%d` part -- vs. what `gets(n)` does? – T.J. Crowder Aug 23 '21 at 18:02
  • `gets` is dangerous and as the man pages suggests – it should never be used. It can be exploited by buffer overflows , and i'm sure other ways due to the way it reads input –  Aug 23 '21 at 18:07
  • `gets()` does *not* read integers, and `gets(n)` should give you a compile-time error. – Steve Summit Aug 23 '21 at 18:19
  • you need to learn the differences between types – 0___________ Aug 23 '21 at 18:26
  • "where is the problem" Well, did you start by trying to read the documentation for `gets`? In your own words, what *exactly* do you think it should do, and why do you think it should do that? – Karl Knechtel Aug 23 '21 at 21:08

4 Answers4

0

The returned value of gets is char*, and it takes a char[] buffer to write to as a parameter. It can be (but shouldn't, as it's prone to buffer overflows) used to read a line in. In your case, I guess you could use it and then use something like atoi() or strtol() to read the first or more numbers from a line, but... I guess you were just mistaken about what it did.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
wojand
  • 158
  • 9
0

The functions gets and fgets are used for reading a line of input from the user as a string. In contrast to scanf, it cannot read in a number directly. If you want to convert the string that it reads to a number, then you must do that in a separate step, for example by using the function strtol.

Although the simplest solution to read a number from the user is to use scanf with the %d format specifier, I don't recommend doing that, as that function does crazy things, because it is not designed for line-based input. For example, when the user enters "6sdfh4q", scanf accepts the "6" as valid input, but subsequent calls to scanf will fail, unless you remove the remainder of the line ("sdfh4q") from the input stream manually.

See this guide for more information on why not to use scanf:

A beginners' guide away from scanf()

The function gets was removed from the ISO C standard, because it was too dangerous to use. Therefore, for reading a string from the user, I recommend that you use the function fgets instead. Afterwards, you can use the function strtol to convert that string to a number, for example like this:

//the code below requires the following headers
#include <stdio.h>
#include <stdlib.h>

[...]

char buffer[100], *p;
long l;

//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
    fprintf( stderr, "error reading input\n" );
}
else
{
    //attempt to convert string to number
    l = strtol( buffer, &p, 10 );
    if ( p == buffer )
    {
        printf( "error converting string to number\n" );
    }
    else
    {
        //do something with the converted number
    }
}

However, this code has the following problems:

  1. It does not check whether the input buffer was large enough to contain the whole line of input.

  2. It does not check whether the number the user entered is so high or low that it is not representable as a long.

  3. The function strtol provided a long as the converted number, so additional range checks will be required if you want an int. You will have to make sure that the number is not too high or too low to be representable as an int.

  4. If the user enters "6sdfh4q", it will consider this valid input of the number 6, because the first character of the line is a digit. It will ignore the rest of the line, although the entire input should probably be rejected in this case. It would probably be best to reject the input if it contains any non-whitespace characters after the converted digits.

The following code solves all of these problems.

In this code, I provide a function get_int_from_user with a simple interface. It takes one parameter which points to the string that the user should be prompted with (e.g. "Please enter a number: "). The function will repeat this prompt until the user enters a valid number. Once the user has entered a valid number, it will return that value as an int (provided that the number is not too high or low to be representable as an int).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>

int get_int_from_user( const char *prompt )
{
    for (;;) //loop forever until user enters a valid number
    {
        char buffer[1024], *p;
        long l;

        fputs( prompt, stdout );

        //get one line of input from input stream
        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "unrecoverable error reading from input\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
        {
            int c;

            printf( "line input was too long!\n" );

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF )
                {
                    fprintf( stderr, "unrecoverable error reading from input\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        //attempt to convert string to number
        errno = 0;
        l = strtol( buffer, &p, 10 );
        if ( p == buffer )
        {
            printf( "error converting string to number\n" );
            continue;
        }

        //make sure that number is representable as an "int"
        if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
        {
            printf( "number out of range error\n" );
            continue;
        }

        //make sure that remainder of line contains only whitespace,
        //so that input such as "6sdfh4q" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto next_outer_loop_iteration;
            }
        }

        return l;

    next_outer_loop_iteration:
        continue;
    }
}

Instead of writing

printf ("Enter the testing number\n") ;
scanf ("%d", &n);

you can call the function get_int_from_user like this:

n = get_int_from_user( "Enter the testing number: " );
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • Missing ``. Should be noted that this function will error out as if the line was too long / an IO error occurred when used to read the final line of `stdin` which does not contain a newline (`echo -n "123" | program`). Though, whether or not input terminated by EOF fulfills the proper definition of a *line* is up for debate. – Oka Aug 23 '21 at 20:00
  • @Oka: Oh, you are right. The macros `INT_MIN` and `INT_MAX` should require the header `limits.h`. For some reason, my Microsoft compiler did not complain and the code compiled without including the header. I guess the header was implicitly included by one of the other headers. If I instead try to compile my code in `gcc`, the compiler provides an error message about the missing header. Therefore, I have now edited the code to include that header. – Andreas Wenzel Aug 23 '21 at 20:23
  • @Oka: Since this function is designed to take user input, I don't think it is important to distinguish between end-of-file and stream error. Although it is also possible for the user to enter end-of-file as input (CTRL-D on Linux, CTRL-Z on Windows), that is a very uncommon thing to do, and I am unsure on what would be a meaningful way to handle it. Therefore, as far as I can tell, treating it as an error seems the best thing to do. – Andreas Wenzel Aug 23 '21 at 20:36
  • @Oka: I could attempt to make the function more pipe-friendly, but this would make the function even more complex than it already is. The function already is so complex that I'm afraid that OP will probably not understand half of the function, so I don't want to make the function even more complex, which would probably make it impossible for OP to understand it. – Andreas Wenzel Aug 23 '21 at 20:46
  • Fair points, though I'm simply speaking to the semantics of the messaging produced, depending on what you consider a valid *line* of input (from my short example "excessive input" is confusing, vs. "unexpected EOF"). `ferror` might offer some further granularity. As for the point of complexity, maybe this monolithic function isn't the best teaching tool - considering they're struggling with basic types. Perhaps some more minimal examples are needed. – Oka Aug 23 '21 at 21:50
  • @Oka: You are right that my function needed additional explanation. I have expanded my answer accordingly. Also, I have changed the code a bit, so that what you did (`echo -n "123" | program`) should now work. – Andreas Wenzel Aug 24 '21 at 06:46
0

The old gets routine is no longer part of the standard C library. If you are using some other version of gets, such as something supplied for a programming course you are taking, we cannot tell you what the difference between that version and scanf is because we do not have the documentation for that version.

If you are using the old standard gets function, then a key difference is that scanf reads input and converts it to various types. Notably, for %d, scanf reads input and converts a decimal numeral in it to an int. So the corresponding argument is a pointer to an int where scanf will put the converted value. scanf("%d", &n); provides that pointer.

In contrast, gets reads characters and does not interpret them except to look for a newline character which will end the current input. It should be passed a pointer to an array of char where it will put the input.

That old standard gets is not suitable for reading a decimal numeral without further processing, and passing it the int n, as in gets(n), is wrong for two reasons. One, gets needs to know where to put the characters, so it needs a pointer, not an int. Two, gets should be passed a pointer to an array of char, not an int.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
-1

simply, gets() is used for char arrays (or strings).

using gets with n (which is an int) should not even let you compile the program.

If you really wanted to use gets() you could do something like:

#include <stdio.h>
#include <stdlib.h>

/* ... */

int main() {
    char input[20]; //assuming input integer with less than 20 digits
    gets(input);

    int n = atoi(input), root;

    /* ... */
}

edit: but as many people have pointed out, you really shouldn't

Alex
  • 31
  • 4
  • `should not even let you compile the program` in C it will. – 0___________ Aug 23 '21 at 18:18
  • I don't recommend using the function `atoi` for converting a string to a number, as that function will simply return `0` on a conversion error. Therefore, it is impossible to detect whether the string actually contains a zero or whether the string is not a valid number. For that reason, I recommend using the function `strtol` instead, as I have done in my answer. – Andreas Wenzel Aug 23 '21 at 18:25
  • 1
    [Which functions from the standard library must (should) be avoided?](https://stackoverflow.com/a/46563868/584518) Among those who should never be used, we find `gets` and `atoi`. – Lundin Aug 24 '21 at 06:52
  • It will compile, but usually with a warning to be fair. Also as others mentioned `gets` and `atoi` should be avoided, because they can be unsafe. At least you added a comment, but it's wrong, if we have a 20-digit int, then the tailing zero still writes outside of the buffer. – Uncle Dino Aug 26 '21 at 15:41
  • 1
    @Sasszem: Why is the comment wrong? The comment says that it is assuming an integer with "less than 20 digits", not "less than or equal 20 digits", which you seem to be implying. – Andreas Wenzel Sep 01 '21 at 16:43
  • @AndreasWenzel My bad, <20 is not <=20, so there's room for that zero byte, but I'd still not recommend gets in any circumstances. (also, if you compile this program on linux but for some reason you have CRLF endings in an input file it will only work up to 18 digits) – Uncle Dino Sep 03 '21 at 16:56