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.
reallocwith a temporary pointer. Whenreallocfails it returnsNULLwhich 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; }