1

i wrote a combination output code for an input array. i wrote an input_array function for an new array. i wrote an input_decimal_number function for an size_t type single-number. i set N as the number of elements in a combination. And i pass compilation. Here is code followed:

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

size_t input_decimal_number(void);
size_t input_array(int *array);
void combination(const int *array, int *combination_array,
                 size_t start, const size_t end, const size_t N, size_t i);

int main(void)
{
     size_t N, LEN;
     int *array;
     array = (int *)malloc(sizeof(int));
     LEN = input_array(array);

     printf("enter N value(0 < N <= %zd):\n", LEN); /* N is number of elements in a combination */
     while ((N = input_decimal_number()) > LEN)
          printf("N > %zd, enter N again: ", LEN);
     int combination_array[N];
     puts("Here are all combinations for this integer array:");
     combination(array, combination_array, 0, LEN - 1, N, 0);
     return 0;
}

size_t input_decimal_number(void)
{ /* here is a common size_t type single-number input functions */
     size_t decimal_number;
     _Bool input_check;
     while ((input_check = fscanf(stdin, "%zd", &decimal_number)) != 1)
          if (input_check != 1)
          {
               scanf("%*s");
               fprintf(stdout, "invalid input, enter this number again: ");
          }
     return decimal_number;
}

size_t input_array(int *array)
{ /* this is input array functions */
     size_t LEN = 0;
     char buf[BUFSIZ];
     void *alloc_check;
     fprintf(stdout, "Enter decimal integer arrays(use spaces key to separate every number):\n");
     while (fscanf(stdin, "%d", &array[LEN]) == 1)
     {
          alloc_check = realloc(array, (LEN + 1) * sizeof(int));
          if (alloc_check == NULL)
               ;
          else
               array = (int *)alloc_check;
          /* dynamically allocate memory for array */

          LEN++;
          if (getchar() == '\n')
               break;
     }
     if (LEN == 0)
     {
          printf("no number entered correctly.\n");
          exit(EXIT_FAILURE);
     } /*if no input, exit the whole program */
     setbuf(stdin, NULL);
     setvbuf(stdin, buf, _IOLBF, BUFSIZ);
     /* skip rest of input content */
     return LEN;
}

void combination(const int *array, int *combination_array,
                 size_t start, const size_t end, const size_t N, size_t i)
{
     /* this functions could find all combination of N elements in an array*/
     if (i == N)
     {
          for (int k = 0; k < N; k++)
               printf("%d ", combination_array[k]);
          putchar('\n');
          return;
     }
     for (start; start <= end && end - start + 1 >= N - i; start++)
     /* "end-start+1 >= N-i" makes sure that including i at N will make a combination with remaining elements at remaining positions */
     {
          combination_array[i] = array[start];
          combination(array, combination_array, start + 1, end, N, i + 1);
     }
}

when i input such like 1 2 3 4 5 6, and input N again it turned to be ok! But if i input 1 2 3 4 5 6 7, it turned to be realloc(): invalid next size Aborted (core dumped), why?

anastaciu
  • 23,467
  • 7
  • 28
  • 53
brushmonk
  • 109
  • 7

2 Answers2

1

You have a corrupted heap, which leads to undefined behavior, think about what happens in the second iteration of while (fscanf(stdin, "%d", &array[LEN]) == 1), when it's called to grab the second input, the allocated memory still only has space for one int but you assign the value to array[1] which means the array should have at least space for 2 ints, but the second is not allocated yet.

Quick fix, use (LEN + 2).

To pass the allocated space to caller function, in this case the main function:

For the latter:

