Sunspawn
Sunspawn

Reputation: 837

C getchar input problems

I need to receive a single line of characters and convert them from the base they are written in to a decimal base, as soon as a character is typed.

I did this by using the following function:

int inToDec(int base) //My assumption is that Enter will not be pressed as the first character - if it is, I am not sure what to do in that case
{
    int numdec=0, c;//decimalized number, a variable for character inpu
    while((c = getchar()) != '\n' && c != EOF)
    {
        if(c >= 97)//Checks if the char is an actual char
            c = c - 'a' - 1;
        else//or a digit char
            c = c;
        numdec += numdec * base + c;//every time another char is inputted, it multiplies the numdec by the base and adds the char's value in decimal base - which is essentially the algorithm for conversion to decimal base.
    }
    return numdec;
}

My main():

#include <stdio.h>
#include "funcs.h"

int main()
{
    int option, fbase, tbase, num1, num2;
    do
    {
        scanf("%d",&option);
        switch(option)
        {
        case 1:
            scanf("%d",&fbase);
            num1 = inToDec(fbase);
            num2 = inToDec(fbase);
            outFromDec(num1+num2,fbase);
            break;
        case 2:
            scanf("%d",&fbase);
            num1 = inToDec(fbase);
            num2 = inToDec(fbase);
            if(num1>=num2)
                outFromDec(num1-num2,fbase);
            else
                outFromDec(num2-num1,fbase);
            break;
        case 3:
            scanf("%d",&fbase);
            scanf("%d",&tbase);
            num1 = inToDec(fbase);
            outFromDec(num1,tbase);
            break;
        }
    }while(option != 4);
    return 0;
}

Except that when I input the number(after the option and the desired base) and type Enter to continue to the next one, suddenly it starts to print a character time after time without end. What did I do wrong and how do I fix it? Or, if you do not want to explain some obvious fact to a noob, where do I read about it (preferably without too much heavy duty tech-talk, as I am relatively new to programming).

outFromDec() -

void outFromDec(int num, int base) //converts num into the base given and prints
{
    int count = 0 , temp = num, i;
    char c;
    while(temp != 0)
    {
        temp /= base;
        count++;
    }
    temp = num;
    do
    {
        for(i=1;i<count;i++)
            temp /= base;
        if(temp<=9)
            c = '0' + temp;
        else
            c = 'a' + temp - 1;
        putchar(c);
        count--;
        temp = num / temp;
    }while(temp != 0);
}

Upvotes: 0

Views: 2312

Answers (2)

Jonathan Leffler
Jonathan Leffler

Reputation: 755044

This code has problems:

while(c = getchar() != '\n' && c != EOF)

Not enough parentheses — assignment is a low priority operator. It should be:

while ((c = getchar()) != '\n' && c != EOF)

Another set of problems is in the conversion code in the body of the loop:

    if (c >= 97)
        c = c - 'a' - 1;
    else
        c = c;
    numdec += numdec * base + c;

The choice of 97 is presumably the ASCII or ISO 8859-n or Unicode code point for a. This ignores upper case letters, punctuation, and treats the digits 0 to 9 as though they 48..57. You probably need to use #include <ctype.h>. It also does not validate that the 'digit' is valid for the base.

However, on the first call to intToDec(), the first character read is a newline, left over by scanf(), so the first number is always zero, if you enter the numbers one per line as you're told to.

When you finally get to outToDec(), you have some interesting numerology. I added printf() statements to track function entry and exit, and also for key points in the loop:

void outFromDec(int num, int base)
{
    printf("-->> %s: %d %d\n", __func__, num, base);
    int count = 0, temp = num, i;
    char c;
    while (temp != 0)
    {
        temp /= base;
        count++;
    }
    printf("Count: %d\n", count);
    temp = num;
    do
    {
        printf("count: %d; temp = %d\n", count, temp);
        for (i = 1; i < count; i++)
            temp /= base;
        if (temp <= 9)
            c = '0' + temp;
        else
            c = 'a' + temp - 1;
        putchar(c);
        count--;
        temp = num / temp;
    } while (temp != 0);
    printf("<<-- %s\n", __func__);
}

