简体   繁体   中英

Problem with malloc and/or realloc of a char** pointer of an array of pointers

I tried to debug part of a larger code of mine, the code is mainly for testing malloc and realloc of char** on a smaller, more manageable scale with fewer functions. What I was trying to achieve is store a "debugging_in_progress" string through multiple pointers, each mallocated individually (no shallow copy) and have these pointers stored in a (char**) pointer. For this example, "texts" starts with a size of 10, then increases to 40 by doubling it twice, and the amount of texts stored in it should be 35 strings.

For debugging purposes, I tried printing out the memory locations themself with the stored string included while putting the pointers into "texts". After all 35 mallocs were done and all pointers were stored, I tried printing them out again to check if anything changed.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
void reallocMem(char **currArray, int* maxSize, int currSize);
void memCheck(void ptr);

int main()
{
char debug[100] = "debugging_in_progress";

    int length = strlen(debug);
    char** texts = (char**) malloc (sizeof(char*) * 10);
    int max = 10;
    int c;
    
    for (int i= 0; i < 35; ++i)
    {
        reallocMem(texts,&max,i);
        texts[i] = (char*) malloc (sizeof(char) * length);
        printf("OG %d.: %p\n",i,texts[i]);
        
        for (int j = 0; j <= length; j++)
        {
            texts[i][j] = debug[j];
        }
        printf("OG %d.TEXT: %s\n",i,texts[i]);
        c++;
    
    }
    
    for (int i = 10; i < 35; ++i)
    {
        printf("%d.:%p\n",i+1,texts[i]);
        // free(texts[i]);
    }
    printf("%d/%d",c,max);
    return 0;

}

void reallocMem(char **currArray, int* maxSize, int currSize)
{
if (currSize >= (*maxSize))
{
(*maxSize) *= 2;
currArray = (char*) realloc(currArray, (*maxSize) * sizeof(char*));
memCheck(currArray);

    }

}

void memCheck(void* ptr)
{
if (!ptr)
{
fprintf(stderr,"Row couldn't be processed: no memory left");
exit(7);
}
}

The problems that I encountered: These problems likely come from my misunderstanding of how malloc and realloc actually function.

First, the text below is the output of the code above, which seems to have lost the first 2 pointers to some strings.

Second, when I tried printing out strings (using %s) after storing all pointers, I got Segmentation Fault, even when I tried to "avoid" the seemingly lost pointers (by setting i = 10 for example).

Third, when I tried to set max and it's corresping value when malloc-ing texts to for example 4, I got no erros, nothing, the program just seeming crashed.

Output of code above:

"""

▒'}@(}▒&}▒%}▒(})} (} (}▒&}'} &}▒(}▒(} '}@&}&} '} )}▒%}ugging_in_progress

OG 3.: 00000208e97d23c0 OG 3.TEXT: debugging_in_progress

OG 4.: 00000208e97d23e0 OG 4.TEXT: debugging_in_progress

OG 5.: 00000208e97d2400 OG 5.TEXT: debugging_in_progress

OG 6.: 00000208e97d2420 OG 6.TEXT: debugging_in_progress

OG 7.: 00000208e97d2440 OG 7.TEXT: debugging_in_progress

OG 8.: 00000208e97d2460 OG 8.TEXT: debugging_in_progress

OG 9.: 00000208e97d2480 OG 9.TEXT: debugging_in_progress

OG 10.: 00000208e97d12f0 OG 10.TEXT: debugging_in_progress

OG 11.: 00000208e97d1310 OG 11.TEXT: debugging_in_progress

OG 12.: 00000208e97d1330 OG 12.TEXT: debugging_in_progress

OG 13.: 00000208e97d2550 OG 13.TEXT: debugging_in_progress

OG 14.: 00000208e97d28a0 OG 14.TEXT: debugging_in_progress

OG 15.: 00000208e97d2940 OG 15.TEXT: debugging_in_progress

OG 16.: 00000208e97d27c0 OG 16.TEXT: debugging_in_progress

OG 17.: 00000208e97d2840 OG 17.TEXT: debugging_in_progress

OG 18.: 00000208e97d26c0 OG 18.TEXT: debugging_in_progress

OG 19.: 00000208e97d25e0 OG 19.TEXT: debugging_in_progress

OG 20.: 00000208e97d2880 OG 20.TEXT: debugging_in_progress

OG 21.: 00000208e97d2900 OG 21.TEXT: debugging_in_progress

OG 22.: 00000208e97d2860 OG 22.TEXT: debugging_in_progress

OG 23.: 00000208e97d2820 OG 23.TEXT: debugging_in_progress

OG 24.: 00000208e97d2680 OG 24.TEXT: debugging_in_progress

OG 25.: 00000208e97d2700 OG 25.TEXT: debugging_in_progress

OG 26.: 00000208e97d2620 OG 26.TEXT: debugging_in_progress

OG 27.: 00000208e97d28c0 OG 27.TEXT: debugging_in_progress

OG 28.: 00000208e97d28e0 OG 28.TEXT: debugging_in_progress

OG 29.: 00000208e97d2720 OG 29.TEXT: debugging_in_progress

OG 30.: 00000208e97d2640 OG 30.TEXT: debugging_in_progress

OG 31.: 00000208e97d2600 OG 31.TEXT: debugging_in_progress

OG 32.: 00000208e97d2760 OG 32.TEXT: debugging_in_progress

OG 33.: 00000208e97d2920 OG 33.TEXT: debugging_in_progress

OG 34.: 00000208e97d25c0 OG 34.TEXT: debugging_in_progress

11.:0000007373657267 12.:00000208e97d1310 13.:00000208e97d1330 14.:00000208e97d2550 15.:00000208e97d28a0 16.:00000208e97d2940 17.:00000208e97d27c0 18.:00000208e97d2840 19.:00000208e97d26c0 20.:00000208e97d25e0 21.:00000208e97d2880 22.:00000208e97d2900 23.:00000208e97d2860 24.:00000208e97d2820 25.:00000208e97d2680 26.:00000208e97d2700 27.:00000208e97d2620 28.:00000208e97d28c0 29.:00000208e97d28e0 30.:00000208e97d2720 31.:00000208e97d2640 32.:00000208e97d2600 33.:00000208e97d2760 34.:00000208e97d2920 35.:00000208e97d25c0 35/40

"""