int *input_array(size_t *LEN) {//LEN will be passed as an argument pointer

    int *array;
    if ((array = malloc(sizeof *array)) == NULL) { //same as sizeof(int) but safer
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    fprintf(stdout, "Enter decimal integer arrays(space to separate every number):\n");
    while (fscanf(stdin, "%d", &array[*LEN]) == 1) {
        //no need for auxiliary pointer
        if ((array = realloc(array, (*LEN + 2) * sizeof *array)) == NULL) {
            perror("malloc");
            exit(EXIT_FAILURE);
        }
        (*LEN)++;
        if (getchar() == '\n')
            break;
    }
    if (LEN == 0) {
        fprintf(stderr, "no number entered correctly.\n");
        exit(EXIT_FAILURE);
    }
    return array; //return the allocated space
}

And in main:

//...
size_t N, LEN = 0;
int *array;
array = input_array(&LEN); //allocated space assigned to array and LEN as an argument pointer
//...

Live demo

There are still potencial issues in the combination function, as N grows the number of combinations shrinks to the point that if N == LEN, the only printed combination is the array itself, if that is not the expected output you should also address that.

size_t format specifier is %zu.

Use size_t i = 0 in the for loop for signedness comparation consistency.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • Why `LEN+1` is not enough for `realloc`? – brushmonk Jun 08 '20 at 08:37
  • @brushmonk, on the second iteration of scanf the allocated space is still for only one int, the heap is corrupted and it leads to undefined behavior. I added this to my answer. – anastaciu Jun 08 '20 at 09:48
  • i find another problem in `input_decimal_number`, if i input array like `0 243 -34667 -2764 -4 0`, array will get wrong. it may become `0 0 -34667 -2764 -4 0`. could u hep me? – brushmonk Jun 09 '20 at 09:29
  • @brushmonk, yes, for the allocated space to be passed to main you would need a double pointer, or to return the allocated space, and pass LEN by pointer, for the latter: https://repl.it/@anastaciu/GlitteringJoyousAngle . Another problem is in the combination function, as N grows the number of combinations will get smaller to the point that if N == LEN it only prints the original array, if that is not what you need, you'll have to refactor that. If you have trouble doing it ask a new question, the comment section is not really for answering. – anastaciu Jun 09 '20 at 14:20
  • @brushmonk, I added the previous comment to my answer, the problem with comments, besides the limited space is that they might be deleted after some time. – anastaciu Jun 09 '20 at 14:34
  • i appreciate your patience to my annoying questions. but i am still confused that why my current `input_array` function can't receive input for `array` correctly? is this because of error at where heap allocation to `input_array` function stack? – brushmonk Jun 10 '20 at 03:29
  • @brushmonk, the heap problem is addressed by giving it enough space, the question is to allocate memory to a pointer passed by argument, counterintuitively the pointer itself is passed by value, so in order to change it you will need to pass a pointer to pointer aka a double pointer, [check this answer](https://stackoverflow.com/a/1398321/6865932), it explains it quite clearly. I, for one, prefer to return it, but you can use the double pointer strategy. – anastaciu Jun 10 '20 at 10:01
  • @brushmonk, also, don't forget to [accept answers to your questions](https://meta.stackexchange.com/a/5235/684852) if they address them correctly, it's an important feature that should be used. – anastaciu Jun 10 '20 at 10:26
  • thank u. i think i've understand. i just think i am too superficial to understand the mechanism to pass argument to function. i've marked your answers as acceptance. – brushmonk Jun 11 '20 at 03:16
  • @brushmonk, I'm glad you got it, normally when we pass a pointer, the value is changed in the caller function, but in this case since what we want to change is the pointer itself, we need to pass a pointer to that pointer we want to change, it's mind twisting. So returning the pointer to the allocated space, like in my sample code, is simpler and produces cleaner code. If you have any more doubts, just ask. – anastaciu Jun 11 '20 at 09:01
1

Your input_array() function has another issue: you are setting the buffer for stdin to a local array that ceases to exist when the function returns:

size_t input_array(int *array)
{ /* this is input array functions */
     size_t LEN = 0;
     char buf[BUFSIZ];   <=====  This array ceases to exist when this function returns

     ...

     setbuf(stdin, NULL);
     setvbuf(stdin, buf, _IOLBF, BUFSIZ);  <==== stdin's buffer will disappear
     /* skip rest of input content */
     return LEN;
}

Since you're only calling input_array() once, the easiest fix is to make buf static so it exists as long as the program is running:

     static char buf[BUFSIZ];

Without static each time input_array() is called, each call of the function will have its own buf array. If two threads are running at the same time, there will be two buf arrays in your program. But those arrays will cease to exist when the function returns.

With the addition of the static keyword, there will only be one buf array, it will exist as long as the program is running, and it will be shared by all invocations of input_array().

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • All you say is correct and UV, but `buf` serves no purpose, the OP could just remove it. – anastaciu Jun 09 '20 at 14:59
  • @anastaciu We don't know why that code was added - it seems like it might be useful in flushing `stdin`'s input buffer. – Andrew Henle Jun 09 '20 at 15:09
  • Yes, that may be. – anastaciu Jun 09 '20 at 15:16
  • the purpose i add `setbuf(stdin, NULL)` `setvbuf(stdin, buf, _IOLBF, BUFSIZ)` is eliminate remaining data in input buffer. as you said the remaining data in input buffer would not exist after function return, does this mean i don't need add `setbuf(stdin, NULL)` `setvbuf(stdin, buf, _IOLBF, BUFSIZ)` ? – brushmonk Jun 10 '20 at 06:09