tags:

views:

200

answers:

5

So I've been doing a fairly easy program of converting a string of Characters (assuming numbers are entered) to an Integer.

After I was done, I noticed some very peculiar "bugs" that I can't answer. Mostly because of my limited knowledge on how these 3 functions in Thread title works (I did read a lot of literature though).

So without writing too much text, here's the code of the program:

#include <stdio.h>

#define MAX 100

int CharToInt(const char *);

int main()
{
    char str[MAX];

    printf(" Enter some numbers (no spaces): ");
    gets(str);
//  fgets(str, sizeof(str), stdin);
//  scanf("%s", str);

    printf(" Entered number is: %d\n", CharToInt(str));

    return 0;
}

int CharToInt(const char *s)
{
    int i, result, temp;

    result = 0;
    i = 0;

    while(*(s+i) != '\0')
    {
        temp = *(s+i) & 15;
        result = (temp + result) * 10;
        i++;
    }

    return result / 10;
}

So here's the problem I've been having. First, when using gets() function, the program works perfectly. Second, when using fgets(), the result is slightly wrong because apparently fgets() function reads New Line (ASCII value 10) character last which screws up a result. Third, when using scanf() function, the result is completely wrong because first character apparently has a -52 ASCII value. For this, I have no explanation.

Now I know that gets() is discouraged to use, so I would like to know if I can use fgets() here so it doesn't read (or ignores) New Line character. Also, what's the deal with scanf() function in this program?

Thank you.

+2  A: 

You're correct that you should never use gets. If you want to use fgets, you can simply overwrite the newline.

char *result = fgets(str, sizeof(str), stdin);
if(result != NULL)
{
  str[strlen(str) - 1] = '\0';
}
else
{
  // handle error
}

This does assume there are no embedded NULs. Another option is POSIX getline:

char *line = NULL;
size_t len = 0;
ssize_t count = getline(&line, &len, stdin);
if(count >= 1)
{
  line[count - 1] = '\0';
}
else
{
  // Handle error
}

The advantage to getline is it does allocation and reallocation for you, it handles embedded NUL, and it returns the count so you don't have to waste time with strlen. Note that you can't use an array with getline. The pointer must be NUL or free-able.

I'm not sure what issue you're having with scanf.

Matthew Flaschen
+2  A: 

Yes, you want to avoid gets. fgets will always read the new-line if the buffer was big enough to hold it (which lets you know when the buffer was too small and there's more of the line waiting to be read). If you want something like fgets that won't read the new-line (losing that indication of a too-small buffer) you can use fscanf with a scan-set conversion like: "%N[^\n]", where the 'N' is replaced by the buffer size - 1.

One easy (if strange) way to remove the trailing new-line from a buffer after reading with fgets is: strtok(buffer, "\n"); This isn't how strtok is intended to be used, but I've used it this way more often than in the intended fashion (which I generally avoid).

Jerry Coffin
A: 

Try using fgets() with this modified version of your CharToInt():

int CharToInt(const char *s)
{
    int i, result, temp;

    result = 0;
    i = 0;

    while(*(s+i) != '\0')
    {
        if (isdigit(*(s+i)))
        {
            temp = *(s+i) & 15;
            result = (temp + result) * 10;
        }
        i++;
    }

    return result / 10;
}

It essentially validates the input digits and ignores anything else. This is very crude so modify it and salt to taste.

Amardeep
To clean things up, consider replacing `strchr()` with `isdigit()`. Even better, replace the entire `CharToInt()` function with a call to `atoi()`. http://www.cplusplus.com/reference/clibrary/cctype/isdigit/http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/
sigint
isdigit() is a great suggestion... done.
Amardeep
+1  A: 

never use gets(), it can lead to unprdictable overflows. If your string array is of size 1000 and i enter 1001 characters, i can buffer overflow your program.

Peter Miehle
Thank you all for your answers. They were very much helpful.But I'd also like to know why scanf() doesn't work in this program?Thank you.
Marko
+1  A: 
jamesdlin
As I said in my answer, `getline` is now standard.
Matthew Flaschen
@Matthew Flaschen: Which standard? When I say "non-standard", I mean "non-standard C", not non-POSIX.
jamesdlin