Search code examples
crealloc

Realloc setting pointer to empty


void test(){
   char *c = malloc(strlen("I like coffe") + 1);
   strcpy(c, "I like coffe");
   char **s = &c;
   while(strlen(*s) < 25)
      my_function(s);
}

void my_function(char **s){
  char *w  = *s;   

  char *tmp = realloc(w, len + 2);//Error HERE. *s gets = ""
  if(tmp != NULL)
  w = tmp;
  for(i= len; i>=p; i--){
      w[i+1] = w[i];
  }
  w[p] = c;    
}  

This function is used to insert a new character inside a char *.
Also, this function is inside a while loop. It works fine but by the 3rd time the loop runs, it just sets *s = "".
I thought that by using the char *tmp I could keep the data if any wrong thing happen. I can't understand why P *s is been setted to empty string.


Solution

  • You've forgotten to assign the new value to *s in the function that contains realloc().

    void test(void)  // Unaltered; still broken!
    {
        char *c = malloc(strlen("I like coffe") + 1);
        strcpy(c, "I like coffe");
        char **s = &c;
        while (strlen(*s) < 25)
            my_function(s);
    }
    
    void my_function(char **s)  // Fixed one way
    {
        char *w  = *s;   
        size_t len = strlen(w) + 1;  // Define and initialize len
    
        char *tmp = realloc(w, len + 2);
        if (tmp != NULL)
            w = tmp;
        *s = w;  // Reassign to `*s`
    }
    

    Or, more simply:

    void my_function(char **s)  // Fixed another way
    {
        char *w  = *s;   
        size_t len = strlen(w);  // Define and initialize len
    
        char *tmp = realloc(w, len + 2);
        if (tmp != NULL)
            *s = tmp;  // Reassign to `*s`
    }
    

    Assigning to w only sets the local variable which is a copy of *s; it does not reset the pointer in the calling code.

    Note that even with this fix, the loop in test() is going to run a long time because nothing changes the length of the string in c. There's also another problem: you don't pass the address of s to my_function(), so my_function() can't modify s.

    void test(void)
    {
        char *c = malloc(strlen("I like coffe") + 1);
        strcpy(c, "I like coffe");
        while (strlen(c) < 25)
        {
            my_function(&c);
            strcat(c, "AZ");  // Grow string — not good in real code
            printf("%2zu: <<%s>>\n", strlen(c), c);
        }
    }
    
    void my_function(char **s)
    {
        char *w = *s;   
        size_t len = strlen(w) + 1;  // Define and initialize len
    
        char *tmp = realloc(w, len + 2);
        if (tmp != NULL)
            *s = tmp;  // Reassign to `*s`
    }
    

    This does away with the pointer to pointer to char in test(). If that's crucial, there's more thinking to be done.

    Code not formally tested yet!

    Code now tested — can you say "pig's ear"? Copy'n'paste of the wrong material made my test code fail. Here's the instrumented working version — valgrind gives it a clean bill of health.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    static void my_function(char **s)
    {
        char *w = *s;
        size_t len = strlen(w) + 1;  // Define and initialize len
        printf("M1: %p: %2zu: <<%s>>\n", (void *)w, len, w);
    
        char *tmp = realloc(w, len + 2);
        if (tmp != NULL)
            *s = tmp;  // Reassign to `*s`
        printf("M2: %p: %2zu: <<%s>>\n", (void *)*s, strlen(*s), *s);
    }
    
    static void test(void)
    {
        char *c = malloc(strlen("I like coffe") + 1);
        if (c == 0)
        {
            fprintf(stderr, "Out of memory\n");
            exit(EXIT_FAILURE);
        }
        strcpy(c, "I like coffe");
        printf("T1: %p: %2zu: <<%s>>\n", (void *)c, strlen(c), c);
        while (strlen(c) < 25)
        {
            my_function(&c);
            printf("T2: %p: %2zu: <<%s>>\n", (void *)c, strlen(c), c);
            if (c == NULL)
            {
                fprintf(stderr, "Out of memory\n");
                exit(EXIT_FAILURE);
            }
            strcat(c, "AZ");  // Grow string — not good in real code
            printf("T3: %p: %2zu: <<%s>>\n", (void *)c, strlen(c), c);
        }
        free(c);
    }
    
    int main(void)
    {
        test();
        return 0;
    }