C Source Code/Pointers

From Wikiversity
Jump to navigation Jump to search

This page contains examples of pointers. These examples assume the reader has the following knowledge: compiling C programs, variable creation and assignment, arrays, functions, basic I/O and an understanding of computer memory layout. These examples build from simple to more complex.

Basic Pointers[edit | edit source]

Creating, initializing and assigning[edit | edit source]

This example shows simple pointer creation, assignment and dereference.

#include <stdio.h>

/*Example showing simple pointer assignment operations.
 */
int main(int argc, char *argv[])
{
   int *intPtr1;
   int *intPtr2;

   int a = 5;
   int b = 10;

   printf("\nBefore Initializing pointers.");
   printf("\nintPtr1: %d, *intPtr1: %d", intPtr1, *intPtr1);
   printf("\nintPtr2: %d, *intPtr2: %d\n", intPtr2, *intPtr2);

   /*Initialize the pointers*/
   intPtr1 = NULL;
   intPtr2 = NULL;

   printf("\nAfter Initializing pointers to NULL");
   printf("\nintPtr1: %d", intPtr1);
   printf("\nintPtr2: %d\n", intPtr2);

   intPtr1 = &a;
   intPtr2 = &b;

   printf("\nAfter pointing intPtr1 to the address of 'a' and "
         "\npointing intPtr2 to the address of 'b'\n");
   printf("\nintPtr1: %d, *intPtr1: %d", intPtr1, *intPtr1);
   printf("\nintPtr2: %d, *intPtr2: %d", intPtr2, *intPtr2);
   printf("\na: %d, b: %d\n", a, b);

   *intPtr1 = 7;
   *intPtr2 = 12;

   printf("\nAfter assigning 7 into the memory location pointed to by "
         "intPtr1 \n and assigning 12 into the memory location pointed to by intPtr2");
   printf("\nintPtr1: %d, *intPtr1: %d", intPtr1, *intPtr1);
   printf("\nintPtr2: %d, *intPtr2: %d", intPtr2, *intPtr2);
   printf("\na: %d, b: %d\n", a, b);

   intPtr1 = intPtr2;
   printf("\nAfter storing the memory location of intPtr2 into intPtr1. \nMakes intPtr2 now point to variable 'b' as well");
   printf("\nintPtr1: %d, *intPtr1: %d", intPtr1, *intPtr1);
   printf("\nintPtr2: %d, *intPtr2: %d", intPtr2, *intPtr2);
   printf("\na: %d, b: %d\n", a, b);

   *intPtr2 = 10;
   printf("\nAfter assigning '10' into the memory location pointed to by intPtr1 and intPtr2");
   printf("\nintPtr1: %d, *intPtr1: %d", intPtr1, *intPtr1);
   printf("\nintPtr2: %d, *intPtr2: %d", intPtr2, *intPtr2);
   printf("\na: %d, b: %d\n", a, b);

   b = 25;
   printf("\nAfter assigning '25' into the variable 'b'");
   printf("\nintPtr1: %d, *intPtr1: %d", intPtr1, *intPtr1);
   printf("\nintPtr2: %d, *intPtr2: %d", intPtr2, *intPtr2);
   printf("\na: %d, b: %d\n", a, b);
}

Pointer as function argument[edit | edit source]

This example shows how pointers can be used in functions. The code gives the reader some questions to consider and answer on their own.

#include <stdio.h>

/*Example showing how pointers are used in functions.
 */

/* Only swaps the values within this fuction */
void badSwap(int c, int d);
void goodSwap(int *c, int *d);
void decrement(int *p);
void increment(int *p);

void badSwap(int c, int d) {
   int temp = 0;

   printf("\nbadSwap");

   printf("\n\tBefore swapping in badSwap");
   printf("\n\ttemp: %d, c: %d, d: %d", temp, c, d);

   temp = c;
   c = d;
   d = temp;

   // Why won't this work?
   printf("\n\tAfter swapping in badSwap");
   printf("\n\ttemp: %d, c: %d, d: %d", temp, c, d);
}

