Lion King
Lion King

Reputation: 33823

Expanding a dynamic memory block as needed

I want to assign data to a block of memory but I don't know what's the size which I need.
For example, the following is just a simple example which illustrates the purpose:

char *str = (char*)malloc(2);
for (int i = 1; i < 10; i++) {
    str[i] = i;
    realloc(str, strlen(str) + 1);
}
printf("%s\n", str);

The previous code prints unknown symbols like the following:

What is the problem, then?

Upvotes: 0

Views: 235

Answers (2)

user3629249
user3629249

Reputation: 16540

What is the problem, then?

very simple, the values in i are 0x00000000 0x00000001 ... 0x00000009

(which do not fit into a char)

and those values are not part of the 'printable' ascii character set.

Suggest adding 0 (0x30) to each of the values as they are assigned to the array.

Also, suggest casting the int i to (char) at each assignment

the values in the array do not make a 'string' as there is no terminating NUL (0x00) byte.

Following edited for the comment from lion king

Suggest writing the code similar to the following:

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

int main( void )
{
    char *str = malloc(2);   // do not 'cast' the returned value
    if( !str )
    {
        perror( "malloc failed" ); // output enclosed text and reason
                                   // system thinks the 'malloc` failed
                                   // to `stderr`
        exit( EXIT_FAILURE );
    }

    // implied else, malloc successful

    int i;
    for ( i = 0; i < MAX_CHARS; i++) 
    {
        str[i] = (char)('0' + i);
        char *temp = realloc(str, i+3);  // assign to temp variable
                                         // and check if successful
                                         // to avoid causing a memory
                                         // leak when it fails
        if( !temp )
        {
            perror( "realloc failed" );
            free( str );  // cleanup
            exit( EXIT_FAILURE );
        }

        // implied else, realloc successful

        str = temp;   // update the target variable
    }

    // terminate the string
    str[ i ] = '\0';

    printf("%s\n", str);

    free( str );  // cleanup

    return 0;
}

Upvotes: 0

Pablo
Pablo

Reputation: 13590

You are allocating memory, that's OK. You are writing a character inside the limits, that's OK too. But then you use strlen, that's not OK, because strlen expects a valid string, you are not passing string.

In C a string is just a sequence of characters that ends with the '\0'-terminating byte. You are not doing that, so strlen will go beyond the limit until it finds the '\0'-terminating byte and this is undefined behaviour.

Also realloc might change the memory location when expanding the memory. You are not accounting for that. If realloc returns a different pointer, the old location is invalid and the next realloc call will yield undefined behaviour.

The way you are filling the data, you don't even need the strlen call, i gives you that information already.

To fix your code:

char *str = calloc(2, 1);
if(str == NULL)
{
    // error handling
    // do not continue
    return;
}

for (int i = 0; i < 9; i++) {
    str[i] = i + 1 + '0';
    str[i+1] = 0; // making sure you have a valid c-string

    char *tmp = realloc(str, strlen(str) + 2);
    if(tmp == NULL)
    {
        // error handling,
        // do not continue
        free(str);
        return;
    }

    // making sure that str points to the new location
    // if realloc returned a different pointer
    str = tmp;
}

printf("%s\n", str);
free(str);

You should always check for the return value of malloc, calloc, realloc. Also the realloc should be strlen(str) + 2. The +2 because you want to add a new character and you have to have space for the '\0'-terminating byte.

Note that for the first allocation I used calloc, because calloc also sets the allocated memory to 0, great for initialization. But regardless of that, you should set the '\0'-terminating byte every time you add a new character (str[i+1] = 0; line). I also store the value i + '0', which is most probably what you want. 1 != '1', because the character '1' has the value 49, not 1. See: ASCII table.

I've changed the str[i] = i + '0'; to str[i] = i + 1 + '0';, because it seems that you want the string to start with a '1', not a '0'.

Also never forget to free the memory you've allocated. You can free the memory at the end of the program or better still, when you don't need it anymore.

Upvotes: 1

Related Questions