0

I have created a structure which includes an array of strings and have populated that with words. When I try and fill the array more than half full I want to create a larger structure, copy the current data to that larger structure and then have that larger structure 'replace' the old one that is called from main. Although I have successfully created and copied the data to the new structure; which I can prove by printing the data out from within the function; I am not able to replace the old structure in main. The next book_insert I try inserts to the old, smaller structure not the new, larger one.

I am operating within a constraint whereby I cannot do the resizing / copying / replacing within main; it has to be called from the book_insert function called from main. Additionally I cannot edit void book_insert(dic* s, char* v) (i.e. add double pointers), it has to remain in this format.

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

struct book {
   int size;
   int count;
   char** words;
};

typedef struct book book;

/* Create empty book, specifying lenght of strings and how many of them */
book* book_init(int wordlen, int maxwords);
/* Add one element into the book */
void book_insert(book* s, char* v);
/* Creates and returns new, bigger book */
book* resize(book* s, book* new);
/* Prints book */
void prints(book* a);

int main(void) 
{
   book* test;
   test = book_init(60, 10);

   book_insert(test, "dog");
   book_insert(test, "cat");
   book_insert(test, "mouse");
   book_insert(test, "elephant");
   book_insert(test, "snake");

   /*The next insert will cause the resize function to trigger*/
   book_insert(test, "fish");

   /*The resize funtion should cause 'test' to be replaced by a bigger book*/
   /*But doesn't as this next print shows*/     
   printf("But printing from main function means I'm back to %d\n", test->size);
   prints(test);

}


void book_insert(book* s, char* v)
{
   int i = 0;

   while (s->words[i] != NULL ) {
      i++;
   }

   s->words[i] = v;
   s->count++;

   /*If the book is half full resize is triggered, and should pass back new, bigger book*/
   if((s->count * 100 / s->size) > 50) {
      book *new_book;
      new_book = book_init(60, 20);
      s = resize(s, new_book);
      printf("Printing from resize function gives me new length of %d\n", s->size);
      prints(s);
   }
}

book* resize(book* s, book* new)
{
   int i;
   for (i = 0; i < s->size; i++) {
      if (s->words[i] != NULL ) {
         new->words[i] = s->words[i];
      }
   }



   return new;
 }

 book* book_init(int wordlen, int maxwords)
 {
    int i;
    book* new = malloc(sizeof(book));
    new->size = maxwords;
    new->count = 0;
    new->words = (char**) calloc((size_t)new->size, sizeof(char*));

    for (i=0; i<new->size; i++) {
       new->words[i] = (char*) calloc(wordlen, sizeof(char));
       new->words[i] = NULL;
    }

    return new;
 }

 void prints(book* a)
 {
    int i;
    for (i = 0; i < a->size; i++) {
       printf("Index: %d, word: %s\n", i, a->words[i]);
    }
 }

I have also attempted this with a pointer swap in a separate function, but this does not seem to work either. In this version I have made book_resize void and instead from dic_insert called the below function, after the resize, with dictionary_swap(&new_book, &s):

void dictionary_swap(book **new, book **old) 
{
   book *temp = *old;
   *old = *new;
   *new = temp;
}

This again lets me print out the new larger, structure within the book_insert function, but has no affect on what happens in main.

EDIT ANSWER This question has been marked as a duplicate, which means I can't answer it myself, however I have since found the answer; I changed the above duplicate swap so that I called dictionary_swap(new_book, s); (no ampersands) on the following code:

void dictionary_swap(book *new, book *old) 
{
   book temp;

   temp = *old;
   *old = *new;
   *new = temp;
}
Concrete_Buddha
  • 556
  • 4
  • 16
  • 2
    `new->words[i] = (char*) calloc(wordlen, sizeof(char)); new->words[i] = NULL;` why do you set it to `NULL` right after allocating? – Osiris Dec 12 '18 at 11:02
  • @Osiris You are right, it is not needed. I was trying to create a minimum viable example, and this code sneeked in from an example where I was using the NULL elsewhere. – Concrete_Buddha Dec 12 '18 at 11:26

1 Answers1

2

In order to modify a pointer inside a function you have to pass the address of the pointer to the function, eg:

    void changePtr(char* test) {
        test = "Hello";
    }

The above will not work because test cannot be returned to the caller, however:

    void changePtr(char** test) {
        if ( test != NULL ) {
            *test = "Hello";
        }
    }

The above will work because the address of the pointer is passed and it can be de-referenced to change the contents.

Example of calling:

    char* ptr;
    changePtr(&ptr);

Here is a rewrite of your code implementing the above technique:

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

    typedef struct _book {
       int size;
       int count;
       char** words;  //Must allocate space for each pointer before copying to.
    } book;

    //No need for below, see above:
    //typedef struct book book;

    /* Create empty book, specifying lenght of strings and how many of them */
    book* book_init(int wordlen, int maxwords);
    /* Add one element into the book */
    void book_insert(book** s, char* v);
    /* Creates and returns new, bigger book */
    book* resize(book* s, book* new);
    /* Prints book */
    void prints(book* a);

    int main(void) {
       book* test = book_init(60, 10);        
       book_insert(&test, "dog");
       book_insert(&test, "cat");
       book_insert(&test, "mouse");
       book_insert(&test, "elephant");
       book_insert(&test, "snake");      
       /*The next insert will cause the resize function to trigger*/
       book_insert(&test, "fish");        
       /*The resize funtion should cause 'test' to be replaced by a bigger book*/
       /*But doesn't as this next print shows*/     
       printf("But printing from main function means I'm back to %d\n", test->size);
       prints(test);
    }


    void book_insert(book** s, char* v) {
       if ( s == NULL || v == NULL ) {
         return;
       }
       (*s)->words = realloc((*s)->words, sizeof(char*) * (++(*s)->count));
       (*s)->words[(*s)->count - 1] = v;
       /*If the book is half full resize is triggered, and should pass back new, bigger book*/
       if((((*s)->count * 100) / s->size) > 50) {
          book *new_book;
          new_book = book_init(60, 20);
          *s = resize(*s, new_book);
       }
    }

    book* resize(book* s, book* new) {
       int i;
       for (i = 0; i < s->size; i++) {
          if (s->words[i] != NULL ) {
             new->words[i] = s->words[i];
          }
       }        
       printf("Printing from resize function gives me new length of %d\n", new->size);
       prints(new);
       return new;
     }

     book* book_init(int wordlen, int maxwords) {
        int i;
        book* new = calloc(1, sizeof(book));
        new->size = maxwords;
        return new;
     }

     void prints(book* a) {
        int i;
        for (i = 0; i < a->size; i++) {
           printf("Index: %d, word: %s\n", i, a->words[i]);
        }
     }
SPlatten
  • 5,334
  • 11
  • 57
  • 128
  • Appreciate your comments. I have tried what I think is this approach though, I've edited my question to include this. I am sure you are right - it is just my implementation that is lacking :) – Concrete_Buddha Dec 12 '18 at 11:36
  • See my edit to the answer, there is more wrong than just passing back the pointer. – SPlatten Dec 12 '18 at 11:36
  • Thank you for your answer, and I do appreciate the time you have put towards this - unfortunately a constraint I have is that the function in main I have to call is void book_insert(dic* s, char* v) i.e. single pointer only. I'll edit the question to add this. – Concrete_Buddha Dec 12 '18 at 11:51
  • You can do it, but I think your original question and implementation were a bit misleading, all you actually need is one instance of book with multiple words, correct? – SPlatten Dec 12 '18 at 12:00