notes (often straight copy and paste, but a bit digested) from various sources including
Portfolio Courses (Daniel Stenberg)
Stack Overflow forums
Pointers (pointer variables) are special variables that store memory addresses rather than values.
If have a variable var,
var gives the value of the variable
&var gives the address of the variable
To declare a pointer variable, use the * operator during declaration, e.g. an integer pointer:
int *p1; = int* p1; = int * p1; (space before or after *)
int* p1, p2; // ptr p1 but p2 not a ptr
int num = 5;
p1 = #
p2 = #
printf("sizeof(int): %d\n", sizeof(int)); // 4 bytes
printf("sizeof(int*): %d\n", sizeof(int*)); // 8 bytes
printf("sizeof(p1): %d\n", sizeof(p1)); // 8 bytes -- cos ptr!
printf("sizeof(p2): %d\n", sizeof(p2)); // 4 bytes -- not a ptr but an int
printf("*p1: %d\n", *p1); // 5
printf("*p2: %d\n", *p2); // COMPILATION ERROR "Indirection requires pointer operand"
Use the & operator to get the address of the variable
int* pc, c; // declare pointers pc and normal variable c, both of type int;
// At this stage, as neither var is initialized,
// pc points to no address or a random address,
// and var c has an address but contains random garbage val
c = 5; // assign value 5 to var c
pc = &c; // assign address of c to pointer pc
Use the * operator to dereference an address stored in a pointer in order to get the value it points to, e.g.
int* pc, c; // declare int pointer pc and int var c
c = 5; // assign value 5 to c
pc = &c; // assign address of c to pointer var pc
printf("%d", *pc); // Output: 5
Key: since pointers store the address of a variable, changing the value of one will change the value of the other.
int* pc, c;
c = 5;
pc = &c;
c = 1;
printf("Address of c: %p\n", &c);
printf("%d", c); // Output: 1
printf("%d", *pc); // Ouptut: 1
*pc = 2;
printf("%d", *pc); // Ouptut: 2
printf("%d", c); // Output: 2
int c, *pc;
pc = c; // ERROR -- pc is an address but c is not
*pc = &c; // ERROR -- &c is address but *pc is not
pc[0] = .. // ERROR -- segmentation fault
pc = &c; // Not an error -- both &c and pc are ADDRESSES
*pc = c; // Not an error -- both c and *pc are VALUES
#include <stdio.h>
int main() {
int c = 5;
int *p1 = &c; // set ptr p to addr of c -- equiv to int *p; p = &c;
// i.e. creating a pointer p (not *p) and assigning &c to it
printf("%d", *p1); // 5
return 0;
}
Declaring an array also creates a pointer to the first element in the array.
int list[10]; // the variable list is a pointer
// to the first element (here integer) in the array
int * p; // p is a pointer. It has the same type as list.
p = list; // Both point to ints.
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // Pointer to the 1st elem of array
ptr[index] // ADDRESS
*ptr[index] // VALUE at the index
printf("Address of arr[0]: %p\n", ptr); // Addr of arr[0]: 0x7fff4f32fd50
return 0;
printf("Sz of int pointer: %lu\n", sizeof(ptr)); // Sz of int pointer: 8
printf("Sz of derefer'd int pointer: %lu\n", sizeof(*ptr)); // Size of deref'd int pointer: 4
int (*ptr2)[5] = &arr;
printf("Sz of ptr2 to int array: %lu\n", sizeof(ptr2)); // 8
printf("Sz of dereferenced pointer to int array: %lu\n", sizeof(*ptr2)); // 20
The address of an array is the address of the 1st element of the array, i.e. for an array x.
&x[0] = &x //
x[0] = *x
// more generally:
&x[i] = x + i
x[i] = *(x+i) // DEREFERENCE the ptr to get VALUE at the address of the pointer
Accessing array w/array pointer
Array subscripts have higher precedence than pointer indirection
int arr[3] = {5, 10, 15};
int n = sizeof(arr) / sizeof(arr[0]); // length of arr = (ttl # bytes) / (# bytes 1st elem)
int (*ptr)[3]; // pointer pts to an arr of 3 int
// SUBSCRIPT HAS HIGHER PRECED so need to ENCLOSE INDIREC OPERATOR
// AND POINTER NAME INSIDE PARENTHESES
ptr = &arr; // assign address of arr[0] to ptr
// ptr = &arr[0] // same as ptr = &arr
for (int i = 0; i < n; i++)
printf("%d ", (*ptr)[i]); // output: 5 10 15
ft_print_arr_sz(&arr); // w/fcn below
void ft_print_arr_sz(int (*arr)[3]) {
printf("%lu ", sizeof(*arr)); // total number of bytes used by arr
}
Dereference pointers to get the value stored at the address a pointer points to, using the * operator.
int matrix[3][5] =
{
{0,1,2,3,4},
{5,6,7,8,9},
{10,11,12,13,14},
};
matrix[1]; // gives 1st elem of 2nd row of this 2D array (5)
&matrix[1]; // gives ptr to 2nd row in the 2D array
printf(" matrix[1]: %zu\n", matrix[1]); // ret addr of 1st elem in the 2nd row
printf(" matrix[1] + 1: %zu\n", matrix[1] + 1); // adds 4 (1 byte = 4 bits) to mem addr
printf(" *(matrix[1] + 1): %zu\n", *(matrix[1] + 1)); // DEREF gets val 5 and adds 1 => prints 6
printf(" &matrix[1]: %zu\n", &matrix[1]); // ret same addr as w/matrix[i] but here, ENTIRE row
printf(" &matrix[1]: %zu\n", &matrix[1] );
printf(" &matrix[1] + 1: %zu\n", &matrix[1] + 1); // pts to ENTIRE row so 5 ints in row 2
// => 5 * 4 bits = 20 added to the addr
// ==> basically points to 3rd row
printf(" *(&matrix[1] + 1): %zu\n", *(&matrix[1] + 1)); // ERROR cos pointing to ENTIRE row
printf("*(*(&matrix[1] + 1)): %zu\n", *(*(&matrix[1] + 1))); // OK! cos now not entire row but 1st elem
int *ptr = &matrix[1] + 1; // way to get around prev error BUT get warning
// get warning: "Incompatible ptr types initializing 'int *' with an
// expression of type 'int (*)[n]'
int *ptr = (int *) (&matrix[1] + 1); // type cast to get rid of warning.
// Be sure to wrap entire (&matrix[1] + 1) and not just &matrix[1]
printf("ptr: %zu\n", ptr); // same mem addr as matrix[1] and &matrix[1]
// but here get 10 because it's pointing to the 3rd row
printf("*ptr: %zu\n", *ptr); // get mem addr that pts to 1st elem in last row
printf("**ptr: %zu\n", **ptr); // get the value (10)
Strings exist in C as string literals, e.g.
char s1[] = "abcdef"; // CHARACTER ARRAY
s1[0] = 'X';
printf("s1: %s\n", s1); // gives "s1: Xbcdef" what is passed here is a POINTER TO THE 1st char of ARRAY
// CONSTANT PTR -- CAN'T CHANGE WHAT IT POINTS TO
sizeof() operator returns the length of the string including the null character.
print("sizeof(s1): %d\n", sizeof(s1)); // "sizeof(s1): 7"
// cos CHAR ARRAY ON STACK so 1 byte/char* (6 char
s1++; // ERROR!
s1 = "new string"; // ERROR! cos an array ("Array type 'char[n]' is not assignable) -- need
String literals are character arrays stored on a stack (it's modifiable).
However, where pointers are stored in memory are not defined in C (we don't know where it's stored and whether we can modify it).
char *s2 = "abcdef"; // POINTER TO 1st CHAR in the string literal
printf("s2: %s\n", s2); // passing POINTER TO 1st CHAR (enough for C to access the rest)
s2[0] = 'X' // ERROR (EXC_BAD_ACCESS) cos trying to modify memory that's can't be mod
print("sizeof(s2): %d\n", sizeof(s2)); // "sizeof(s2): 8" = sz of pointer on the stack! not the arr
s2++; // ARRAY ARITHMETIC in C --
// modifies which char s2 points to (default: 1st char, now next char)
printf("s2: %s\n", s2); // prints "bcdef"
s2 = "new string" // WORKS! cos setting s2 to point to this other string literal
Best way to avoid forgetting this and modifying pointers to literal strings, it's good practice to set such pointers as a constant:
const char *s2 = "abcdef";
s2[0] = 'X'; // now rather than run-time err, get "Read-only var is not assignable"
array = pointer to the 1st elem in the array
&array = pointer to the entire array
This shows up with pointer arithmetic.
int array[5];
array[2] = 5;
printf("array: %zu", array); // PRINT ADDRESS; %zu = potentially very large positive number
// (also %p, which prints in hexadecimal)
printf("&array: %zu", &array); // gives same address as above BUT
// &ARR -> entire arr, ARR -> 1st elem in the arr
printf("array: %zu", array + 1); // int normally takes up 4 bytes, so this adds 4 to the memory addr
printf("array: %zu", array + 1); // as pointer to ENTIRE array --
// if have 5 int values then 5*4 = 20, so adds 20 to the mem addr
In C, arrays are always passed by reference (i.e. as pointers).
When you pass an array to a function, e.g. ft_doSth(arr), the array arr decays into a pointer -- you are passing the address of the first element of the array, NOT a copy of the full array. The rest of the elements of the array are accessed following this calculation:
address(arr[i]) = (start address of array) + i * (size of individual element)
Because you are passing the address, any modification to the array in a function wil also be modified in the calling environment.
There are many different ways (different syntax) of passing arrays as pointers.
For any array a or pointer to a, a[i] is equivalent to *(a + i).
NOTE: you have to pass the size of the array as another parameter to the fcn, as there is no other way for the calling function to determine the size, leading to undefined/erratic behavior.
int sz;
sz = sizeof(x) / sizeof(x[0]); // (# bytes of entire array) / (# bytes of 1st element)
Ways of passing an array to a function
as an array with an undefined size w/i function
// below is equiv to:
// void printArr(int *arr, int n) { // PASSING AS REFERENCE
void printArr(int arr[], int n) { // PASSING AS POINTER
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
}
int main(void) {
int arr[] = {1, 2, 3, 4, 5}; // arr w/no sz info, BUT
printArr(arr, 5); // array auto "DECAYS" to a ptr; still need to pass sz of array
return (0);
}
as sized array
like above, but now the function is
void printArr(int arr[5], int n) { // PASSING AS SIZED POINTER
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
}
...
// main is the same
with pointer notation (*arr)
method more flexible when working w/dynamically allocated arrays
because passing a pointer variable, will modify values in array main
...
void printArr(int *arr, int n) {
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
}
...
// main like above using calling printArr(arr, 5);
#include <stdio.h>
void ft_doSth(int *a) {
...
int i;
printf("%d\n", a[1]; // prints the 2nd elem of the array
for (i = 0; i < 3; i++) {
printf("%d\n", *(a+i);
}
}
int main(void) {
int x[3] = {1, 2, 3};
// all the calls below are equivalent
ft_doSth(&x[0]); // a pointer to an int
ft_doSth(&x); // a ptr to int[3] -- but call is like above equiv to the one above
ft_doSth(x);
return 0;
}
Notice that calling the function this way: foo(&array) does not decay to int * because it's a pointer type, which does not adjust to pointer types.
Likewise, just as you can't pass the actual values/copy of an array to a function, you also can't return arrays. You have to return the address to the array OR pass the address of the array to be worked on:
safest method: caller defines the array, passes the address and size to the fcn that's supposed to modify it:
no need to deallocate memory cos automatic storage duration
void ft_returnArr(const char *arr_src, size_t sz_src int*, char *arr_dest, size_t sz_dest)
...
arr_dest[i] = arr_src[i];
...
}
int main(void) {
...
char src[] = "copy me";
char dest[sz_src];
...
ft_returnArr(src, sz_src, dest, sz_dest);
...
}
dynamically allocate memory inside fcn, which then returns the pointer and size of the array
caller responsible for deallocating memory for returned array
char* ft_returnArr(const char *arr_src, size_t sz_src int*, size_t *sz_dest)
char *arr_dest = malloc(sz_dest); // use heap memory
arr_dest[i] = arr_src[i];
...
return arr_dest;
}
int main(void) {
...
char src[] = "copy me";
char *dest;
...
dest = ft_returnArr(src, sz_src, dest, sz_dest);
// do stuff with dest
free(dest); // need to dellocate memory
}
declare a pointer to an N-element array and do like above
older vers of C expect SOME_SIZE to be a compile-time constant -- i.e. will only work with one array size
have to dereference pointer before applying subscript
char (*ft_returnArr(const char *arr_src, size_t sz_arr))[SOME_SIZE]
{
char (*arr_dest)[SOME_SIZE] = malloc(sizeof *arr_dest);
if (arr_dest)
{
...
(*arr_dest)[i] = ...; // dereferencing the array before subscript
...
}
return arr_dest;
}
int main(void)
{
char src[] = "This is a test";
char (*dst)[SOME_SIZE];
...
dst = ft_returnArr(src, sz_src);
...
printf("%c", (*dst)[j]);
...
}
#include <stdio.h>
void swap(int *n1, int *n2);
int main()
{
int num1 = 5, num2 = 10;
// address of num1 and num2 is passed
swap( &num1, &num2);
printf("num1 = %d\n", num1); // outputs num1 = 10
printf("num2 = %d", num2); // outputs num2 = 5
return 0;
}
void swap(int* n1, int* n2) // passing addresses
{
int temp;
temp = *n1;
*n1 = *n2; // this also swaps num1 and num2 in main
*n2 = temp;
}
#include <stdio.h>
void addOne(int* ptr) {
(*ptr)++; // adding 1 to *ptr -- since ptr and p have same addresses, *p inside main() also changed
}
int main()
{
int* p, i = 10;
p = &i; // stores 10 at p, *p
addOne(p); // pointer passes address to fcn
printf("%d", *p); // 11
return 0;
}
Void pointer
is a type that is not associated with any data type -- it is NOT itself A DATA TYPE
cannot create a variable with type void
cannot dereference a void *
cannot always use pointer arithmetic w/void pointers
char string[] = "String";
char *c = &string[1];
printf("Zc: %c\n", *c); // get t
c = c + 1; // gets next character in memory
printf("Zc: %c\n", *c); // get 'r'
// for void ptrs, need to typecast before deref'ing
void *p;
p = &stringp[1];
p = p + 1;
//printf("%f\n", *p); // ERROR -- CANNOT DEREF A VOID PTR DIRECLY
printf("p: %c\n", *((char*) p)); // TYPECAST -> DEREF -> now get 'r'
but can cast it:
void *p;
*(int*)p = 0xDEADBEEF;
is a placeholder
good when want to store generic memory addresses,
e.g.
when don't really care about what pointing to
int a = 5;
double b = 3;
void *p;
p = &a;
p = &b;
when dynamically allocate a block of memory on the heap
void *p = malloc(sizeof(int));
printf("p -> %p (%x)\n", p, *(int*)p);
free(p); // see man page of malloc: this sys command uses void ptrs
void *refs[NUMREFS}; is an array of void pointers
void modifyValue(int *ptr) {
*ptr = 42; // Modifies the value at the address pointed to by ptr
}
int main() {
int x = 10;
modifyValue(&x); // Pass the address of x
// Now, x will be 42
return 0;
}