/* Swaps the values using pointers*/
void goodSwap(int *c, int *d) {
   int temp = 0;

   printf("\ngoodSwap");

   printf("\n\tBefore swapping in goodSwap");
   printf("\n\ttemp: %d, c: %u, *c: %d, d: %u, *d: %d", temp, c, *c, d, *d);

   temp = *c;
   *c = *d;
   *d = temp;

   // Why does this work?
   printf("\n\tAfter swapping in goodSwap");
   printf("\n\ttemp: %u, c: %u, *c: %d, d: %u, *d: %d", temp, c, *c, d, *d);
}

void badSwap2(int *c, int *d) {
   int *temp = NULL;

   printf("\nbadSwap2");

   printf("\n\tBefore swapping in badSwap2");
   printf("\n\ttemp: %d, c: %u, *c: %d, d: %u, *d: %d", temp, c, *c, d, *d);

   temp = c;
   c = d;
   d = temp;

   // c and d have changed values! Great! Why won't their values be changed when
   // we return?
   printf("\n\tAfter swapping in badSwap2");
   printf("\n\ttemp: %u, c: %u, *c: %d, d: %u, *d: %d", temp, c, *c, d, *d);
}



/*Decrements the value pointed to by p */
void decrement(int *p) {
   // What we want *p = *p - 1;
   (*p)--;
   // What does *p-- do?
}


/*Increments the value pointed to by p*/
void increment(int *p) {
   int temp = p;
   // What we want *p = *p + 1
   (*p)++;
   // What does *p++ do?

   printf("\nincrement");
   printf("\n\t p: %u, *p: %u, &p: %u\n", p, *p, &p);

   p = p + 1;
   printf("\n\t p: %u, *p: %u, &p: %u\n", p, *p, &p);
   p = temp;

   p = *p + 1;
   printf("\n\t p: %u, &p: %u\n", p, &p);
   p = temp;

   p = &p + 1;
   printf("\n\t p: %u, *p: %u, &p: %u\n", p, *p, &p);
   p = temp;

   *p = p + 1;
   printf("\n\t p: %u, *p: %u, &p: %u\n", p, *p, &p);
   p = temp;

   *p = &p + 1;
   printf("\n\t p: %u, *p: %u, &p: %u\n", p, *p, &p);
   p = temp;
}

int main(int argc, char *argv[]) {
   int *aPtr;
   int *bPtr;

   int a = 5;
   int b = 10;

   /*Initialize the pointers*/
   aPtr = &a;
   bPtr = &b;

   /*Before call to badSwap*/
   printf("\nBefore call to badSwap");
   printf("\n\taPtr: %u, *aPtr: %d", aPtr, *aPtr);
   printf("\n\tbPtr: %u, *bPtr: %d", bPtr, *bPtr);
   printf("\n\ta: %d, b: %d\n", a, b);

   badSwap(a, b);
   /* After call to badSwap */
   printf("\n\nAfter call to badSwap");
   printf("\n\taPtr: %u, *aPtr: %d", aPtr, *aPtr);
   printf("\n\tbPtr: %u, *bPtr: %d", bPtr, *bPtr);
   printf("\n\ta: %d, b: %d\n", a, b);

   /*Before call to goodSwap*/
   printf("\nBefore call to goodSwap");
   printf("\n\taPtr: %u, *aPtr: %d", aPtr, *aPtr);
   printf("\n\tbPtr: %u, *bPtr: %d", bPtr, *bPtr);
   printf("\n\ta: %d, b: %d\n", a, b);

   goodSwap(aPtr, bPtr);
   goodSwap(&a, &b);

   /* After call to goodSwap */
   printf("\n\nAfter call to goodSwap");
   printf("\n\taPtr: %u, *aPtr: %d", aPtr, *aPtr);
   printf("\n\tbPtr: %u, *bPtr: %d", bPtr, *bPtr);
   printf("\n\ta: %d, b: %d\n", a, b);

   badSwap2(aPtr, bPtr);
   /* After call to badSwap2*/
   printf("\n\nAfter call to badSwap2");
   printf("\n\taPtr: %u, *aPtr: %d", aPtr, *aPtr);
   printf("\n\tbPtr: %u, *bPtr: %d", bPtr, *bPtr);
   printf("\n\ta: %d, b: %d\n", a, b);


   increment(&a);
   return 0;
}

