0

I have this c program where I am inputing a number N followed by N more numbers. For example, I'll enter 100 followed by 100 more numbers. For some reason, after so many inputs the scanf function will stop working properly. It's as if it has stopped taking input and will just continue one with whatever value is in size.

The use case I came up with is 100 1 2 3 4 5 6 7 8 9 10... (repeated ten times). then after three or four times of that I'll type in 100 10 9 8 7 6 5 4 3 2 1... (repeated ten times) and then there will be an infinite loop of print statements.

int main(int argc, const char * argv[]) {
  int histogram[10000];
  int i;
  while (1) {
    int *rectPtr = histogram;
    int size;
    scanf("%d", &size);
    if (!size) return 0;
    for (i = 0; i < size; ++i) {
        scanf("%d", rectPtr);
        rectPtr++;
    }
    printf("%d", 1);
    printf("\n");
  }
  return 0;
}
j will
  • 3,747
  • 11
  • 41
  • 64
  • see the trick here. `if (!size) return 0;`. did you try putting `0` once after entering the loop elements when it's asking for number of elements again? mind it, it's in `while(1)` loop. – Sourav Ghosh Dec 18 '14 at 06:24
  • 1
    You're not testing the return value from `scanf()`, so you don't know whether it is working. The pair of `printf()` statements is odd; why not write `printf("%d\n", 1);` or even `puts("1");`? – Jonathan Leffler Dec 18 '14 at 06:25
  • @JonathanLeffler What do you mean? Of course I know it isn't working because I have debugged it. The double printf was unnecessary. – j will Dec 18 '14 at 06:26
  • 1
    To clarify, here, you're checking the _input_ value _suppossed_ to be scanned by `scanf()` but you missed to check the `return` value of `scanf()` itself. – Sourav Ghosh Dec 18 '14 at 06:31
  • 1
    Your code does not test or capture the return value from `scanf()`, so you do not know whether `scanf()` is reporting a problem. As a general rule, test the return value of input functions to make sure what you thought happened did in fact happen. You could also print out the values read just after you read them: `if (scanf("%d", rectPtr) != 1) { fprintf(stderr, "scanf() failed\n"); return 1; } printf("--> %d\n", *rectPtr); rectPtr++;`. Similarly when inputting `size`. Also consider `if (size <= 0) return 0;`. And using `fgets()` plus `sscanf()` can make reporting errors easier. – Jonathan Leffler Dec 18 '14 at 06:33
  • Ok, thanks for the information, and it is great to know if scanf fails, but I want to know why it fails and prevent it from failing. How do I do that? – j will Dec 18 '14 at 06:35
  • The while loop which you are checking `while(1)` will always be true so the loop will always be executed, and there will be no end of the loop. – sharon Dec 18 '14 at 06:35
  • I understand you'd like to know. With `scanf()`, the best you can do after a failure is usually to read all the characters that follow up to a newline or EOF, and if you want to know what went wrong, then you print those characters too. `printf("Error at: <<"); int c; while ((c = getchar()) != EOF && c != '\n') putchar(c); puts(">>");` since `scanf()` leaves the last character that it read in the input buffer ready for the next input operation. The first character in the string is what caused the failure. You can package that as a function (`void gobble(void)`, for example). – Jonathan Leffler Dec 18 '14 at 06:39
  • See also [How to use `sscanf()` in loops?](http://stackoverflow.com/questions/3975236/how-to-use-sscanf-in-loops/3975254#3975254) – Jonathan Leffler Dec 18 '14 at 06:39
  • @JonathanLeffler So this is what I got, in case you were wondering: `Error at: << 10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1>>` – j will Dec 18 '14 at 06:51
  • The Unicode symbols there are curious; something went wrong at that point in the input, yielding something that is definitely not a digit, and therefore causing `scanf()` to fail to convert the bytes into digits. It is far from clear where the erroneous data came from, but that's why things go haywire. If I'm reading the codes correctly, the values are U+F702 and U+F703, which are in the BMP Private Use Area (see [Unicode Code Charts](http://unicode.org/charts)). There is no standard meaning for those Unicode code points; only whatever is agreed between the parties exchanging information. – Jonathan Leffler Dec 18 '14 at 07:06

3 Answers3

1

IMO, you need to check the return value of scanf() for proper operation. Please check the below code. I have added some modifications.

To exit from the program, you need to press CTRL+ D which will generate the EOF. Alternatively, upon entering some invalid input [like a char instead of int] wiil also cause the program to beak out of while() llop and terminate.

I have put the sequence to check first scanf(). All others need to be checked, too.

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

int main(int argc, const char * argv[]) {
        int histogram[10000] = {0};
        int i;

        int *rectPtr = histogram;
        int size = 0;
        int retval = 0;


        printf("Enter the number of elements \n");
        while (  (retval = scanf("%d", &size)) != EOF && (retval == 1)) {
                rectPtr = histogram;
                if (!size) return 0;
                printf("Enter %d elements\n", size);
                for (i = 0; i < size; ++i) {
                        scanf("%d", rectPtr);   //check in a simmilar way to above
                        rectPtr++;
                }
                printf("%d\n", 1111111);

                printf("Enter the number of elements: \n");
        }
        return 0;
}

The output of a sample run

[sourav@broadsword temp]$ ./a.out 
Enter the number of elements: 2
Enter 2 elements
1
2
1111111
Enter the number of elements: 3
Enter 3 elements
1
2
3
1111111
Enter the number of elements: 9
Enter 9 elements
0
9
8
7
6
5
4
3
2
1111111
Enter the number of elements: r   
[sourav@broadsword temp]$
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • while(1) is still a infinite loop you got to get rid of it. There has to be a purpose to have them in code and break out it gently that is something which I don't see here – Gopi Dec 18 '14 at 06:44
  • @Gopi Sorry, but did you miss `if (retval == EOF) return 0;` statement? – Sourav Ghosh Dec 18 '14 at 06:45
  • Enter an `a` (or any other letter) and watch the code loop. – Jonathan Leffler Dec 18 '14 at 06:45
  • @JonathanLeffler ahh. I see. Will update the code to fix that part. – Sourav Ghosh Dec 18 '14 at 06:47
  • The check for retval is within your if condition get rid of it – Gopi Dec 18 '14 at 06:48
  • What about the return value from the *second* `scanf()`? – Weather Vane Dec 18 '14 at 06:48
  • @WeatherVane: It's not ideal, but it is in a bounded loop that will stop. Ideally, the code should test both `scanf()` calls. – Jonathan Leffler Dec 18 '14 at 06:50
  • What about checking there is enough room in `histogram[]` or is its size arbitrarily large so as not to bother? – Weather Vane Dec 18 '14 at 06:51
  • @WeatherVane the second [and others, if any] `scanf()` should also be checked. Right. – Sourav Ghosh Dec 18 '14 at 06:55
  • 1
    You have indeed. You could simplify `while ( (retval = scanf("%d", &size)) != EOF && (retval == 1)) {` so that it reads `while ((retval = scanf("%d", &size)) == 1) {`, which is effectively the idiomatic test that `scanf()` converted all the values you expected it to convert (1 here, but you might have been asking for 2, 3, or more values, and the test for N values is `== N`). If `retval` is EOF or 0, both loop conditions terminate the loop, but there's an extra test in your version for the normal case (everything worked). – Jonathan Leffler Dec 18 '14 at 07:42
  • @JonathanLeffler Thank you sir. Initially I started off with the idea to terminate the loop with and in this logic, it's redundant. Thank you very much for taking your time to review and suggest the changes. :-) – Sourav Ghosh Dec 18 '14 at 07:46
1

Distrust infinite loops.

In a series of comments, I said:

You're not testing the return value from scanf(), so you don't know whether it is working. The pair of printf() statements is odd; why not write printf("%d\n", 1); or even puts("1");?

Your code does not test or capture the return value from scanf(), so you do not know whether scanf() is reporting a problem. As a general rule, test the return value of input functions to make sure what you thought happened did in fact happen. You could also print out the values read just after you read them:

if (scanf("%d", rectPtr) != 1)
{
    fprintf(stderr, "scanf() failed\n");
    return 1;
}
printf("--> %d\n", *rectPtr);
rectPtr++;

Similarly when inputting size. Also consider if (size <= 0) return 0;. And using fgets() plus `sscanf() can make reporting errors easier.

j.will commented:

It is great to know if scanf fails, but I want to know why it fails and prevent it from failing. How do I do that?

I responded:

I understand you'd like to know. With scanf(), the best you can do after a failure is usually to read all the characters that follow up to a newline or EOF, and if you want to know what went wrong, then you print those characters too, because scanf() leaves the last character that it read in the input buffer ready for the next input operation.

void gobble(void)
{
    printf("Error at: <<");
    int c;
    while ((c = getchar()) != EOF && c != '\n')
        putchar(c);
    puts(">>");
    if (c == EOF)
        puts("<<EOF>>");
}

The first character in the output is what caused the failure.

See also How to use sscanf() in loops?


Hacking your code to match this:

#include <stdio.h>

static void gobble(void)
{
    printf("Error at: <<");
    int c;
    while ((c = getchar()) != EOF && c != '\n')
        putchar(c);
    puts(">>");
    if (c == EOF)
        puts("<<EOF>>");
}

int main(void)
{
    enum { MAX_VALUES = 10000 };
    int histogram[MAX_VALUES];
    int size;
    while (printf("Number of items: ") > 0 && scanf("%d", &size) == 1 &&
           size > 0 && size <= MAX_VALUES)
    {
        int *rectPtr = histogram;
        for (int i = 0; i < size; ++i)
        {
            if (scanf("%d", rectPtr) != 1)
            {
                gobble();
                return 1;
            }
            rectPtr++;
        }
        printf("size %d items read\n", size);
    }
    return 0;
}
Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I've lost my faith in C. How could it fail for something so simple? – j will Dec 18 '14 at 07:36
  • 2
    It isn't C per se — or probably isn't. I don't know how you're entering the data, but I suspect you're using copy'n'paste in a terminal window on a windowing system, and I'd tentatively put the blame on the copy'n'paste mechanism blowing up rather than the C code in your program having gone haywire. You could try putting the data into a file and then using `./your-program < data-file` to run it, and I would be astonished if that blows up, assuming that the data file is itself clean. – Jonathan Leffler Dec 18 '14 at 07:37
0

histogram is declared to have size 10000. You say you do 100 1 2 3 ... repeated 10 times. If I correctly understand that uses 1000 slots in histogram.

If you repeat the test more than 10 times, you exhaust histogram and begin to write past the end of array causing undefined behaviour.

So you must either :

  • reset recPtr = histogram at each iteration
  • control recPtr - histogram + size <= sizeof(histogram) after reading size (IMHO better)

And as other said, you should always control input operations : anything can happen outside of your program ...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252