Reputation:
I'm learning C and using the free() function lead me to this question. Say I have a pointer:
char *p = "String";
Now 7 bytes of memory have been allocated for p to point to. However, sizeof(*p) returns a 1 which makes it seem like the pointer only "knows" about the 1 byte whose address it holds.
If this is the case, then how does free(p) free up all 7 bytes. So the only things I can think of are:
Something is happening behind the scenes
Only the first byte that is being pointed to needs to be deallocated
Is it one of these? If it's 1. what is the hidden process/how does it work? If it's 2. then how does the machine keep track of the 6 other bytes that should not be allocated for other pointers and variables?
Thanks for the help!
Upvotes: 2
Views: 131
Reputation: 46365
sizeof(*p)
is 1 because p
points to a character (which is one byte).
"How does it know" how big the allocated memory is - it's "platform dependent". Very often you will find two things about malloc:
NOTE - what I am describing above relates to memory allocate with malloc
- which is the only kind that C can free()
. A string constant is allocated in a different way, and its size and position are fixed - which is why you would usually write
const char *p = "hello";
For such a string, you know the size by looking for the nul
terminator. But if you actually decided to overwrite a character earlier in the string with '\0'
, it does not change the amount of memory allocated:
char p[] = "hi there world";
p[2] = '\0';
printf("%s***\n", p);
output:
hi***
but the memory is not freed up.
Upvotes: 1
Reputation: 263237
char *p = "String";
This is legal, but it really should be:
const char *p = "String";
so you don't accidentally try to modify the array.
What array, you ask? Any string literal corresponds to a statically allocated array of size LEN+1
, where LEN
is the length of the literal. So in this case, there's an anonymous array of type char[7]
that exists during the entire execution of your program, containing the values
`{ 'S', 't', 'r', 'i', 'n', 'g', '\0' }`.
The initializer for p
causes it to point to the first element of that array, which contains the 'S'
. And since the array was not allocated by a call to malloc()
(or calloc()
, or realloc()
), you must not attempt to free()
it.
Given p
, you can't directly determine the size of the array (since p
points to its first element, not to the array as a whole). You can compute strlen(p) + 1
, which is probably the same as the allocated size -- but if the string literal were "foo\0bar"
, then it would be 8 bytes long, but strlen
would stop at the first null character and return 3
. So if you need to be able to determine the size of the array, don't discard that information by assigning its address to a pointer.
Now if you had instead written:
char arr[] = "String";
the initializer would copy the contents of the string literal into the array arr
, and the size of arr
would be determined by the compiler from the size of the initializer. Again, you must not attempt to call free()
on this array (or rather, on a pointer to its first element), since it wasn't allocated with malloc()
. But you can easily determine its size via sizeof arr
, which will yield 7
. (sizeof p
would give you the size of a pointer, not of the array it points to.)
Finally, if you used malloc()
to allocate an array:
#define message "String"
char *p = malloc(sizeof message + 1);
if (p == NULL) {
fprintf(stderr, "malloc failed\n");
exit(EXIT_FAILURE);
}
strcpy(p, message);
you still can't use p
(which is just a pointer to a single character) to determine the size of the allocated array -- but free()
is able to figure it out. How? The language doesn't say, but any implementation will keep extra bookkeeping information behind the scenes to let free()
do the right thing.
Note that the implementation doesn't necessarily remember the size you requested. A call to malloc(7)
might actually allocate, say, 16 bytes, and the only information free
needs is the base address and the actual allocated size.
But since you wrote the call to malloc
, if you need to remember the allocated size, just save it somewhere:
// ...
const size_t allocated_bytes = sizeof message + 1;
char *p = malloc(allocated_bytes);
// ...
If you have a declared array object, you can use sizeof
to determine its size. Arrays are usually manipulated using pointers, which do not retain the size of the array; you have to keep track of that yourself. malloc()
and friends do maintain some size information behind the scenes, but only for the purpose of letting free()
work correctly; you (probably) can't access that information yourself. If you need to keep track of the size of an allocated array, you have to do it yourself.
Upvotes: 1