Advanced Pointers[edit | edit source]

This section describes how to use pointers to access dynamically allocated memory, point to structures, point to other pointers, point to functions, and point to void.

Pointers with Malloc[edit | edit source]

This example show how pointers can be used with malloc to point to blocks of memory.

#include <stdlib.h>
#include <stdio.h>

#define NUM_ALPHA 26

typedef unsigned char uchar;
typedef unsigned int uint;

/*
 * Fills the data pointed to by cPtr with the alphabet.
 */
void fillWithAlpha(uchar *cPtr, int size);

void printDataAsChar(uchar *cPtr, int size);

/* This function shows how to malloc data. It shows that data might
 * be zero upon initialization, but it is not guaranteed to be 0.
 * */
void mallocExample();

/* This function shows malloc data can be passed to a function.
 */
void mallocWithFunction();

int main(int argc, char **argv) {
	mallocExample();
	mallocWithFunction();
	return 0;
}

void mallocExample() {
	/* Using malloc */
	uint size, i;
	printf("\n Enter the desired size: ");
	scanf("%d", &size);
	uchar *data;
	/* Check to make sure a NULL pointer wasn't returned from malloc */
	if ((data = (uchar*) malloc(size)) == NULL)
		exit(1);

	/* Raw data after first malloc. Not guaranteed to be initialized*/
	printf("\n After first malloc");
	for (i = 0; i < size; i++)
		printf("\n [%d]: %d", i, data[i]);

	/* Fill */
	for (i = 0; i < size; i++)
		data[i] = i;

	/* After fill */
	printf("\n After fill");
	for (i = 0; i < size; i++)
		printf("\n [%d]: %d", i, data[i]);

	/* When data is no longer needed it MUST be freed */
	free(data);

	if ((data = (uchar*) malloc(size)) == NULL)
		exit(1);
	/* Raw data after second malloc. Shows how there can be stuff 'left over'
	 * in memory after malloc call*/
	printf("\n Raw data after malloc");
	for (i = 0; i < size; i++)
		printf("\n [%d]: %d", i, data[i]);

	free(data);
}

void mallocWithFunction() {
	uint size;
	printf("\n Enter the desired size: ");
	scanf("%d", &size);
	uchar *data;
	if ((data = (uchar*) malloc(size)) == NULL)
		exit(1);
	printDataAsChar(data, size);
	fillWithAlpha(data, size);
	printDataAsChar(data, size);

}

void fillWithAlpha(uchar *cPtr, int size) {
	int i;
	for (i = 0; i < size; i++)
		cPtr[i] = 'a' + i%NUM_ALPHA;
}

void printDataAsChar(uchar *cPtr, int size) {
	int i;
	for (i = 0; i < size; i++)
		printf("[%d]: %c \n", i, cPtr[i]);
}

Pointers to structs[edit | edit source]

Below is example showing how pointers are used with structures. It creates a classic link list which stores numbers in sorted form.

#include <stdio.h>
#include <stdlib.h>

/* Example showing how pointers are used with structures.
 */

struct LinkList 
{
	int Number;
	struct LinkList *Next;
}

insertLinkList(struct LinkList **ll,int Number)
{
	struct LinkList *llTemp,*llSearch,*llPrev;
	int lastElementFlag=0;

	llTemp=(struct LinkList *)malloc(sizeof(struct LinkList));

	llTemp->Number=Number;
	llTemp->Next=NULL;

if (*ll==NULL)
	/* LinkList is empty */
	*ll=llTemp;
else if ((*ll)->Number>Number)
	{/*insert first element */
	llTemp->Next=*ll;
	*ll=llTemp;
	}

else {
	llSearch=*ll;
	while (llSearch->Number<Number)
	{
		if (llSearch->Next==NULL) 
		{
			lastElementFlag=1;
			break;
		}
		
		llPrev=llSearch;
		llSearch=llSearch->Next;
	}
	if(llSearch->Number==Number)
		printf("Number %d already exists\n",Number);
	else if(lastElementFlag==1)
		/* Insert last element */
	{
		llSearch->Next=llTemp;
	}
	else
		/* Insert between */
	{
		llPrev->Next=llTemp;
		llTemp->Next=llSearch;
	}



	}
}

