Search code examples
carraysstdineofgetchar

How to exit stdin when EOF is reached in C programming


I'm having troubles with my program. The aim of it is to read an input of numbers from the user, and when they stop inputting numbers (ctrl-d) it collects the inputted numbers and prints out 'Odd numbers were: blah blah' and 'Even numbers were: blah blah'.

I'm having trouble with how to exit the program at EOF and when it feels like I have overcome that problem, another problem occurs which is my program doesn't print the numbers from the array. It only prints 'Odd numbers were:' and 'Even numbers were:'.

Any help is appreciated. Thanks

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
int main(void) {
int n, i, array[1000];
    i=0;
    while (i = getchar() !=EOF) {
        scanf("%d", &array[i]);
        i++;
    }   
    printf("Odd numbers were:");
    i=0 ;
    while(i = getchar() != EOF) { 
        if(array[i]%2!=0) {
            printf(" %d", array[i]);
            i++;
        }
    }
    printf("\nEven numbers were:");
    i=0 ;
    while(i = getchar() !=EOF) { 
        if (array[i]%2==0) {
            printf(" %d", array[i]);
            i++;
        } 
    }
        printf("\n");
return 0;
}

Solution

  • Performing Single-Digit Conversion to int

    You may be making things a bit harder on yourself than it needs to be. While you can use while (scanf ("%d", &array[i]) == 1) to read whitespace separated integers and avoid having to perform a manual conversion, if your intent is to read single-digits, then using getchar() is fine. (for that matter, you can read any multi-digit integer with getchar(), it is simply up to you to provide a conversion from the ASCII characters to final numeric value)

    The manual conversion for single-digit characters is straight forward? When you read digits as characters, you are not reading the integer value represented by the digit, you are reading the ASCII value for the character that represents each digit. See ASCII Table and Description. In order to convert a single ASCII character digit to it's integer value, you must subtract the ASCII value of '0'. (note: the single-quotes are significant)

    For example if you read a digit with int c = getchar();, then you need to subtract '0' to obtain the single-digit integer value, e.g. int n = c - '0';

    When filling an array, you must ALWAYS protect against writing beyond the bounds of your array. If you declare int array[1000] = {0}; (which has available zero-based array indexes of 0-999), then you must validate that you never write beyond index 999 or Undefined Behavior results. To protect your array bounds, simply keep track of the number of indexes filled and test it is always below the number of array elements available, e.g.

    while (n < MAX && (c = getchar()) != EOF)   /* always protect array bounds */
        if ('0' <= c && c <= '9')               /* only handle digits */
            array[n++] = c - '0';               /* convert ASCII to int */
    

    Next, while you are free to test n % 2 (using the modulo operator), there is no need (little endian). Since any odd number will have its ones-bit set to 1, all you need is a simple bitwise comparison, e.g (7 in binary is 0111).

        if (array[i] & 1)                       /* if ones-bit is 1, odd */
            printf (" %d", array[i]);
    

    Of course, for even numbers, the ones-bit will be 0 (e.g. 8 in binary is 1000), so the corresponding test can be:

        if ((array[i] & 1) == 0)                /* if ones-bit is 0, even */
            printf (" %d", array[i]);
    

    Putting all of the pieces together, you can store all single digits read in array and then segregate the even and odd numbers for printing in a very simple manner (note: neither stdlib.h or math.h are required),

    #include <stdio.h>
    
    #define MAX 1000   /* define a constant rather than use 'magic' numbers in code */
    
    int main (void)
    {
        int array[MAX] = {0},
            c, i, n = 0;
    
        while (n < MAX && (c = getchar()) != EOF)   /* always protect array bounds */
            if ('0' <= c && c <= '9')               /* only handle digits */
                array[n++] = c - '0';               /* convert ASCII to int */
    
        printf ("\narray        : ");               /* output array contents */
        for (i = 0; i < n; i++)
            printf (" %d", array[i]);
    
        printf ("\narray - odd  : ");
        for (i = 0; i < n; i++)
            if (array[i] & 1)                       /* if ones-bit is 1, odd */
                printf (" %d", array[i]);
    
        printf ("\narray - even : ");
        for (i = 0; i < n; i++)
            if ((array[i] & 1) == 0)                /* if ones-bit is 0, even */
                printf (" %d", array[i]);
    
        putchar ('\n');                             /* tidy up w/newline */
    
        return 0;
    }
    

    Example Compilation with Warnings Enabled

    $ gcc -Wall -Wextra -pedantic-std=gnu11 -Ofast -o bin/arrayevenodd arrayevenodd.c
    

    Example Use/Output

    $ echo "01234567890123456789" | ./bin/arrayevenodd
    
    array        :  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
    array - odd  :  1 3 5 7 9 1 3 5 7 9
    array - even :  0 2 4 6 8 0 2 4 6 8
    

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


    Performing Manual Multi-Digit Conversion to int

    If you are needing to convert multi-digit integers from characters, the easy way is to use a function that provides the conversion for you (e.g. the scanf family of functions or by using line oriented with fgets or getline and parsing and converting the strings of digits with strtok and then strtol, or parsing with sscanf, etc.)

    However, performing a manual conversion of individual characters read by getchar() into a multi-digit integers is straight forward. You simply check for a valid character than can begin an integer (including the prefixes of +/-) and sum each digit providing a proper offset for the place of the digit by increasingly multiplying the sum by 10 before adding (or actually subtracting if building a the sum as a negative sum for coding efficiency purposes) each digit until you reach the next non-digit and then you add the final sum to your array and advance the array index.

    While building the sum, it is up to you to check for integer Overflow before adding the final sum to your array. (you can handle the overflow condition however you like, the example below just throws the error and exits)

    The manual conversion probably adds about 20 lines of code, e.g.

    #include <stdio.h>
    #include <stdint.h> /* exact length types */
    #include <limits.h> /* INT_X constants */
    
    #define MAX 1000    /* define constant rather than use 'magic' number in code */
    
    int main (void)
    {
        int array[MAX] = {0},
            c, i, start = 0, sign = 1, n = 0;
        int64_t sum = 0;
    
        while (n < MAX && (c = getchar()) != EOF) { /* always protect array bounds */
            if (!start) {                           /* flag to start building int  */
                start = 1;                          /* flag - working on int */
                if (c == '+')                       /* handle leading '+' sign */
                    continue;
                else if (c == '-')                  /* handle leading '-' sign */
                    sign = -1;
                else if ('0' <= c && c <= '9')      /* handle digits */
                    sum = sum * 10 - (c - '0');     /* note: sum always computed */
                else                                /*       as negative value   */
                    start = 0;                      /* reset - char not handled  */
            }
            else if ('0' <= c && c <= '9') {        /* handle digits */
                sum = sum * 10 - (c - '0');         /* convert ASCII to int */
                if (sum < INT_MIN || (sign != -1 && -sum > INT_MAX))
                    goto err;               /* check for overflow, handle error */
            }
            else {                          /* non-digit ends conversion */
                if (sum)                    /* if sum has value, add to array */
                    array[n++] = sign != -1 ? -sum : sum;
                sign = 1;                   /* reset values for next conversion */
                start = 0;
                sum = 0;
            }
        }
        if (sum)    /* add last element to array on EOF */
            array[n++] = sign != -1 ? -sum : sum;
    
        printf ("\narray        : ");               /* output array contents */
        for (i = 0; i < n; i++)
            printf (" %d", array[i]);
    
        printf ("\narray - odd  : ");
        for (i = 0; i < n; i++)
            if (array[i] & 1)                       /* if ones-bit is 1, odd */
                printf (" %d", array[i]);
    
        printf ("\narray - even : ");
        for (i = 0; i < n; i++)
            if ((array[i] & 1) == 0)                /* if ones-bit is 0, even */
                printf (" %d", array[i]);
    
        putchar ('\n');                             /* tidy up w/newline */
    
        return 0;
    
        err:
    
        fprintf (stderr, "error: overflow detected - array[%d]\n", n);
        return 1;
    }
    

    Example Use/Output

    $ echo "1,2,3,5,76,435" | ./bin/arrayevenodd2
    
    array        :  1 2 3 5 76 435
    array - odd  :  1 3 5 435
    array - even :  2 76
    

    Example with '+/-' Prefixes

    $ echo "1,2,-3,+5,-76,435" | ./bin/arrayevenodd2
    
    array        :  1 2 -3 5 -76 435
    array - odd  :  1 -3 5 435
    array - even :  2 -76
    

    Look over the new example and let me know if you have any more questions.