Home > Blockchain >  Resizing dynamic arrays in c
Resizing dynamic arrays in c

Time:04-15

How do you properly resize an array using realloc so that the newly allocated array can have the data from the previous array plus the newly received data

int receiver (int soc_desc, char * buffer)
{
    char *arr; 
    size_t received =0 , total_received=0;
    char buff[MAX 1];
    memset(buff , 0 , MAX 1);
    while (1)
    {
        received = recv(soc_desc, buff , MAX , 0 );
        if (received <= 0 )
            break;
        else
        {
            total_received = total_received   strlen(buff);
            buffer = realloc(buffer, total_received);
            printf("Total: %ld received: %ld \n",total_received , received);
            strcat(buffer, buff);
        }
        printf("%s\n",buff);
    } 
    printf("Final result: %s \n", buffer);

in this function, we pass a socket descriptor and a char *buffer = malloc(MAX) we receive data and add it to the allocated buffer and then try to reallocate the buffer for the next chunk of data, is there a way to resize the original mallocd buffer so that I can fit more characters in it without creating a new pointer for realloc each time it is called

when I compile and run this code with valgrind I get


==13850==  Address 0x4a5c0e3 is 0 bytes after a block of size 3 alloc'd
==13850==    at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==13850==    by 0x109884: ??? (in /home/User/Desktop/test)
==13850==    by 0x109476: ??? (in /home/User/Desktop/test)
==13850==    by 0x48870B2: (below main) (libc-start.c:308)

.
.
.
.

==13850== HEAP SUMMARY:
==13850==     in use at exit: 27 bytes in 1 blocks
==13850==   total heap usage: 22 allocs, 22 frees, 15,807 bytes allocated

since buffer is a function parameter I used free(buffer) outside the function

CodePudding user response:

Compile using -g3 -ggdb3 -Wall -Wextra flags to get line number of error in valgrind and other sort of warnings.

Also, realloc() copies previous data to new one.

Some Points:

  • total_received = total_received strlen(buff); can be written as total_received = strlen(buff);
  • You need to take buffer as char **buffer and then de-reference it like (*buffer), so that modification of buffer can be done permanently in another function's scope
  • "%zu" is valid format specifier for size_t
  • Make sure that buffer is heap-allocated in its definition scope
  • memset(buff , 0 , MAX 1); can be written as char buff[MAX 1] = {};
  • I don't see any use of arr variable in receiver() function
  • NOTE: Make sure that new size for buffer is larger than the previous size
  • Always check whether heap allocation was successful or not, by checking the pointer against NULL, eg., if(!buffer) { /* error */ }
  • Pass buffer like &buffer [give address of buffer to receiver() function]
  • received is unsigned long int AKA size_t which means it starts from 0, hence checking for less than 0 is not required, instead check for (received == 0)
  • Use memcpy() to append buff to *buffer by limiting the length of buff
  • Append null terminating character at the very end of *buffer

Final Code:

int receiver(int soc_desc, char **buffer)
{
    char *arr; // idk
    size_t received = 0, total_received = 0;
    char buff[MAX   1] = {}; // every element is now 0
    while (1)
    {
        received = recv(soc_desc, buff, MAX, 0);
        if (received == 0)
            break;
        else
        {
            total_received  = received;
            (*buffer) = realloc(*buffer, total_received   1);
            if (*buffer == NULL) // error occurred
            {
                exit(EXIT_FAILURE);
            }
            printf("Total: %zu received: %zu\n", total_received, received);
            memcpy(*buffer, buff, received);
            (*buffer)[total_received   1] = 0; // nul terminating character
        }
        printf("%s\n", buff);
    }
    printf("Final result: %s \n", *buffer);

    /*your rest of the code */

CodePudding user response:

According to the documentation:

Reallocates the given area of memory. It must be previously allocated by malloc(), calloc() or realloc() and not yet freed with a call to free or realloc. Otherwise, the results are undefined.

The reallocation is done by either:

a) expanding or contracting the existing area pointed to by ptr, if possible. The contents of the area remain unchanged up to the lesser of the new and old sizes. If the area is expanded, the contents of the new part of the array are undefined. (*)

b) allocating a new memory block of size new_size bytes, copying memory area with size equal the lesser of the new and the old sizes, and freeing the old block.

If there is not enough memory, the old memory block is not freed and null pointer is returned. (#)

The formatting (bold text) and (*) and (#) have been added, and were not in the quoted text.

Solving (#):

bool realloc_buffer(void **buffer, size_t new_size)
{
    void *tmp = *buffer;
    *buffer = realloc(*buffer, new_size);
    
    if (!*buffer) { // Realloc failed: restore the old pointer.
        *buffer = tmp;
        return false;
    }
    
    return true;
}

Then in your code:

int receiver (int soc_desc, char **buffer)
{
    // ...
    
    if (!realloc_buffer(buffer, old_size   total_received   1)) { // You should know the old size
        // Handle failure
    }
}

You said:

... so that the newly allocated array can have the data from the previous array plus the newly received data

According to (*), you have to manually append the new data.

int receiver (int soc_desc, char **buffer)
{
    // ...
    
    char *copy = malloc(sizeof(char*) * (old_size 1));
    // Make a copy of the old buffer
    memcpy(copy, buffer, old_size 1);
    
    if (!realloc_buffer(buffer, old_size   total_received   1)) { // You should know the old size
        // Handle failure
    } else {
        memcpy(buffer   old_size * sizeof(char*), copy, total_received 1);
    }
    
    free(copy);
    // ...
}
  • Related