APS105 L12 - Pointers

#APS105 Slides Lecture Link to web notes

L01: Recall that computers use bits to store information.

Assuming we have a 64 bit binary number, we can represent the whole numbers using a series of binary numbers. E.g. 00000...101 as a whole number is 5.


Hexadecimal (16 values) instead of binary (2 values)

Writing decimal numbers (the ones we read as humans) as hexadecimal is more convenient than using binary, since it allows us to represent a value with fewer characters. Well, it's not really more convenient, but for memory address representation it is.

Hexadecimal is a system that uses 0-9, as well as 6 other characters, to represent data.

Info

In C, a hex value starts with 0x
e.g. 0x12f3af (This is not important information for tests)

Translating between hexadecimal and decimal

The conversion table between decimal values (0-15 inclusive) to hexadecimal would be as follows:

0 1 ... 9 10 11 12 13 14 15
0 1 ... 9 a b c d e f

Thus, the decimal number 3844, represented in hexadecimal, would be as follows:

1615 1614 1613 1612 ... 162 161 160
0 0 0 0 ... f 0 4

The conversion from hexadecimal to decimal for this number would be as follows:
(f×162)+(4×160)=(15×162)+(4×160)=3844

Note that conversion between decimal, binary, hexadecimal, etc. isn't tested in assessments in this course.

Storing Data in Memory in C

Recall from L01 that memory is stored in byte-sized cells, where each cell is assigned an address.

Pasted image 20250222134024.png|500
In the above image, the value of int x is stored across four different cells, which each store a single byte.


Pointers in C

A pointer is the starting address of a value in memory.

L01: Recall that &variableName is the address-of operator

The result of calling this is the pointer of the data stored at that address in memory.
e.g. &x -> 0x7d004

The data type of a pointer for an int value is an int*, not int. This means that it is "pointing to" an an integer value (i.e., the address).

Example: Creating a valid pointer

int x = 1;

int z = &x; // invalid because &x is a pointer, not an int
int *z = &x; // valid because now z is specified to be a pointer

Example: Changing the value stored at a specific address, using a pointer.

#include <stdio.h>
int main(void) {
	int x = 1;
	int y = 2;
	int *z = &x; // creates a pointer equal to address of x
	*z = 3; // reassigns the value at the address that the pointer is referencing to 3
	printf("%d\n", x); // x now returns value of 3
	printf("%p\n", z); // returns the memory address that z points to
	
	z = &y; // setting the pointer equal to another memory address (that of y)
	printf("%p\n", z); // returns the new memory address that z points to
	return 0;
}
Accessing a value through a pointer is known as dereferencing.

e.g. y = *pointer is known as dereferencing, and printing y results in the value stored at the address that pointer points to.

Whenever we declare a variable with a *, that is a pointer. This means that each time we set a value for the pointer using *z = 105, we are changing the value stored at the address that the pointer is pointing to, rather than the address that the address is pointing to.

Important! Functions can change values of variables through pointers

For variables outside of the scope of the function, the function can still change their values if they do it through pointers. This is because the pointer is referencing an address in memory, rather than an arbitrary variable name—which is localized based on function scope.

Example: Changing the value of a variable using a function, out of scope

#include <stdio.h>
void setThree(int *p) {
	*p = 3;
}

int main(void) {
	int x = 1;
	int *z = &x; // setting a pointer variable equal to address of x
	setThree(z); // changing the value at address of x to 3, even out of scope
	printf("Address: %p\n", (void *) z); // prints the address of z using %p
	printf("%d", x);
	return 0;
}

Understanding the Asterisk * Operator in C

Each use of * when working with a pointer removes a * from the data type.

#include <stdio.h>
int main(void) {
	int x = 1;
	int *pointer = &x;
	int y = *pointer // removes an instance of * from the pointer
	// thus resulting in the value stored at the address (i.e., 1)

	int **newPointer;
	// typeof(newPointer) would be int **
	// typeof(*newPointer) would be int *
	// typeof(**newPointer) would be int
}

Format Specifier of a Pointer

We can also print the address of a pointer by using the format specifier %p.

Example: swap(&a, &b) function swaps the values at addresses of a and b.

#include <stdio.h>
#include <stdlib.h>
void swap(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}

int main(void) {
	int a = 1;
	int b = 2;
	printf("main (before swap) a: %d, b: %d\n", a, b);
	swap(&a, &b);
	printf("main (after swap) a: %d, b: %d\n", a, b);
	return EXIT_SUCCESS;
}