[13] in 6.033-lab
Pointer Rules
daemon@ATHENA.MIT.EDU (Patrick McCormick)
Mon Mar 17 03:43:05 1997
To: 6.033-lab@MIT.EDU
Date: Mon, 17 Mar 1997 03:42:52 EST
From: Patrick McCormick <pmccormi@MIT.EDU>
I know of a few people having pointer confusion, so I drew up this short
how-to on my two rules on using pointers with functions. They suggested
that I forward it along to everyone. Enjoy.
--Pat McCormick
------- Forwarded Message
Here's the basic two rules regarding passing pointers.
1. If you want to _use_ an already existing, allocated pointer, then pass
it as a simple pointer in the function declaration.
Ex:
void fill_me(char *buffer, int len)
{
assert(buffer != NULL);
memcpy(buffer, SOME_DATA, len);
}
You are giving the memory reference to the procedure and saying,
"Here! Put stuff starting at this address!" If you are passing a
character buffer, this pointer gives no indication of how much
space has been allocated, so you need to pass a length
pointer. This isn't true for int's and the like since we know how
long those are.
Note the "assert" function above that prevents people from passing
a null pointer in as a buffer. It won't prevent a user from sending
you a bogus memory address, however; if they do, you will get a
segmentation fault.
2. If you want to _create_ a pointer, you have two options:
A) return a simple pointer
B) pass in a double pointer that will get "filled" with the new
single pointer value.
The key to remember is that CHANGES TO PARAMETERS ONLY TAKE EFFECT DURING
THE PROCEDURE. A simple example is:
void silly_function(int a) {
a = 1;
}
main() {
int b = 0;
silly_function(b); /* b is unchanged. */
printf("%d\n", b);
}
which prints "0". This seems obvious, but is harder to see when you start
using pointers. Here's an example using pointers:
void alloc_buffer(char *buf)
{
buf = (char*)malloc(sizeof(BUF_LEN * sizeof(char)));
}
main() {
char *buf = NULL;
alloc_buffer(buf); /* buf is unchanged */
strcpy(buf, "my data"); /* BOOM! Segfault! */
}
buf is still NULL after alloc_buffer, and the strcpy tries to
write to address 0x0, which causes a segfault.
A question that crops up is "But I did a malloc! Where did my
allocated memory go?" The answer is "la-la land". It's lost memory;
not deallocated, but a block of memory whose address is not
remembered by any pointer in your program. When alloc_buffer
terminates, the address returned by malloc is lost forever.
To do things right, you can either return the new pointer value, or
use a double-pointer. Returning the new pointer value is
straightforward.
char* alloc_buffer() {
buf = (char*)malloc(sizeof(BUF_LEN * sizeof(char)));
}
main() {
char *buf = NULL;
buf = alloc_buffer();
strcpy(buf, "data"); /* all is well. */
}
Or, you can use another pointer to hold the new pointer value. This
often scares people, but the idea of a pointer holding another
pointer value makes sense - a pointer can hold a memory reference
to any other object in C, and that includes pointers.
However, ALWAYS REMEMBER TO ALLOCATE THE DOUBLE POINTER. Sometimes
people try:
char **a = NULL;
*a = malloc(...); /* BOOM! */
What happens here is that the **a pointer value holds NULL, so *a
resolves to "a pointer whose value is located at address 0x0,"
which is bogus and causes a segfault.
One way to get around this is by using an address operator. This
returns a pointer with one more degree of indirection that its
argument.
So if we have "char *a", then "&a" is a double pointer. "&a"
contains the address of the pointer a, which contains the address
of some character buffer.
In practice, routines look like:
void alloc_buffer(char **buf) {
assert(buf != NULL);
*buf = (char*)malloc(BUF_LEN * sizeof(char));
}
main() {
char *buffer;
alloc_buffer(&buffer); /* this is a double-pointer */
}
That covers how to pass pointers around.
------- End of Forwarded Message