Pointers in C / C++

Pointers (c and c++)

The Basics and Pitfalls of Pointers in C | Hackaday

 

single indirection

Define a Pointer int *intPtr

 

* – indirection operator or de-referencing operator

 

&varable – Memory address to variable

*intPtr – Dereference pointer (indirection), pull literal value located at the memory address of the pointer

 

Pointer Example

// Declare Variable
int value1 = 5000;
// Declare 3 pointers
int *ptr1;
int *ptr2;
int *ptr3;

// Assign address of variable to ptr1
ptr1 = &value1;
// Assign address of ptr1 to ptr2 then ptr2 to ptr3
ptr2 = ptr1;
ptr3 = ptr2;

// Print literal value stored in value1
printf("value1: %d\n\r", value1);
printf("ptr1:   %d\n\r", *ptr1);
printf("ptr2:   %d\n\r", *ptr2);
printf("ptr3:   %d\n\r", *ptr3);

/*
Output
value1: 5000
ptr1:   5000
ptr2:   5000
ptr3:   5000
*/

// Set Literal value to the memory address in ptr3, setting the value of value1 to 4000
*ptr3 = 4000;

// Print literal values
printf("\n\rAssign 4000 to *ptr3\n\r");
printf("value1: %d\n\r", value1);
printf("ptr1:   %d\n\r", *ptr1);
printf("ptr2:   %d\n\r", *ptr2);
printf("ptr3:   %d\n\r", *ptr3);

/*
Output:
Assign 4000 to *ptr3
value1: 4000
ptr1:   4000
ptr2:   4000
ptr3:   4000
*/

 

Pointer Math

Adding 1 to will increment the pointer address the size of the value type (ie int, char etc)

 

int c[100];
int *pc;

pc = c;

pc = pc + 1 // move form c[0] to c[1] or add 4 bytes

 

Multiple indirection

int **ptrptr; – pointer to a pointer (multiple indirection) to an int, two asterisks`

** – Can be used to dereference a pointer to a pointer printing the value in the original pointer

 

int orig;
int *ptr;
int **ptrptr;

ptr = &orig;
ptrptr = &ptr;

// print address of ptr stored in ptrptr
printf("%p", ptrptr);
// print value of ptr which is referced by ptrptr
printf("%d", **ptrptr);

 

 

void pointer

void *ptr; // ptr is a void pointer

 

void pointer, also known as the generic pointer, is a special type of pointer that can be pointed at objects of any data type! A void pointer is declared like a normal pointer

 

void pointer does not know what type of object it is pointing to, it cannot be dereferenced directly! Rather, the void pointer must first be explicitly cast to another pointer type before it is dereferenced.

 

C++

int *intPtr = static_cast<int*>(voidPtr); // however, if we cast our void pointer to an int pointer... `

 

C

int *intPtr = (int *)(voidPtr); // however, if we cast our void pointer to an int pointer...

 

->, parenthesis, and structs

Also see structs in C Notes

 

(From Hack-A_Day Article)

// regular struct member assignment
s.some_member = value;
// dereferencing void pointer, will result in compiler error
foo->some_member = value;
// cast before dereferencing void pointer, this is okay
((struct something *) foo)->some_member = value;
// dereferencing explicit pointer type, no problems here
bar->some_member = value;

 

Note the arrow operator -> when dereferencing a struct (or union) to access its members. This is a shortcut C offers and is identical to (*variable).member. Beware though that (*variable).member is not the same as *variable.member. The first, enforced by the parentheses, dereferences the pointer *before* accessing member, while the second dereferences a pointer-type member inside the struct. This is naturally an easy source for errors, which the arrow operator helps us to prevent.

 

Structure pointer member can also be accessed using operator.

 

(*personPtr).age is same as personPtr->age
(*personPtr).weight is same as personPtr->weight

 

memory management

 

  • stack is cleared when function exists (local variables)
  • heap remains after the function exits (global memory)

 

sizeof() – returns size of the data in bytes

 

structs

Struct may allocated more then the needed memory for the structure. This will depend on the order of the variables. It will pad out smaller variables to align to the larger variables

 

efficient order:

typedef struct {
	int a;				// 4 bytes
	int c;				// 4 bytes
	double b;			// 8 bytes
	long long int d;	// 8 bytes
} MYSTRUCT;			// 24 bytes allocated

 

inefficient order:

typedef struct {
	int a;				// 4 bytes + 4 empty padding bytes
	double b;			// 8 bytes
	int c;				// 4 bytes + 4 empty padding bytes
	long long int d;	// 8 bytes
} MYSTRUCT;			// 32 bytes allocated

 