The __func__ is a predefined identifier in C99 containing the function name. It may not be available to you in MSVC; if not, replace it with the function name.

For the inputs 1, 9, 9, the output from the program was:

-->> inToDec: 9
<<-- inToDec: 0
-->> inToDec: 9
<<-- inToDec: 57
-->> outFromDec: 57 9
Count: 2
count: 2; temp = 57
6count: 1; temp = 9
9count: 0; temp = 6
6count: -1; temp = 9
9count: -2; temp = 6

And the count continued to decrease and the 6's and 9's continued to alternate. You seem to be trying to isolate the digits with the most significant digit (MSD) discovered first. The loop determining count is correct; the loop printing the digits clearly isn't. You should be able to take it from there; it is routine debugging. Note how I used the print statements to see what was going on. If you can't resolve it by just looking at the code, print out the result of every expression, if necessary.

I observe that to print 57 in base 10, you'd discover that there are 2 digits to print (count == 2). The first digit would be found by dividing by the base (10) count-1 times; this would give you 5 to print. You probably need to subtract 5 * 10 from the number so that the next (in this case, the last) time around the loop, you'd start with just 7, which you'd print. The loop would stop. You should ensure the loop breaks if count ever goes negative.

This is an expensive way of formatting a 10-digit 32-bit number (or even more so a 19-digit 64-bit number). It can be made to work, though. The standard procedure collects the digits in reverse order and arranges to print them out in the inverse order. (number % base gives you the digit to be printed; number /= base reduces the number of digits left to process.)


As happens quite often, the OP is under artificial constraints and may not use strings. Yuck!

Here is a 'read an integer' function that is plausibly robust. It does assume 2's complement arithmetic; it contains an assertion that should fire if it was ever run on a machine that was sign-magnitude or 1's complement (but I haven't tested it; I don't have any such machines available to test on).

Note that the code accumulates the number as a negative number, and makes it positive at the end if it should be positive. This makes it easier to deal with INT_MIN than trying to accumulate it as a positive int.

For the purposes of the exercise, I'm treating the system as though sizeof(intmax_t) == sizeof(int) (and hence sizeof(int) == sizeof(long) and sizeof(int) == sizeof(long long) too); this technique would work if the integer type were intmax_t instead of int. Note that the C standard does not rule out this supposed configuration (but the standard requires that CHAR_BIT * sizeof(int) >= 64 to be a conforming implementation).

#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>

/* Read an integer from stdin without prompt: code is not allowed to use strings! */
enum { E_OK = 0, E_EOF = -1, E_INVCHAR = -2, E_OVERFLOW = -3 };

extern int read_an_int(int *value);

int read_an_int(int *value)
{
    int number = 0;
    int c;
    int pos_neg = +1;

    assert(-INT_MAX != INT_MIN);    // Probably 2's complement

    while ((c = getchar()) != EOF && isspace(c))
        ;
    if (c == '-')
    {
        pos_neg = -1;
        c = getchar();
    }
    else if (c == '+')
    {
        pos_neg = +1;
        c = getchar();
    }
    if (c == EOF)
        return E_EOF;
    if (!isdigit(c))
        return E_INVCHAR;

    number = '0' - c;   /* Negated digit */

    while ((c = getchar()) != EOF && isdigit(c))
    {
        int d = '0' - c; /* Negated digit */
        if (number < INT_MIN / 10 || (number == INT_MIN/10 && d < INT_MIN % 10))
            return E_OVERFLOW;
        //printf("N1 %d; d %d; ", number, d);
        number = number * 10 + d;
        //printf("N2 %d\n", number);
    }
    if (c != EOF)
        ungetc(c, stdin);

    if (pos_neg != -1)
    {
        //printf("Should be switched (%d)(%d)\n", pos_neg, number);
        if (number == INT_MIN)
            return E_OVERFLOW;
        number = -number;
        //printf("Should be positive (%d)(%d)\n", pos_neg, number);
    }

    *value = number;
    return E_OK;
}

