-1

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

epic_rain
  • 1
  • 3
  • 1
    You need to pass a pointer to pointer in order to modify it inside the function – UnholySheep Apr 14 '22 at 15:39
  • 1
    Does this answer your question? [How do I modify a pointer that has been passed into a function in C?](https://stackoverflow.com/questions/766893/how-do-i-modify-a-pointer-that-has-been-passed-into-a-function-in-c) – UnholySheep Apr 14 '22 at 15:39
  • 1
    The `realloc` function might have increased the size of the buffer at the same address or it might have allocated a new buffer at a different address and have copied the data. So there is no way around allowing the pointer to change. Note that `buffer = realloc(buffer, total_received);` is wrong because `realloc` might return `NULL` on errors. The correct way would be something like `tmpbuf = realloc(buffer, total_received); if(tmpbuf != NULL) { buffer = tmpbuf; } else { /* handle error */ }` – Bodo Apr 14 '22 at 15:41
  • @UnholySheep can you explain in an answer how to do this – epic_rain Apr 14 '22 at 15:43
  • @Bodo I used your method and it seems to work but when i try to check with valgrind it throws a seg fault but works fine without it – epic_rain Apr 14 '22 at 15:46
  • @epic_rain A segmentation fault is often related to errors in other parts of the code. – Bodo Apr 14 '22 at 15:52
  • 1
    `strlen(buff)` is not a good idea, and will give you a wrong result. You have received exactly `received` characters. Use that number. `strcat` is no good here either, for the same reason. – n. m. could be an AI Apr 14 '22 at 16:13

2 Answers2

0

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 */
Darth-CodeX
  • 2,166
  • 1
  • 6
  • 23
  • This is very useful but as far as i understood this only makes code more readable and does not solve my issue (correct me on that if i am wrong ) – epic_rain Apr 14 '22 at 15:58
  • @epic_rain There shall be other issue in rest of your code – Darth-CodeX Apr 14 '22 at 15:59
  • @epic_rain Are you calling `realloc()` in other part of your code? – Darth-CodeX Apr 14 '22 at 16:03
  • no this is the only function where i use `realloc` – epic_rain Apr 14 '22 at 16:16
  • 2
    @epic_rain "this only makes code more readable" no, it solves a very real problem with your code. Namely, that your assignment to `buffer` is invisible outside of `receiver`. There are several other unsolved problems still. – n. m. could be an AI Apr 14 '22 at 16:18
  • 1
    You can't use `strlen` and `strcat` because `buff` isn't NUL-terminated. `strlen(buff)` should be replaced with `received`, and `strcat(*buffer, buff)` needs to be replaced with a `memcpy`. Also, you need to return the amount read (or add a NUL). – ikegami Apr 14 '22 at 16:18
  • `memcpy(*buffer, buff, received);` is wrong. You are just throwing away everything received in the previous iteration. – n. m. could be an AI Apr 14 '22 at 17:20
0

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);
    // ...
}
Zakk
  • 1,935
  • 1
  • 6
  • 17