use sizeof() on types when using malloc() or calloc() or any static memory allocations.

 

Memory Allocation

 

malloc(size) – allocated memory on global the heap

Example: s = (char *)malloc(6)

 

calloc(number of array elemant, sizeof each element) – clears memory ahead of allocation

Example: s = (char *)calloc(6, sizeof(char))

 

  • Calloc/malloc return
    • Generic pointer (void) on success
    • NULL pointer (0) on failure

 

Misc

free(*) – returns memory to the heap

realloc(*, size) – resize memory allocation

 

linked list

  • used to allocate variable size sets of data
  • can not be integrated through using pointer math, each element in the set contains the address of the next item in the set

 

Single linked list

This creates a sequential linked list of items, can only be read in order

typedef struct listitem {
	struct listitem *nextitem;
	int data;
} LISTITEM;

int main(void) {
	LISTITEM *listhead, *temp;

	listhead = NULL; // Set the end of the list
	
	for (int i = 0; i < 3; i++) {
		temp = malloc(sizeof(LISTITEM)); 	// allocate memory for list item
		temp->data = i; 					// set value
		temp->next = listhead; 				// set next to value of previous elemet
		listhead = temp;					// add element to the  start (head) of the list
	} 

 

double linked list

 

  • In this code, the most recently added item is added to the beginning of list list
  • list can be read in any direction
typedef struct listitem {
	struct listitem *nextitem; // item before current item
	struct listitem *previtem; // item after current item
	int data;
} LISTITEM;

int main(void) {
	LISTITEM *temp, *head;

	// define end of list
	head.nextitem = (LISTITEM*)&head; 		// Last added item
	head.previtem = (LISTITEM*)&head; 		// first added item
	head.data = -1;
	
	for (int i = 0; i < 3; i++) {
		temp = malloc(sizeof(LISTITEM)); 	// allocate item in list
		temp->data = i; 					// set data in item
		temp->nextitem = head.nextitem;		// set nextitem in new element to previous item in list
		head.nextitem = temp;				// set head.nextitem to address of new item on list	
		temp->previtem = &head;				// set previtem of new item to address of head
		temp->nextitem->previtem = temp;	// set previtem of previous item to temp
	} 

Data structures

  • queue (FIFO structure)
  • stack
    • Accessed in a LIFO order
      • usually sized size
    • Items are pushed or pop’ed off a stack

 

function pointers

Declared like the following, the types in the definition prototype should match the functions definition.

 

int test(int blah) {
..
}

int main() {
	int (*funtionptr)(int);
	functionprt = test;
}

It’s common to typedef the pointer definition to clean the code.

typedef int(*PFI)(int); 
...

PFI functionptr;
functionptr = test;

 

Call by reference

Used when passing a pointer into a function allowing a function to access and/or manipulate the data referenced in the pointer. This allows for a function to not have to return data that is manipulated.

 

When passing a single variable you must reference the address with a &

 

#include <stdio.h>
void inc_count(int *count_ptr) {
	(*count_ptr)++;
}

int main() {
	int count = 0;
	
	while (count < 10) {
		int_count(&count);
	}
return(0);

}

 

When passing an array the array is already a reference to an address.

 

...
// Define function with integer pointer and constant integer arguements
void bubbleSort(int *, const int);

int main() {
    const int arraySize = 10;
		// define and array of values
    int a[arraySize] = {2, 6, 4, 8, 10, 12, 89, 68, 45, 37};
  ...
	  // pass array and arraySize into the function
    bubbleSort(a, arraySize);
	...
}

/* bubbleSort takes used the reference to the array and does processing on it directly with out using return to send the value back */
void bubbleSort(int *array, const int size) {
	...
}

 

You can pass a pointer into a function allowing that function to change the value without passing the value or returning the variable

 

Array of functions

  • You can define an array of functions, below is an example of the array declaration.
  • An function reference is just a reference to the location in memory that function is located.

 

// function proto types
void function1(int);
void function2(int);
void function3(int);

main() {
	// array declaration
	void (*functionarry[3]) (int) = {function1, function2, function3 };

	// Exectute function 3
	choice = 3;
	(*functionarry[choice])(choice);
}

Copying

  • Deep copy – copy memory to new location (duplicate data)
    • More safe
  • shallow copy – two pointers pointer to same memory location (create new instance of data)
    • Less Safe, risk to two changes being made to same memory locations

 

#coding/c #coding/cpp