0

I've implemented a dynamic array data structure in C; and i'm now looking for a proper way to fill up my arrays from stdin. Using scanf() or fgets() seems not to be a good idea, since their buffer size is fixed at compilation time i would lose the dynamicity of my structure. So i'm using getline() and allocating the memory dynamically for every char i want to put in my array.

I wrote this function to fill up two arrays from stdin:

//input arrays from keyboard
void use_input_array(){
 char *line = NULL;
 size_t len = 0;
 ssize_t read;
 puts("Enter your first ARRAY : ");

 struct dynarray *first;
 create_dynarray(&first, 1);

 while((read = getline(&line, &len, stdin)) != -1){
   if(read > 0){
    add_elem(first, line);
   }
 }
 free(line);

 char *sline = NULL;
 size_t slen = 0;
 ssize_t sread;
 puts("Enter your second ARRAY :");

 struct dynarray *second;
 create_dynarray(&second, 1);

 while((sread = getline(&sline, &slen, stdin)) != -1){
   if(sread > 0){
     add_elem(second, sline);

   }
 free(sline);    
 }
 puts("END");
}

When i execute the function i'm able to insert the first string with no problem, but when it comes to second the execution goes directly to the end. I have no idea why this is happening. Here's a small compilable example to better show my problem:

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

typedef struct dynarray
{
   void **memory;
   size_t allocated;
   size_t used;
   int index;
} dynarray;


void create_dynarray(dynarray **array, size_t size)
{
  *array = calloc(size, sizeof **array);
  (*array)->memory = NULL;
  (*array)->allocated = 0;
  (*array)->used = 0;
  (*array)->index = -1;
}



//adds a new element at the bottom of dynarray
void add_elem(dynarray *array, void *data)
{
  size_t toallocate;
  size_t size = sizeof(void *);
  if ((array->allocated - array->used) < size){
    toallocate = array->allocated == 0 ? size : (array->allocated * 2);
    array->memory = realloc(array->memory, toallocate);
    array->allocated = toallocate;
  }

  array->memory[++array->index] = data;
  array->used = array->used + size;
}

//input arrays from keyboard
void use_input_array(){
  char *line = NULL;
  size_t len = 0;
  ssize_t read;
  puts("Enter your first ARRAY : ");

  struct dynarray *first;
  create_dynarray(&first, 1);

  while((read = getline(&line, &len, stdin)) != -1){
    if(read > 0){
     add_elem(first, line);
    }
  }
  free(line);

  char *sline = NULL;
  size_t slen = 0;
  ssize_t sread;
  puts("Enter your second ARRAY :");

  struct dynarray *second;
  create_dynarray(&second, 1);

  while((sread = getline(&sline, &slen, stdin)) != -1){
    if(sread > 0){
      add_elem(second, sline);

    }
  free(sline);    
  }
  puts("END");
}

int main(){

  use_input_array();
}


Another problem is related to the fact that my input string is an array of char but my structure is built to be typeless so it expects a void pointer. I could create an array of int ,double etc. to perform the conversion but this would also mean to have a fixed size.I was thinking about implementing my own coversion function and calling it before inserting the element in the array. But i really don't know where to start... Any suggestions? Thanks.

terdop
  • 49
  • 6
  • correct, but even passing the right parameter is not solving the problem – terdop Feb 01 '20 at 22:03
  • My idea was not to the free the array, but to free the ```line``` i use as buffer – terdop Feb 01 '20 at 22:08
  • By using ctrl-d – terdop Feb 01 '20 at 22:16
  • Well using ```clearerr()``` worked fine thx – terdop Feb 01 '20 at 22:29
  • regarding: `array->memory = realloc(array->memory, toallocate);` When calling `realloc()`, always assign the returned value to a temp pointer. then check (!=NULL) the temp pointer. If NULL, then `realloc()` failed, so handle error. Otherwise, assign the temp pointer to the `array->memory`. Because if `realloc()` fails and the returned value is assigned directly to the `array->memory` then the pointer to allocated memory is lost, resulting in a unrecoverable memory leak – user3629249 Feb 01 '20 at 23:59
  • Also, `*array = calloc(size, sizeof **array);` allocates a single pointer, as you add lines you must `realloc` the number of pointers before allocating storage for the new line. Your test `if(read > 0)` would test *true* for empty-lines because `getline` includes the `'\n'` in the `line` filled. A better test would be `if (*line != '\n')` – David C. Rankin Feb 02 '20 at 00:03
  • regarding: `*array = calloc(size, sizeof **array);` this is not doing what you want. Suggest: `*array = calloc( 1, sizeof( dynarray ) ); if( ! *array ) { perror( "calloc failed" ); exit( EXIT_FAILURE ); }` – user3629249 Feb 02 '20 at 00:04
  • OT: regarding: `ssize_t read;` `read()` is a well known C library function. Naming variables the same as a C library function is a very poor programming practice. Suggest: `ssize_t bytesRead;` And note: it is okay to use the same variable instance, later in the same function – user3629249 Feb 02 '20 at 00:08

1 Answers1

2

Something like this could work.
Only one pointer is needed for getline. Since the pointer is assigned array->memory[++array->index] = data; more memory needs to be allocated for each iteration. Set line to NULL and len to zero.
Consider breaking out of the loop when an empty line is entered. '\n' == line[0].
Free line at the very end.

char *line = NULL;
size_t len = 0;
ssize_t read;
puts("Enter your first ARRAY : ");

struct dynarray *first;
create_dynarray(&first, 1);

while((read = getline(&line, &len, stdin)) != -1){
    if ( '\n' == line[0]) {
        break;
    }
    if(read > 0){
        add_elem(first, line);
        line = NULL;
        len = 0;
    }
}

puts("Enter your second ARRAY :");

struct dynarray *second;
create_dynarray(&second, 1);

while((read = getline(&line, &len, stdin)) != -1){
    if ( '\n' == line[0]) {
        break;
    }
    if(read > 0){
        add_elem(second, line);
        line = NULL;
        len = 0;

    }
}
free(line);
user3121023
  • 8,181
  • 5
  • 18
  • 16