Table of Contents
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
- Accessed in a LIFO order
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