0

I am getting an array from the input, and when there is no more memory I allocate more (I can't use realloc). my problem is that I do not know what to put as a stop condition for the loop. I want it to stop taking input when I press enter, but it just waits for more.(I am supposed to use scanf)

My code:

#include<stdio.h>
#include<stdlib.h>
#define CHUNK 5
int main()
{
    int *arr, allocate_size=0, i, j, k, temp=0, *p, len=0;
    printf("Enter array: ");
    for(i=0,j=0;temp!='\n';i++){
        if(i>=allocate_size){
            allocate_size+= ((++j)*CHUNK);
            p = (int*)malloc(sizeof(int)*allocate_size);
            if(p==NULL){
                printf("Error");
                return 1;
            }
            for(k=0;k<i && i!=0;k++){ /*copying array*/
                p[k]=arr[k];
            }
            free(arr);
            arr = p;
        }
        if(!scanf(" %d", &temp)){
            printf("Invalid input");
            return 1;
        }
        if(temp!='\n'){
            arr[i] = temp;
            len++;
        }
    }
    printf("length is %d\n", len);
    for(i=0;i<len;i++){
        printf("%d",arr[i]);
    }
    return 0;
} 
  • Have a look at [this answer](https://stackoverflow.com/a/18989659/3316645) on a related question, I think it might help you. – domsson Jan 12 '20 at 07:28
  • Use `fgets` and then `sscanf`, but check if the first character is `'\n'` before passing to `sscanf` for `int` conversion to break the loop on [Enter] alone. There is no need to cast the return of `malloc`, it is unnecessary. See: [Do I cast the result of malloc?](http://stackoverflow.com/q/605845/995714) You can look at the answer by chux in the link provided in the comment above (not the chosen answer) Be aware that `!scanf(" %d", &temp)` will not catch `EOF`, always validate equal to the number of conversions, e.g. `scanf(" %d", &temp) != 1` – David C. Rankin Jan 12 '20 at 07:36
  • `arr` is used while uninitialized – M.M Jan 12 '20 at 07:52

1 Answers1

1

The preferred way to take user input is to read an entire line of input at a time with fgets() or POSIX getline(). The reason for this is you consume an entire line of input with each read and what remains in stdin does not depend on the scanf conversion specifier used. It also eliminates having to remember which conversion specifiers consume leading whitespace and those that do not and eliminates characters left in stdin on a matching failure.

It also provides the convenience of being able to check if the first character in the buffer used to hold the input is '\n' telling you that Enter alone was pressed without any other input.

To incorporate the use of fgets() and sscanf() in your code, simply declare a buffer to hold a line of user input sufficient for whatever the user may enter (or what results if a cat steps on the keyboard) -- don't skimp on buffer size. Then read the input into the buffer with fgets() and check if the first character is '\n' to determine if Enter alone was pressed.

You can do that as follows:

int main()
{
    char buf[256];
    ...
        if (!fgets (buf, sizeof buf, stdin)) {  /* read line of input/validate */
            fputs ("(user canceled input)\n", stderr);
            return 1;
        }
        if (buf[0] == '\n')     /* break loop on [Enter] alone */
            break;
        if (sscanf (buf, "%d", &temp) != 1) {
            fputs ("error: invalid integer input.\n", stderr);
            return 1;
        }
    ...

(note: the check of sscanf (buf, "%d", &temp) != 1 will catch a manually generated EOF if the user presses Ctrl+d (or Ctrl+z on windows) where !sscanf(buf, " %d", &temp) won't)

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


Edit Per-Comment

Since in your question you indicate you have to allocate dynamically for each integer read, but you cannot use realloc to resize your array, the only logical presumption for the dual pointers arr and p is that you are supposed to manually implement realloc by allocating storage each time with p, saving the pointer to the allocated block by assignment from arr = p; at the end of each iteration so you can copy from arr to the new p you allocate on the next iteration and then freeing arr before you assign arr = p; again to prevent memory leaks.

Putting the above solution together with the code you posted, you can do something similar to the following:

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

#define CHUNK  5
#define MAXC 256

int main (void)
{
    char buf[MAXC] = "";        /* buffer to hold each line */
    int *arr = NULL,            /* pointer to block holding final array */
        *p = NULL,              /* temp pointer to allocate for each number */
        allocate_size = 0,      /* tracks length of array */
        temp = 0;               /* temp value for integer conversion */

    printf ("Enter %d elements of array:\n\n  arr[%d]: ", CHUNK, allocate_size);

    while (fgets (buf, MAXC, stdin)) {  /* read user input into buf */
        int rtn;                        /* variable to hold sscanf return */

        if (*buf == '\n')               /* check if ENTER alone */
            break;                      /* break loop if so */

        /* use sscanf to convert to int, saving return */
        if ((rtn = sscanf (buf, "%d", &temp)) == 1) {
            /* allocate new storage for allocate_size + 1 integers */
            if (!(p = malloc ((allocate_size + 1) * sizeof *p))) {
                perror ("malloc-p");
                break;
            }
            for (int i = 0; i < allocate_size; i++) /* copy arr to p */
                p[i] = arr[i];
            p[allocate_size++] = temp;  /* add new element at end */

            free (arr);                 /* free old block of memory */
            arr = p;                    /* assign new block to arr */
        }
        else if (rtn == EOF) {  /* manual EOF generated */
            fputs ("(user canceled input)\n", stderr);
            break;
        }
        else {  /* not an integer value */
            fputs ("error: invalid integer input.\n", stderr);
        }

        if (allocate_size < CHUNK)  /* if arr not full, prompt for next */
            printf ("  arr[%d]: ", allocate_size);
        else
            break;                  /* done */
    }

    /* output results */
    printf ("\nlength is %d\n", allocate_size);
    for (int i = 0; i < allocate_size; i++)
        printf (" %d", arr[i]);
    putchar ('\n');

    free (arr);     /* don't forget to free what you allocate */
}

Example Use/Output

Without stopping or error:

$ ./bin/arrnorealloc
Enter 5 elements of array:

  arr[0]: 10
  arr[1]: 20
  arr[2]: 30
  arr[3]: 40
  arr[4]: 50

length is 5
 10 20 30 40 50

Stopping early:

$ ./bin/arrnorealloc
Enter 5 elements of array:

  arr[0]: 10
  arr[1]: 20
  arr[2]:

length is 2
 10 20

Handling user error:

$ ./bin/arrnorealloc
Enter 5 elements of array:

  arr[0]: 10
  arr[1]: 20
  arr[2]: dogs
error: invalid integer input.
  arr[2]: 30
  arr[3]: fish
error: invalid integer input.
  arr[3]: 40
  arr[4]: 50

length is 5
 10 20 30 40 50

Memory Use/Error Check

Most importantly when allocating memory dynamically, the error check:

$ valgrind ./bin/arrnorealloc
==5555== Memcheck, a memory error detector
==5555== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5555== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==5555== Command: ./bin/arrnorealloc
==5555==
Enter 5 elements of array:

  arr[0]: 10
  arr[1]: 20
  arr[2]: 30
  arr[3]: 40
  arr[4]: 50

length is 5
 10 20 30 40 50
==5555==
==5555== HEAP SUMMARY:
==5555==     in use at exit: 0 bytes in 0 blocks
==5555==   total heap usage: 7 allocs, 7 frees, 2,108 bytes allocated
==5555==
==5555== All heap blocks were freed -- no leaks are possible
==5555==
==5555== For counts of detected and suppressed errors, rerun with: -v
==5555== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • `arr[i]` in first iteration of loop use uninitialized. Is it OK? – EsmaeelE Jan 12 '20 at 12:41
  • NO. before `p[k]=arr[k];`, `arr` must point to a valid and initialized block of memory. How do you expect to assign a value to `p[k]` from an uninitialized `arr[k]`?? What is you `arr` supposed to point to? – David C. Rankin Jan 12 '20 at 15:53
  • because I don't see any `malloc()` for arr pointer. I think this code in original question not yours, is wrong. Could you please clarify it for me? – EsmaeelE Jan 12 '20 at 16:48
  • Sure, give me a minute and I'll trim what you have down to something that works. – David C. Rankin Jan 12 '20 at 17:17