Search code examples
cfgets

fgets giving me null


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

int main () {

    int firstDigit;
    char *firstNumber = 0;

    char operator;

    int secondDigit;
    char *secondNumber = 0;
    
    printf("digit ");
    scanf("%d *[^\n]", &firstDigit);
    fgets(firstNumber, firstDigit, stdin);
    printf("%s \n", firstNumber);

}

The code here gives me null as firstNumber while it clearly should just be a string.

here is the results of running this code:

$ ./result.exe
digit 2
12
(null) 

Solution

  • I had envisioned leaving just comments, but it is clear you have a misconception about the use of fgets() and scanf(). Continuing from my comments, let's see if we can help.

    First, a pointer is a normal variable that holds the memory address as its value. When you declare a pointer, it is uninitialized (setting the pointer NULL or 0 doesn't provide a valid address to anything). Before you attempt to access the memory address held by the pointer, that memory address must be a valid memory address you can read or write to.

    Since you attempt to use firstNumber with fgets(), the memory address held by firstNumber must be the starting address for a sufficient number of characters to hold what you are reading. The easiest way to provide that is simply to declare firstNumber as a character-array large enough to hold the user input. (and since an array is converted to a pointer to the first element on access, you simply use the array name as your pointer)

    Second, when you learn enough about how scanf() works to understand the pitfalls it presents to the new C programmer, you will understand it is far better to read user input with fgets() and then convert the input to what you need using sscanf() instead of scanf(). Any time you take user-input or attempt a numeric conversion, you cannot use either function correctly unless you check the return to determine if the input or conversion succeeded or failed.

    Since you want to read a sequence of characters and then convert the input to an integer value, simply declare firstNumber as a character array, read the user input into the array and then attempt to convert the contents of firstNumber to the integer firstDigit (if you just want the first ASCII digit in the user-input, that is simply firstNumber[0], no conversion required)

    On the other hand, if you want to convert the entirety of what the user entered into an integer, just use sscanf() in the same manner you would use scanf(), except you read from the buffer firstNumber instead of stdin.

    Now, understand fgets() reads the '\n' generated by the user pressing Enter into the buffer it fills. While it isn't necessary to remove it for the conversion to int, it is often necessary if you want to use the buffer as a string and don't want any dangling '\n' handing off the end. Thankfully it is easy to do with strcspn().

    Putting it altogether to read the user-input into firstNumber and convert that string representation of an integer into an int, you could do:

    #include <stdio.h>
    #include <string.h>   /* for strcspn() */
    
    #define MAXC 1024   /* if you need a constant, #define one (or more) */
    
    int main () {
    
        int firstDigit = 0;
        char firstNumber[MAXC];
        
        fputs ("digit: ", stdout);    /* no conversion, fputs is fine */
        
        /* use firstNumber as a temporary buffer to hold input, validate
         * fgets succeeds, otherwise user canceled with manual EOF
         * ctrl + d (or ctrl + z on windows)
         */
        if (!fgets (firstNumber, sizeof firstNumber, stdin)) {
            puts ("(user canceled input)");
            return 0;
        }
        
        /* trim '\n' from end of buffer by overwriting with nul-character
         * optional -- used for outputting firstNumber as string.
         */
        firstNumber[strcspn (firstNumber, "\n")] = 0;
        
        /* now parse firstDigit from buffer with sscanf() */
        if (sscanf (firstNumber, "%d", &firstDigit) != 1) {
            fputs ("error: invalid integer.\n", stderr);
            return 1;
        }
        
        printf ("\nfirstNumber contains string: \"%s\"\n"
                "FirstDigit contains integer:  %d\n",
                firstNumber, firstDigit);
    }
    

    Understanding this wasn't your ultimate goal, this should provide all the basic building blocks you need to move forward reading input from the user and handling any conversions required.

    Example Use/Output

    $ ./bin/firstnumdigit
    digit: 24371
    
    firstNumber contains string: "24371"
    FirstDigit contains integer:  24371
    

    Or what happens if the user screws up (they will)

    $ ./bin/firstnumdigit
    digit: two hundred twenty one
    error: invalid integer.
    

    And handling the manual EOF indicating the user canceled input:

    $ ./bin/firstnumdigit
    digit: (user canceled input)
    

    Look things over and let me know if you have questions.