user2469227
user2469227

Reputation:

How does size information get passed with a pointer?

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:

  1. Something is happening behind the scenes

  2. 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

Answers (2)

Floris
Floris

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:

  • It allocates a bigger block than you ask for - because it's more efficient to work with, say, 64 bytes at a time (alignment issues and such)
  • Often, there is a small block of code "just below" the pointer that contains some information about the size of the block, and pointers to the "next and previous blocks" - like a linked list. That way if you clear one block, the memory management can keep track of what is free and what is not.
  • It is also possible that a allocation table is being maintained separately that describes what blocks are free / used, how big they are, etc.

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

Keith Thompson
Keith Thompson

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

Related Questions