2

I tried to increase a double pointer buffer in another method/function. However, the size of the allocated buffer does not changes. Here is the code I tried.

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

void change_buffer_size(char ** buffer)
{
     *buffer = realloc(*buffer, sizeof(char) * 2);
}

int main()
{
    char **buffer = malloc(sizeof(char) * 1); 
    *buffer[0] = 'H';
    change_buffer_size(buffer);
    *buffer[1] = 'i';
    printf("%s\n", *buffer);
    return 0;
}

I get the Segmentation fault (core dumped) error.

1
  • 1
    Always realloc with a temporary pointer. When realloc fails it returns NULL which will overwrite your original pointer causing a memory leak. E.g. void *tmp = realloc (*buffer * 2 * sizeof **buffer); if (!tmp) { perror ("realloc-tmp"); return; }; *buffer = tmp; } Commented Jan 4, 2020 at 4:16

2 Answers 2

1
char **buffer = malloc(sizeof(char) * 1); 

This is wrong. You now have a char**, so the thing that it points to is a char*, but there's only enough room in it for a char. You also only allocated one layer of the double pointer.

*buffer[0] = 'H';

This line causes the segfault because of the above problems. It's trying to write to memory in an undefined location.

The best way to fix this is to just allocate the first layer normally, use & where necessary, and only use malloc for the second layer.

Also, %s doesn't stop writing until it sees a null byte, so you need to allocate and write one of them. Here's how you fix it all:

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

void change_buffer_size(char ** buffer)
{
     *buffer = realloc(*buffer, sizeof(char) * 3);
}

int main()
{
    char *buffer = malloc(sizeof(char) * 1); 
    buffer[0] = 'H';
    change_buffer_size(&buffer);
    buffer[1] = 'i';
    buffer[2] = '\0';
    printf("%s\n", buffer);
    return 0;
}
Sign up to request clarification or add additional context in comments.

5 Comments

thank you very much for the solution. could you please tell me how can modify the buffer inside the change_buffer_size function ? for example, buffer[1] = 'i'; after realloc. I get the segmentation fault again.
@vortex Note that buffer is a different type in change_buffer_size and in main now. What you wrote is correct in main, but in change_buffer_size, you'd need to do (*buffer)[1] = 'i'; instead.
thanks again. Could you please elaborate a bit why should I use such a syntax?
@vortex Because buffer is a char** there, so you need to do (*buffer) first to get a char*, and then [1] to get a char that you can finally write 'i' to.
You still invoke Undefined Behavior -- a lack of a nul-terminating character when treating buffer as a string in printf("%s\n", buffer); buffer is too short by one.
1

You already have a good answer by @JosephSible but there is one further point that needs making about the use of realloc. I've alluded to this in the comment under the original question. When using realloc, you must always realloc using a temporary pointer to capture the return of realloc.

Why? When (not if) realloc fails, it returns NULL. If you assign the return directly to the pointer you are attempting to reallocate for, you overwrite the pointer to your existing block of memory with NULL losing your reference to the original block creating a memory leak. For example, you do not want to:

    *buffer = realloc (*buffer, 2);       /* sizeof(char) is always 1 */

Instead, use a temporary pointer and validate the reallocation succeeds before assigning the reallocated block to your original pointer, e.g.

    void * tmp = realloc (*buffer, 2);
    if (!tmp) {     /* validate EVERY allocation & reallocation */
        perror ("realloc-*buffer");
        exit (EXIT_FAILURE);        /* or handle as desired, e.g return NULL, etc.. */
    }
    *buffer = tmp;  /* now assign the reallocated block to your pointer */

A couple of comments on your original post. Recall in C a string must end with a nul-terminating character. You cannot simply assign buffer[0] = 'H'; and treat buffer as a string. The nul-termianting character ('\0', or simply 0) must follow, so you would need buffer[1] = 0; before calling printf("%s\n", *buffer);

Avoid using Magic-Numbers in your code. Your change_buffer_size() function hardcodes the reallocation to size using the magic-number 2. (not very useful). Instead, at least pass the desired size as a parameter so your function is reusable, e.g.

char *change_buffer_size (char **buffer, size_t nchar)
{
    void *tmp = realloc (*buffer, nchar * sizeof **buffer);
    if (!tmp) { /* validate EVERY allocation/reallocation */
        perror ("change_buffer_size()-realloc");
        return NULL;
    }

    return *buffer = tmp;   /* assign new block to *buffer, return */
}

(note: changing the return type to char* allows you to indicate success/failure of the function through the return as well as having direct access to the reallocated block of memory on success)

Now is you want to reallocate your buffer to 2-characters, just pass 2 as nchar, etc.. That combined with a short example that reallocates and adds a character to your buffer at a time (while ensuring it is always nul-terminated) could be something like the following:

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

char *change_buffer_size (char **buffer, size_t nchar)
{
    void *tmp = realloc (*buffer, nchar * sizeof **buffer);
    if (!tmp) { /* validate EVERY allocation/reallocation */
        perror ("change_buffer_size()-realloc");
        return NULL;
    }

    return *buffer = tmp;   /* assign new block to *buffer, return */
}

int main (void) {

    size_t nchar = 1;   /* character counter */
    char *str = "hello world!", *buffer = NULL;

    for (int i = 0; str[i]; i++) {
        if (!change_buffer_size(&buffer, nchar + 1))    /* alloc nchar + 1 */
            return 1;
        buffer[nchar-1] = str[i];           /* copy char from str to buffer */
        buffer[nchar++] = 0;                /* nul-terminate buffer */
        printf ("buffer: '%s'\n", buffer);  /* print current buffer contents */
    }

    free (buffer);      /* don't forget to free what you allocate */
}

(note: don't forget to free() the memory you allocate. Yes, here it will be freed on program exit, but build good habits early -- you will not always be working in main())

Example Use/Output

$ ./bin/realloccharbuf
buffer: 'h'
buffer: 'he'
buffer: 'hel'
buffer: 'hell'
buffer: 'hello'
buffer: 'hello '
buffer: 'hello w'
buffer: 'hello wo'
buffer: 'hello wor'
buffer: 'hello worl'
buffer: 'hello world'
buffer: 'hello world!'

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/realloccharbuf
==19740== Memcheck, a memory error detector
==19740== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19740== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19740== Command: ./bin/realloccharbuf
==19740==
buffer: 'h'
buffer: 'he'
buffer: 'hel'
buffer: 'hell'
buffer: 'hello'
buffer: 'hello '
buffer: 'hello w'
buffer: 'hello wo'
buffer: 'hello wor'
buffer: 'hello worl'
buffer: 'hello world'
buffer: 'hello world!'
==19740==
==19740== HEAP SUMMARY:
==19740==     in use at exit: 0 bytes in 0 blocks
==19740==   total heap usage: 13 allocs, 13 frees, 1,114 bytes allocated
==19740==
==19740== All heap blocks were freed -- no leaks are possible
==19740==
==19740== For counts of detected and suppressed errors, rerun with: -v
==19740== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Look things over and let me know if you have questions.

2 Comments

If you are just going to exit -- it doesn't matter. But when teaching, you always present the general case first and then move on to the exceptions. That way if the OP moves the code to a function -- they are covered. You know C and know enough to forward think from wherever you are in your code and determine whether an action is necessary. That comes from experience more so that being something you can teach as an exception in a answer (It's doable, it just muddies the water a bit)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.