printLinkList(struct LinkList **ll)
{
	struct LinkList *llTemp;
	llTemp=*ll;
	while (llTemp!=NULL)
	{
		printf("Address is %d and Number is %d \n",llTemp->Next,llTemp->Number);
		llTemp=llTemp->Next;
	}
}


main()
{
struct LinkList *llist=NULL,*llist2=NULL;

printf("Inserting 4\n");
insertLinkList(&llist,4);
printf("Inserting 1\n");
insertLinkList(&llist,1);
printf("Inserting 5\n");
insertLinkList(&llist,5);
printf("Inserting 9\n");
insertLinkList(&llist,9);
printf("Inserting 2\n");
insertLinkList(&llist,2);
printf("Inserting 9\n");
insertLinkList(&llist,9);
printf("Inserting 1\n");
insertLinkList(&llist,1);
printf("Inserting 5\n");
insertLinkList(&llist,5);
printf("Inserting 4\n");
insertLinkList(&llist2,14);
printf("Inserting 1\n");
insertLinkList(&llist2,17);

printf("Printing llist \n");
printLinkList(&llist);
printf("Printing llist2 \n");
printLinkList(&llist2);
}

Double pointer[edit | edit source]

TODO

Void pointer[edit | edit source]

A pointer of type void (e.g void *pointerName) represents the address of an object, but not its type, i.e it points to a variable that can be of any type. With void*, you can cast the type of this pointer to any other type.

#include <stdio.h>

int main () {
   int a = 12;
   void *pointer = &a;
   
   printf("Value *(int *)pointer is: %d \n", *(int *)pointer); //12
   printf("Value (int *)pointer is: %p \n", (int *)pointer); //0x7fff1f05ed4c
   printf("Value pointer is: %p \n", pointer); //0x7fff1f05ed4c
}

Or void pointer can be defined in that way:

void *pointer;
int a = 0;
pointer = &a;

Pointer Arithmetic[edit | edit source]

TODO Pointers and Arrays; Pointer Arithmetic [This section corresponds to K&R Sec. 5.3]

Pointers do not have to point to single variables. They can also point at the cells of an array. For example, we can write

int *ip; int a[10]; ip = &a[3];

and we would end up with ip pointing at the fourth cell of the array a (remember, arrays are 0-based, so a[0] is the first cell). We could illustrate the situation like this:


We'd use this ip just like the one in the previous section: *ip gives us what ip points to, which in this case will be the value in a[3].

Once we have a pointer pointing into an array, we can start doing pointer arithmetic. Given that ip is a pointer to a[3], we can add 1 to ip:

ip + 1

What does it mean to add one to a pointer? In C, it gives a pointer to the cell one farther on, which in this case is a[4]. To make this clear, let's assign this new pointer to another pointer variable: ip2 = ip + 1;

Now the picture looks like this:


If we now do *ip2 = 4;

we've set a[4] to 4. But it's not necessary to assign a new pointer value to a pointer variable in order to use it; we could also compute a new pointer value and use it immediately: *(ip + 1) = 5;

In this last example, we've changed a[4] again, setting it to 5. The parentheses are needed because the unary contents of operator * has higher precedence (i.e., binds more tightly than) the addition operator. If we wrote *ip + 1, without the parentheses, we'd be fetching the value pointed to by ip, and adding 1 to that value. The expression *(ip + 1), on the other hand, accesses the value one past the one pointed to by ip.

Given that we can add 1 to a pointer, it's not surprising that we can add and subtract other numbers as well. If ip still points to a[3], then

*(ip + 3) = 7;

sets a[6] to 7, and *(ip - 2) = 4;

sets a[1] to 4.

Up above, we added 1 to ip and assigned the new pointer to ip2, but there's no reason we can't add one to a pointer, and change the same pointer:

ip = ip + 1;