Some good rules when doing C programming:

  1. Set a high compiler warning level
  2. Fix all warnings before executing the program

For the gcc compiler I'll recommend (at least) these options: -Wall -Wextra -Werror

The -Werror turns all warnings into errors and thereby helps you remember rule #2 above.

Now a direct copy of your code and a compiled with gcc 12.1.0 and -Wall -Wextra -Werror gives me:

error: parameter 1 ('ptr') has void type [-Werror]
    6 | void memCheck(void ptr);
      |               ~~~~~^~~
In function 'main':
error: passing argument 1 of 'reallocMem' from incompatible pointer type [-Werror=incompatible-pointer-types]
   19 |         reallocMem(texts,&max,i);
      |                    ^~~~~
      |                    |
      |                    char **
note: expected 'char *' but argument is of type 'char **'
    5 | void reallocMem(char *currArray, int* maxSize, int currSize);
      |                 ~~~~~~^~~~~~~~~
At top level:
error: conflicting types for 'reallocMem'; have 'void(char **, int *, int)'
   42 | void reallocMem(char **currArray, int* maxSize, int currSize)
      |      ^~~~~~~~~~
note: previous declaration of 'reallocMem' with type 'void(char *, int *, int)'
    5 | void reallocMem(char *currArray, int* maxSize, int currSize);
      |      ^~~~~~~~~~
In function 'reallocMem':
error: assignment to 'char **' from incompatible pointer type 'char *' [-Werror=incompatible-pointer-types]
   47 | currArray = (char*) realloc(currArray, (*maxSize) * sizeof(char*));
      |           ^
error: too many arguments to function 'memCheck'
   48 | memCheck(currArray);
      | ^~~~~~~~
note: declared here
    6 | void memCheck(void ptr);
      |      ^~~~~~~~
At top level:
error: conflicting types for 'memCheck'; have 'void(void *)'
   54 | void memCheck(void* ptr)
      |      ^~~~~~~~
note: previous declaration of 'memCheck' with type 'void(void)'
    6 | void memCheck(void ptr);
      |      ^~~~~~~~

All these must be fixed before running the program .

It only requires 3 small changes

#include <string.h>
void reallocMem(char *currArray, int* maxSize, int currSize);
void memCheck(void ptr);

to

#include <string.h>
void reallocMem(char **currArray, int* maxSize, int currSize);
void memCheck(void *ptr);

and

currArray = (char*) realloc(currArray, (*maxSize) * sizeof(char*));

to

currArray = (char**) realloc(currArray, (*maxSize) * sizeof(char*));

er better, just don't do a cast.

currArray = realloc(currArray, (*maxSize) * sizeof(char*));

Now the program compiles without errors but there are still (at least) two problems.

(*maxSize) = 2;

isn't doubling but constantly assign two. You probably wanted:

(*maxSize) *= 2;

The next problem is a common beginners mistake. Look at this code:

#include <stdio.h>

void foo(int x)
{
    x = 42;
    printf("In foo: x=%d\n", x);
}
int main(void) 
{
    int x = 5;
    printf("In start of main: x=%d\n", x);
    foo(x);
    printf("Back start of main: x=%d\n", x);
    return 0;
}

It gives:

In start of main: x=5
In foo: x=42
Back start of main: x=5

As you can see the value of x is changed to 42 inside foo but as soon as we get back in main the value is still 5. This is because foo gets a copy of the value of x in main . So the two x variables are completely independent and changing x in foo will not change x in main .

You have exactly that problem with reallocMem . You pass text to currArray and then you store the return value from realloc into currArray . Fine... but it will not change the value of text in main .

To change text you need to pass a pointer to text . Like:

void reallocMem(char ***currArray, int* maxSize, int currSize)
{
    if (currSize >= (*maxSize))
    {
        (*maxSize) = *2;
        *currArray = realloc(*currArray, (*maxSize) * sizeof(char*));
    }
}

and call it like:

reallocMem(&texts, &max, i);

In fact this is no different from the way you handled max .

And yes, you are now officially a three-star-programmer

Two minor things at the end...

In your malloc / realloc you always use sizeof(type-name), for instance

 TYPE* x = malloc(N * sizeof(TYPE));

So you write TYPE two times which opens for bugs if you don't do it correct. A better way is to use the variable name like

 TYPE* x = malloc(N * sizeof(*x));

And again: Don't cast malloc

Last thing:

realloc may return a NULL pointer. Therefore it's recommended to realloc into a temp variable and check for NULL before assigning to the real target variable. Like

        char** temp = realloc(*currArray, (*maxSize) * sizeof(char*));
        if (temp == NULL)
        {
            // Out of memory! Add error handling
        }
        else
        {
            // All good
            *currArray = temp;
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM