Thread: getchar and integers

  1. #1
    Registered User
    Join Date
    Oct 2012
    Posts
    99

    getchar and integers

    I was working on a problem where initially I thought I was told to use getchar() to read a list of integers. Then I found out getchar() is just for chars. *duh*. However, I did get the program to work using getchar(), but I have a feeling that it is a bad idea to get an integer with getchar() and then subtract 48 from it to get the value I am looking for (because an integer is stored as a char in ASCII 48 digits away from its actual value). I can think of one reason not to do this, because not all systems are ASCII and therefore subtracting 48 won't always give you your integer back. Here is my code for your comments:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
    
        int ch;
        int even_counter=0;
        int odd_counter=0;
        int av_even=0;
        int av_odd=0;
    
        while((ch=getchar())!='0')
        {
            printf("%i\n",ch);
            ch=ch-48;
            printf("%i\n",ch);
    
            if(ch%2==0)
            {
                even_counter++;
                printf("%i\n", av_even);
                av_even+=ch;
                printf("%i\n", av_even);
            }
    
            else
            {
                odd_counter++;
                printf("%i\n", av_odd);
                av_odd+=ch;
                printf("%i\n", av_odd);
            }
    
        }
    
        printf("There were %i even numbers and %i odd numbers.\n", even_counter, odd_counter);
    
        printf("The average for even numbers is %i and odd numbers is %i.\n", av_even/even_counter, av_odd/odd_counter);
    
    }
    I added extra printf() so I could see what was going on with my ch variable. I think this problem actually was asking for scanf() to be used and not getchar(), but I had already done the above.

    Is there another way to get data like getchar() with integers instead of characters?

    Happy Thanksgiving from a resident Noob.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    Are you just reading numbers?
    Code:
    while ( scanf("%d", &num) == 1 ) {
      if ( num % 2 )
        // etc
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Oct 2012
    Posts
    99
    Yeah. It says "write a program that reads integers until 0 is entered." The previous two problems said "Write a program that reads input until # is entered using getchar()". So I just assumed I was supposed to use getchar() for this problem too.

  4. #4
    Registered User
    Join Date
    Oct 2012
    Posts
    99
    Why is this new code giving me a blank line after I hit enter after entering the first number? And why is it not quitting until I enter zero twice?

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        int num;
        int even_counter=0;
        int odd_counter=0;
        int av_even=0;
        int av_odd=0;
    
        printf("Enter a number:  ");
        scanf("%i\n", &num);
    
        while(num!=0)
        {
                 if(num%2==0)
                 {
                    even_counter++;
                    av_even+=num;
                }
            
        else
               {
                   odd_counter++;
                   av_odd+=num;
               }
    
        printf("Enter another number or 0 to quit:  ");
        scanf("%i\n", &num);
        }
        
    printf("There were %i even numbers and %i odd numbers.\n", even_counter, odd_counter);
        
    printf("The average for even numbers is %i and odd numbers is %i.\n", av_even/even_counter, av_odd/odd_counter);
    }

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    > Yeah. It says "write a program that reads integers until 0 is entered."

    So change
    while ( scanf("%d", &num) == 1 )

    into
    while ( scanf("%d", &num) == 1 && num != 0 )
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  6. #6
    Registered User
    Join Date
    Oct 2012
    Posts
    99
    Quote Originally Posted by Salem View Post
    >
    while ( scanf("%d", &num) == 1 && num != 0 )
    is brilliant - but it's still making me enter 0 twice before it quits?

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,661
    Do you still have the other scanf inside the loop?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Registered User
    Join Date
    May 2012
    Posts
    1,066
    Quote Originally Posted by gratiafide View Post
    Why is this new code giving me a blank line after I hit enter after entering the first number? And why is it not quitting until I enter zero twice?
    Becaus you have asked for it:

    Code:
    scanf("%i\n", &num);
    Read C-FAQ - Question 12.17

    Bye, Andreas

  9. #9
    Registered User
    Join Date
    Oct 2012
    Posts
    99
    Salem - No, I only have one scanf. Now my code is as below. It seems like the while loop skips the first entry somehow and remains one entry behind.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        int num;
        int even_counter=0;
        int odd_counter=0;
        float av_even=0;
        float av_odd=0;
    
    printf("Enter a number:  ");
    
    while((scanf("%i\n", &num)==1) && (num!=0)) {
    printf("Num is %i\n", num);
    if(num==0) break;
    else if(num%2==0) { even_counter++; av_even+=num; }
    else
    {
    odd_counter++;
    av_odd+=num;
    }
    }
    printf("There were %i even numbers and %i odd numbers.\n", even_counter, odd_counter); printf("The average for even numbers is %.4f and odd numbers is %.4f.\n", av_even/even_counter, av_odd/odd_counter); return EXIT_SUCCESS; }

  10. #10
    Registered User
    Join Date
    Oct 2012
    Posts
    99
    Quote Originally Posted by AndiPersti View Post
    Becaus you have asked for it:

    Code:
    scanf("%i\n", &num);
    Read C-FAQ - Question 12.17
    Thanks

  11. #11
    Stoned Witch Barney McGrew's Avatar
    Join Date
    Oct 2012
    Location
    astaylea
    Posts
    420
    Quote Originally Posted by gratiafide View Post
    Then I found out getchar() is just for chars. *duh*.
    All you really need to do is modify your code so that it handles the last digit of a number instead of all of them. I suggest something like the following:

    Code:
    int c, last = -1;
    
    while ((c = getchar()) != EOF)
        if (isdigit(c)) {
            last = c;
        } else if (last >= 0) {
            last -= '0';
    
            /* handle least significant digit */
    
            last = -1;
        }
    This snippet doesn't terminate when 0 is read, but when getchar returns EOF. This is a better design for numerous reasons. (eg. What ifyou want to handle 0 as input?, What if an error occurs on stdin?, etc).

    Quote Originally Posted by gratiafide View Post
    I can think of one reason not to do this, because not all systems are ASCII and therefore subtracting 48 won't always give you your integer back.
    Right. A more portable method is to subtract the value by the character constant '0' instead of hard-coding its ASCII value of 48. This is reasonably safe to do since most character sets order digits contiguously.

  12. #12
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    This is reasonably safe to do since most character sets order digits contiguously.
    In fact, it's completely safe, because the C standard defines digits to be contiguous.

  13. #13
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    You can read digits (c >= '0' && c <= '9') in a loop, and append them to your integer, i = 10*i + (c - '0'):
    Code:
        int i, c;
    
        c = getchar();
    
        i = 0;
        while (c >= '0' && c <= '9') {
            i = 10 * i + (c - '0');
            c = getchar();
        }
    
        /* c already contains the next character, which is a non-digit.
         * i contains the parsed unsigned integer. Overflow is not checked for. */
    The digits 0 to 9 are consecutive in all character sets I know of, even those not compatible with ASCII. As long as your compiler reads the code using the same character set as you write it, the above loop approach should always work.

    If you want to be extra careful, you can use a helper function, perhaps
    Code:
    /* Return positive decimal number corresponding to the digit,
     * or -1 if c is not a decimal digit. */
    int decimal_digit(const int c)
    {
        switch (c) {
            case '0': return 0;
            case '1': return 1;
            case '2': return 2;
            case '3': return 3;
            case '4': return 4;
            case '5': return 5;
            case '6': return 6;
            case '7': return 7;
            case '8': return 8;
            case '9': return 9;
            default:  return -1;
        }
    }
    
    ...
    
        int i, c, d;
    
        c = getchar();
    
        i = 0;
        while ((d = decimal_digit(c)) >= 0) {
            i = 10 * i + d;
            c = getchar();
        }
    
        /* c already contains the next character, which is a non-digit.
         * i contains the parsed unsigned integer. Overflow is not checked for. */
    While I agree that complete code should not be shown, because it is will not usually help the original poster, I'll make an exception here.
    Code:
    #include <stdio.h>
    #include <limits.h>
    
    int main(void)
    {
        int c, integer;
    
        c = getchar();
    
        while (c != EOF) {
            unsigned int  negative = 0U;
            unsigned int  value;
            unsigned int  limit;
    
            /* Skip ASCII whitespace and control characters */
            while (c >= 0 && c <= 32)
                c = getchar();
    
            /* End of input? */
            if (c == EOF)
                break;
    
            /* Sign(s)? */
            while (c == '+' || c == '-') {
                negative ^= (c == '-'); /* Flip bit 0 of negative if c == '-'. */
                c = getchar();
            }
    
            /* Not a digit? */
            if (!(c >= '0' && c <= '9')) {
                fprintf(stderr, "Garbage characters in input.\n");
                return 1;
            }
    
            /* Upper limit for the absolute value.
             * It is either abs(INT_MIN) or abs(INT_MAX). */
            if (negative)
                limit = (unsigned int)(-(double)INT_MIN);
            else
                limit = INT_MAX;
    
            /* First digit. */
            value = c - '0';
            c = getchar();
    
            /* Other digits. */
            while (c >= '0' && c <= '9') {
                const unsigned int  digit = c - '0';
    
                /* Would this overflow? */
                if ((limit - digit + 1U) / 10U < value) {
                    fprintf(stderr, "Integer overflow in input.\n");
                    return 1;
                }
    
                value = 10U * value + digit;
    
                c = getchar();
            }
    
            /* Integer too large (limit + 1)? */
            if (value > limit) {
                fprintf(stderr, "Integer overflow in input.\n");
                return 1;
            }
    
            /* Compute correct integer value. */
            if (negative)
                integer = (int)(-(double)value);
            else
                integer = (int)value;
    
            /*
             * New integer:
            */
            printf("%d\n", integer);
        }
    
        return 0;
    }
    The code above has certain features I'd like you to notice:
    • It skips all ASCII control characters (except DEL, 127) between numbers.
    • It supports only ASCII digits.
      Character sets used in current operating systems are all ASCII-compatible. It does not parse integers specified using non-ASCII digits, or when input is in e.g. EBCDIC.
      If you want support for non-ASCII inputs, make your program locale-aware (by calling setlocale(LC_CTYPE, ""); setlocale(LC_NUMERIC, ""); early in your program), and using *scanf() or strtol() or similar functions.
    • It allows more than one successive sign. For example, --5 is the same as +5 or 5.
    • It uses an unsigned integer to compose the absolute value of the integer.
    • It detects integers that cannot be represented by the int type.
      For this, it uses an unsigned integer to hold the absolute value (per the sign). If the unsigned int type can hold both -INT_MIN+1 and INT_MAX+1, and it can on all current architectures, the detection should work.
    • It uses the double type to avoid the typical pitfall on most architectures, where -INT_MIN == INT_MIN unless promoted to a type with better precision.
      This assumes double can represent all possible int and unsigned int values exactly.
      If your C compiler supports it, I recommend using long long instead.
      If you know your architecture uses two's complement numbers, then you can use (unsigned int)(-INT_MIN) and (int)(-value) instead, as the binary representation of the values is such that the values will be correct even if the compiler thinks there may be an overflow.

    The reason I showed this is that it turns out that in all C libraries, standard I/O is pretty slow in parsing numbers. Using low-level I/O instead of getchar() but otherwise the above parsing scheme you can read massive amounts of decimal integers, usually only limited by I/O speed. (The GNU C library, for example, cannot reach that even on very fast x86-64 machines.)

    It is possible to extend this for reading floating-point values -- which are extremely slow to parse, in relative terms -- but correct rounding becomes a difficult issue. (Most C libraries use arbitrary-precision numbers to parse floating-point data, so they can get the least significant bits correct. The IEEE-754 rules are quite strict.) I have been looking for a fast method to do that, but thus far I have only managed to read at 32-bit precision (IEEE-754 Binary32 type, or float on most architectures) using double-precision termporaries. It's enough for visualisation, but not for scientific work. It is more than an order of magnitude faster than the GNU C library, though, and can reach filesystem read speeds even on typical x86-64 workstations; it's a significant improvement, when you try to visualize millions of atoms specified in a text file.

    For your use case, you should omit the irrelevant parts of the code. To determine which parts are not relevant to you, you'll need to understand the code first -- which to me personally warrants showing the code; you cannot just use it as-is.

    Questions?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 03-26-2012, 10:50 AM
  2. EOF value and getchar
    By Sharifhs in forum C Programming
    Replies: 3
    Last Post: 08-12-2010, 11:13 AM
  3. Using getchar()
    By countchocula in forum C Programming
    Replies: 13
    Last Post: 04-23-2008, 08:07 AM
  4. c getchar help
    By thor4life in forum C Programming
    Replies: 3
    Last Post: 10-19-2007, 01:51 PM
  5. Getchar
    By pcwizzzz in forum C Programming
    Replies: 4
    Last Post: 09-06-2002, 02:09 AM