Details about a pointer can easily be obtained and output: it’s name, address, and contents. Obtaining the size of the buffer it references, however, is a different animal.
In C, it’s up to the programmer to know the size of an allocated memory chunk. This value must be managed and adhered to lest the program do something ugly.
But how does the computer know the size? How does the realloc() function know how much memory to release or where to add memory?
While it’s up the C programmer to know these values, internally there must be a way to find out how much memory is associated with a pointer. One function available is malloc_usable_size(), prototyped in the malloc.h
header file. Here’s the man page format:
size_t malloc_usable_size (void *ptr);
The function’s argument is a pointer. The value returned is a size_t, representing the number of bytes available at the address saved in pointer ptr
.
This function isn’t without its quirks. First, it’s a GNU extension and not part of the standard C library. Second, the value returned is intended for research and debugging purposes. Because this is my goal, it’s why I choose to use this function.
The following program is an update from last week’s Lesson. I added a series of printf() statements to output values associated with the original buffer, the allocated buffer, and then re-allocated buffer.
2024_08_24-Lesson.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <malloc.h> int main() { char string[] = "Hello there!"; char *sp; printf("Original string: %s\n",string); printf("Original string's address: %p\n",string); printf("Original string's size: %zu\n",sizeof(string)); /* allocate way too much storage */ sp = malloc(2048); if( sp==NULL ) { fprintf(stderr,"Failed to allocate way too much storage\n"); exit(1); } /* copy the string into storage */ strcpy(sp,string); printf("String copied: '%s'\n",sp); printf("New storage address: %p\n",sp); printf("New storage size: %zu\n",malloc_usable_size(sp)); /* reallocate (re-size) the storage */ sp = realloc(sp,strlen(string)+1); if( sp==NULL ) { fprintf(stderr,"Failed to reallocate storage\n"); exit(1); } printf("Storage reallocated: '%s'\n",sp); printf("Reallocated address: %p\n",sp); printf("Reallocated address size: %zu\n",malloc_usable_size(sp)); /* clean up */ free(sp); return(0); }
Here’s a sample run:
Original string: Hello there!
Original string's address: 0x7ffed53ba57f
Original string's size: 13
String copied: 'Hello there!'
New storage address: 0x5621ee48a6b0
New storage size: 2056
Storage reallocated: 'Hello there!'
Reallocated address: 0x5621ee48a6b0
Reallocated address size: 24
The %p
placeholder is used to obtain the original array’s address. You can prefix the name with an &
(address-of) operator or not; the result is the same. The sizeof operator easily obtains the value of the original string, which is an array.
When malloc() allocates 2048 bytes of storage, its address is returned and the size. The %zu
placeholder represents a size_t value in a printf() format string.
The size_t value returned is 2056, eight bytes larger than what’s allocated. This result is typical for the malloc_usable_size() function. The eight extra bytes are most likely processor overhead or used for memory alignment. This extra storage shouldn’t be accessed or used.
When the realloc() function reallocates storage, the original memory address is retained. The new buffer size reported by malloc_usable_size() is the actual size allocated. Again, this value is for diagnostic and debugging purposes.
I tried to come up with my own version of the realloc() function, but I can’t duplicate whatever magic it performs. So I can just be thankful that it exists and use it as necessary.