static void gobble_input(void)
{
    int c;
    while ((c = getchar()) != EOF)
    {
        if (isdigit(c) || c == '+' || c == '-')
        {
            ungetc(c, stdin);
            break;
        }
        printf("Skip %c\n", c);
    }
}

int main(void)
{
    int rc;
    int number;

    while ((rc = read_an_int(&number)) != E_EOF)
    {
        switch (rc)
        {
        case E_INVCHAR:
            printf("Invalid character spotted\n");
            gobble_input();
            break;
        case E_OVERFLOW:
            printf("Input would have overflowed integer range %d..%d\n", INT_MIN, INT_MAX);
            break;
        case E_OK:
            printf("Input number: %d\n", number);
            break;
        default:
            assert(0);
            break;
        }
    }

    return 0;
}

The test data file I used was:

0
1
2
3
4
5
6
7
8
9
11
+123
1234
56789
+123456789
2147483647
2147483648
+000000123456789
000000123456789
-0000
+0000
-1
-2
-9
-21
-321
-4321
-2147483647
-2147483648
-2147483649
# Bogus data or partially bogus data
-
+
-213a
+213a
+.213
3.14159E+23

The output from that was:

Input number: 0
Input number: 1
Input number: 2
Input number: 3
Input number: 4
Input number: 5
Input number: 6
Input number: 7
Input number: 8
Input number: 9
Input number: 11
Input number: 123
Input number: 1234
Input number: 56789
Input number: 123456789
Input number: 2147483647
Input would have overflowed integer range -2147483648..2147483647
Input number: 123456789
Input number: 123456789
Input number: 0
Input number: 0
Input number: -1
Input number: -2
Input number: -9
Input number: -21
Input number: -321
Input number: -4321
Input number: -2147483647
Input number: -2147483648
Input would have overflowed integer range -2147483648..2147483647
Invalid character spotted
Skip  
Skip B
Skip o
Skip g
Skip u
Skip s
Skip  
Skip d
Skip a
Skip t
Skip a
Skip  
Skip o
Skip r
Skip  
Skip p
Skip a
Skip r
Skip t
Skip i
Skip a
Skip l
Skip l
Skip y
Skip  
Skip b
Skip o
Skip g
Skip u
Skip s
Skip  
Skip d
Skip a
Skip t
Skip a
Skip 

Invalid character spotted
Invalid character spotted
Input number: -213
Invalid character spotted
Skip 

Input number: 213
Invalid character spotted
Skip 

Invalid character spotted
Input number: 213
Input number: 3
Invalid character spotted
Input number: 14159
Invalid character spotted
Input number: 23

Note that the last line provided 3 valid numbers (and two invalid characters, the . and the E).

It isn't particularly easy; that's why these things are coded in library functions. Also, the 'no strings' requirement means I can't do decent error reporting when there are invalid characters or overflows.

Upvotes: 3

mcleod_ideafix
mcleod_ideafix

Reputation: 11448

If you are working in Windows, add a fflush (stdin) just before the loop in which the getchar() is. That is:

int inToDec(int base) //My assumption is that Enter will not be pressed as the first character - if it is, I am not sure what to do in that case
{
    int numdec=0, c;//decimalized number, a variable for character inpu
    fflush (stdin);  /* flushes input buffer */
    while(c = getchar() != '\n' && c != EOF)
    {
        if(c >= 97)//Checks if the char is an actual char
            c = c - 'a' - 1;
        else//or a digit char
            c = c;
        numdec += numdec * base + c;//every time another char is inputted, it multiplies the numdec by the base and adds the char's value in decimal base - which is essentially the algorithm for conversion to decimal base.
    }
    return numdec;
}

Upvotes: 0

Related Questions