Final Exam Review
Dynamic Memory Allocation
C lets you request memory at runtime using functions like malloc() instead of hardcoding array sizes at compile time. This avoids wasted memory and allows data structures like lists and dynamic arrays.
C has no garbage collection — you must manually free memory when done using free().
malloc(size)
Allocates a block of uninitialized memory.
int *arr = malloc(sizeof(int) * 4);
if (arr == NULL) { return EXIT_FAILURE; }
Use malloc when:
-
The number of elements is determined at runtime (e.g., user input)
-
You're allocating large arrays that may not fit on the stack
-
You're building data structures where elements are created on demand (e.g., linked lists)
-
You need to allocate memory inside a function and return it (or keep using it) after the function ends (without passing a pointer into the function).
calloc(n, size)
Like malloc, but zero-initializes the memory.
int *arr = calloc(4, sizeof(int));
realloc(ptr, new_size)
Changes the size of previously allocated memory.
free(ptr)
Releases memory. Set pointer to NULL afterward to avoid dangling references.
free(arr);
arr = NULL;
sizeof with Pointers
sizeof(ptr) // size of the pointer (e.g. 8 bytes)
sizeof(*ptr) // size of the pointed-to type (e.g. 4 bytes for int)
If you have a pointer to an array, calling sizeof will not give you the size of the pointed to array but instead the size of a pointer.
Pointers and the Heap
A pointer stores the memory address of another variable. When dynamically allocating:
int *ptr = malloc(sizeof(int));
*ptr = 10; // Dereference to assign
- Pointer itself lives on the stack
- Value it points to lives on the heap (since it was malloc'd in this example).
Dynamic Memory for Strings
To handle string input of unknown length:
char buffer[81];
scanf("%80[^
]", buffer);
getchar(); // consume '
'
size_t len = strlen(buffer);
char *copy = malloc(len + 1);
strcpy(copy, buffer);
Scanset
%80[^ ] tells scanf to read up to 80 characters until newline.
- [^...]: match anything except characters inside
Pointers
Pointers are variables that store memory addresses.
Why Use Pointers?
C uses pass-by-value, so variables are copied when passed to functions.
To modify the original variable, pass its address using pointers.
Pointers let us: - Avoid global variables - Share memory between functions - Modify original values in functions
Indirection
Using a pointer to access a value is called indirection.
int count = 7;
int *countPtr = &count;
printf("%d", *countPtr); // prints 7
Pointer Size
Pointers match the CPU register width:
- 32-bit systems: 4-byte pointers
- 64-bit systems: 8-byte pointers
Declaring a Pointer
int *ptr;
All of these are equivalent:
int *ptr;
int* ptr;
int * ptr;
Avoid multiple declarations like:
int *ptr, x; // only ptr is a pointer
Instead:
int *ptr;
int x;
Initializing a Pointer
Always initialize, even to NULL:
int *ptr = NULL;
The Address (&) Operator
Returns the memory address of a variable:
int y = 5;
int *yPtr = &y;
The Indirection (*) Operator
Dereferences the pointer to get the value:
int x = 10;
int *ptr = &x;
printf("%d", *ptr); // prints 10
* means:
- In declarations: declare a pointer
- In expressions: dereference the pointer
const with Pointers
const affects either the data or the pointer:
const double *dPtr; // can't change value pointed to
double *const dPtr; // can't change the pointer itself
const double *const dPtr; // can't change either
When to use const on the pointer
This mainly matters when the pointer refers to something you can iterate over (like an array or buffer):
- if you plan to iterate over the pointer, no
constafter * - if you plan to never iterate (always point to the same element),
constafter *
When to use const on the data
Same idea as const in other languages:
- not changing the underlying value,
conston data type - changing the underlying value, no
conston data type
Incrementing a Pointer
int arr[] = {1, 2, 3};
int *ptr = arr;
++ptr; // now points to arr[1]
Increment moves to the next element, not just next byte.
Pointer increment inside a function only affects the local copy unless passed as a pointer-to-pointer.
sizeof Operator
sizeof is a compile-time operator (not a function) that returns the size (in bytes) of a type or object as a size_t.
int array[20];
printf("%zu", sizeof array); // prints 80 if int is 4 bytes
sizeof(type)requires parentheses.sizeof(variable)does not.
Pointers and sizeof
When used on a pointer, sizeof returns the size of the pointer itself — not what it points to.
int *p;
sizeof(p); // size of pointer (e.g., 8 bytes)
sizeof(*p); // size of int (e.g., 4 bytes)
Arrays decay to pointers when passed to functions, so sizeof no longer gives array size inside a function.
Pointer Arithmetic
Valid operations:
- ++, --
- + and - with integers
- subtracting one pointer from another
When you add n to a pointer, it advances by n * sizeof(type) bytes.
myPtr += 2; // skips 2 elements, not 2 bytes
Subtracting Pointers
int *a, *b;
// suppose a = 0x1000, b = 0x1008, and sizeof(int) = 4
b - a; // result is 2 (elements apart)
Pointing to Arrays
int arr[] = {0, 1, 2};
int *ptr = arr; // same as &arr[0]
Offset Notation
arr[3] == *(arr + 3)
ptr[2] == *(ptr + 2)
Always use parentheses: *(arr + i)
Not: *arr + i (which adds after dereferencing)
void * Pointers
Generic pointer type. Can point to any data type.
void *vp;
int *ip = vp; // allowed with explicit cast
Cannot dereference a void * directly — must cast to a specific type first.
Function Pointers
Functions have addresses just like data.
void bubbleSort(int a[], size_t n, int (*cmp)(int, int));
Calling:
(*cmp)(a, b);
Parentheses around *cmp are required to make it a pointer to a function.
<ctype.h> – Character Classification
Each function takes an int (usually a char cast to int).
Type Checking
isdigit(c)– true if 0–9isalpha(c)– true if a–z or A–Zisalnum(c)– true if letter or digitisxdigit(c)– true if valid hex (0–9, a–f, A–F)
Case Checking and Conversion
islower(c)– true if lowercaseisupper(c)– true if uppercasetolower(c)– converts to lowercasetoupper(c)– converts to uppercase
Whitespace & Control
isblank(c)– space or tabisspace(c)– any whitespace (space, tab, newline, etc.)iscntrl(c)– control characters (e.g., newline, tab)ispunct(c)– punctuation (not space or alphanumeric)
Printable Characters
isprint(c)– any printable char, including spaceisgraph(c)– printable except space
<stdlib.h> – String Conversion
Conversion Functions
strtod(s, endPtr)– string todoublestrtol(s, endPtr, base)– string tolongstrtoul(s, endPtr, base)– string tounsigned long
Behavior:
- Stops at first invalid char
- base = 0 auto-detects:
- 0x or 0X → hex
- 0 → octal
- otherwise → decimal
<stdio.h> – Input/Output
getchar()– reads next char from stdinputchar(c)– writes char to stdoutputs(s)– prints string with newlinesprintf(dest, format, ...)– formats into a stringsscanf(s, format, ...)– parses from stringfgets(s, n, stream)– reads up to n-1 chars, stops at newline or EOF, includes newline
Example (safe input):
char buffer[100];
int value;
if (fgets(buffer, sizeof buffer, stdin)) {
if (sscanf(buffer, "%d", &value) == 1) {
printf("Value: %d\n", value);
}
}
<string.h> – String Handling
Copying and Concatenation
strcpy(dest, src)– copies string including NULstrncpy(dest, src, n)– at most n chars; may not NUL-terminatestrcat(dest, src)– appends src to deststrncat(dest, src, n)– appends at most n chars
Comparing Strings
strcmp(s1, s2)– full comparisonstrncmp(s1, s2, n)– compares first n chars
Searching
strchr(s, c)– first occurrence ofcstrrchr(s, c)– last occurrence ofcstrpbrk(s1, s2)– first match of any char from s2strstr(s1, s2)– first substring matchstrspn(s1, s2)– length of segment with only s2 charsstrcspn(s1, s2)– length of segment with no s2 chars
strtok – Tokenizing
char *token = strtok(str, " ,.-");
while (token != NULL) {
// process token
token = strtok(NULL, " ,.-");
}
- Modifies input string
- Remembers position using internal static storage
- Start new string by calling again with non-NULL
Memory Functions (Raw Byte Handling)
Work on any data, not just strings
memcpy(dest, src, n)– fast copy, no overlap safetymemmove(dest, src, n)– overlap-safe copymemcmp(s1, s2, n)– compares n bytesmemchr(s, c, n)– searches first n bytes forcmemset(s, c, n)– fills memory with bytec
strlen
Returns length of a string excluding the null terminator.
size_t len = strlen("hello"); // 5