[13] in 6.033-lab

home help back first fref pref prev next nref lref last post

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


home help back first fref pref prev next nref lref last post