Now ip points one past where it used to (to a[4], if we hadn't changed it in the meantime). The shortcuts we learned in a previous chapter all work for pointers, too: we could also increment a pointer using ip += 1;

or ip++;


Of course, pointers are not limited to ints. It's quite common to use pointers to other types, especially char. Here is the innards of the mystrcmp function we saw in a previous chapter, rewritten to use pointers. (mystrcmp, you may recall, compares two strings, character by character.)

char *p1 = &str1[0], *p2 = &str2[0];

while(1) { if(*p1 != *p2) return *p1 - *p2; if(*p1 == '\0' || *p2 == '\0') return 0; p1++; p2++; }


The autoincrement operator ++ (like its companion, --) makes it easy to do two things at once. We've seen idioms like a[i++] which accesses a[i] and simultaneously increments i, leaving it referencing the next cell of the array a. We can do the same thing with pointers: an expression like *ip++ lets us access what ip points to, while simultaneously incrementing ip so that it points to the next element. The preincrement form works, too: *++ip increments ip, then accesses what it points to. Similarly, we can use notations like *ip-- and *--ip.

As another example, here is the strcpy (string copy) loop from a previous chapter, rewritten to use pointers:

char *dp = &dest[0], *sp = &src[0]; while(*sp != '\0') *dp++ = *sp++; *dp = '\0';


(One question that comes up is whether the expression *p++ increments p or what it points to. The answer is that it increments p. To increment what p points to, you can use (*p)++.)

Advanced Pointers with Arrays[edit | edit source]

Shows how arrays and pointers can be treated the same. Example: Creating an array structure with pointers. Example: Using pointer arithmetic to index array

Using Pointers to Traverse an Array[edit | edit source]

#include <stdio.h>

int main() {

        /* Create array */
        int arr[10];

        /* Fill with data */
        for (int i = 0; i < 10; i++) {
                arr[i] = i*5;
        }

        /* Create pointer to first item */
        int *ptr = &arr[0];

        /* Iterate through items */
        for (int i = 0; i < sizeof(arr)/sizeof(int); i++) {

                /* Print the address and the value */
                printf("%p: %d\n", ptr, *ptr);

                /* Increment pointer by sizeof(int) */
                ptr++;
        }
        return 0;
}

/* OUTPUT
0x7fff69a4fa20: 0
0x7fff69a4fa24: 5
0x7fff69a4fa28: 10
0x7fff69a4fa2c: 15
0x7fff69a4fa30: 20
0x7fff69a4fa34: 25
0x7fff69a4fa38: 30
0x7fff69a4fa3c: 35
0x7fff69a4fa40: 40
0x7fff69a4fa44: 45
*/

Passing arrays to functions using pointers[edit | edit source]

#include <stdio.h>

void print(int *arr, int p) {
        for (int i = 0; i < p; i++) {
                printf("%p: %d\n", arr, *arr);
                arr++;
        }
}
void multiply(int *arr, int p) {
        for (int i = 0; i < p; i++) {
                *arr *= 5;
                arr++;
        }
}
int main() {
        int arr[10];
        for (int i = 0; i < 10; i++) {
                arr[i] = i;
        }
        printf("Before modification: \n");
        print(&arr[0], sizeof(arr)/sizeof(int));
        multiply(&arr[0], sizeof(arr)/sizeof(int));
        printf("\nAfter modification: \n");
        print(&arr[0], sizeof(arr)/sizeof(int));
        return 0;
}

/* OUTPUT
Before modification: 
0x7fff63f0aa40: 0
0x7fff63f0aa44: 1
0x7fff63f0aa48: 2
0x7fff63f0aa4c: 3
0x7fff63f0aa50: 4
0x7fff63f0aa54: 5
0x7fff63f0aa58: 6
0x7fff63f0aa5c: 7
0x7fff63f0aa60: 8
0x7fff63f0aa64: 9

After modification: 
0x7fff63f0aa40: 0
0x7fff63f0aa44: 5
0x7fff63f0aa48: 10
0x7fff63f0aa4c: 15
0x7fff63f0aa50: 20
0x7fff63f0aa54: 25
0x7fff63f0aa58: 30
0x7fff63f0aa5c: 35
0x7fff63f0aa60: 40
*/

Function Pointers[edit | edit source]