Lecture 7: Pointers
Prerequisites: Lecture 4 Arrays (array declaration, indexing, iteration); Lecture 5 Functions (function definition, parameter passing, return values)
1. Overview
- Every variable in C resides at a specific memory address; a pointer is a variable that stores such an address, thereby enabling programmes to manipulate data indirectly rather than only by name.
- After completing this chapter you will be able to declare and dereference pointers, perform pointer arithmetic over arrays, pass addresses into functions so that they may modify the caller's data (call-by-reference), allocate memory dynamically, and use function pointers to write flexible, callback-driven code.
2. Learning Outcomes
Upon successful completion of this chapter, students will be able to:
- Explain what a pointer is, declare pointer variables of various types, and distinguish the declaration operator
*from the dereference operator*. - Use the address-of operator
&and the dereference operator*to read and modify variables indirectly. - Initialise pointers to
NULLand perform defensive checks before dereferencing. - Traverse and manipulate arrays using pointer notation and pointer arithmetic (
ptr++,*(ptr + i)). - Pass pointers to functions to achieve call-by-reference semantics, enabling in-place modification of the caller's variables and arrays.
- Allocate and free heap memory with
malloc()andfree(), checking for allocation failure. - Declare and use pointer-to-pointer variables to achieve an additional level of indirection.
- Declare, assign, and invoke function pointers, and apply them to build simple dispatch mechanisms such as a calculator.
- Combine pointers with bitwise operators to manipulate individual bits of data.
3. Key Terms
| Term (EN) | 中文 | Notes |
|---|---|---|
| pointer | 指针 | A variable whose value is a memory address. |
address-of operator & | 取地址运算符 | Yields the address of its operand; do not confuse with the bitwise AND &. |
dereference operator * | 解引用运算符 | Accesses the value stored at the address held by a pointer; same symbol as the declaration *, but used in a different syntactic position. |
NULL pointer | 空指针 | A pointer that points to nothing; defined in <stdio.h> and <stdlib.h>. Dereferencing NULL causes undefined behaviour. |
| pointer arithmetic | 指针算术 | Adding or subtracting an integer to/from a pointer; the offset is automatically scaled by sizeof the pointed-to type. |
| call by reference | 引用传递 | Passing the address of a variable to a function so that the function can modify the original. C simulates this via pointers. |
malloc() / free() | 动态内存分配 / 释放 | Library functions in <stdlib.h> for heap allocation. Always check the return value of malloc() and always call free() when the memory is no longer needed. |
| pointer to pointer | 二级指针 | A pointer that stores the address of another pointer, e.g. int **pp;. |
| function pointer | 函数指针 | A pointer that stores the address of a function; enables indirect (dynamic) function calls. Syntax: return_type (*name)(param_list);. |
| dangling pointer | 悬空指针 | A pointer that refers to memory that has been freed or has gone out of scope; dereferencing it is undefined behaviour. |
4. Core Concepts
4.1 What Is a Pointer?
In C, every object (variable, array element, structure, etc.) occupies one or more bytes of memory, and each byte has a unique numeric address. A pointer is simply a variable that holds such an address. Because the pointer "points to" the location of another object, it provides an indirect means of accessing or modifying that object's value.
Pointers are fundamental to C for three principal reasons: they enable dynamic memory allocation (requesting memory at run-time), they allow efficient array and string manipulation (traversing data without copying), and they make it possible to pass data between functions by reference (so that a called function can alter the caller's variables).
Minimal example
File: p07_a.c
Build: gcc p07_a.c -o p07_a
Run: ./p07_a
#include <stdio.h>
int main(void) {
int n = 42;
int *ptr = &n; /* ptr holds the address of n */
printf("Value of n : %d\n", n);
printf("Address of n : %p\n", (void *)&n);
printf("Value stored in ptr: %p\n", (void *)ptr);
printf("Value via *ptr : %d\n", *ptr);
return 0;
}
What you should see: The direct value of n (42), the hexadecimal address of n, the same address stored in ptr, and the value 42 obtained by dereferencing ptr.
Takeaway: A pointer stores an address; the dereference operator * retrieves the value at that address.
4.2 Pointer Declaration and the Dereference Operator
A pointer variable is declared by placing the * symbol between the data type and the variable name. The data type specifies what kind of object the pointer will point to. The * token serves double duty: in a declaration it indicates "this variable is a pointer", and in an expression it acts as the dereference (indirection) operator, meaning "give me the value at this address". All three of the following forms are syntactically valid and semantically identical:
int *p; /* asterisk adjacent to name – most common style */
int* p; /* asterisk adjacent to type */
int * p; /* asterisk with spaces on both sides */
When declaring multiple variables on a single line, take care: int *s, t; declares s as a pointer to int and t as a plain int. To declare two pointers, write int *s, *t;.
Minimal example
File: p07_b.c
Build: gcc p07_b.c -o p07_b
Run: ./p07_b
#include <stdio.h>
int main(void) {
int val = 10;
int *ptr;
ptr = &val; /* & obtains the address of val */
printf("val = %d\n", *ptr); /* * dereferences the pointer */
*ptr = 20; /* modify val indirectly */
printf("val = %d\n", val);
return 0;
}
What you should see: val = 10 followed by val = 20.
Takeaway: The address-of operator & yields an address; the dereference operator * follows that address back to the stored value—and can also be used on the left-hand side of an assignment to modify that value.
4.3 Pointer Assignment — Step by Step
To build a reliable mental model it is helpful to trace how memory changes as pointer operations execute. Consider the following sequence:
int num = 42; /* (a) num occupies, say, address 0x1000 and holds 42 */
int *p_num; /* (b) p_num is declared but uninitialised */
p_num = # /* (c) p_num now stores 0x1000 */
printf("%d\n", *p_num); /* prints 42 */
*p_num = 99; /* (d) the int at 0x1000 is overwritten with 99 */
printf("%d\n", num); /* prints 99 */
At stage (a), num holds 42 and p_num contains an indeterminate value. After the assignment p_num = &num in stage (c), p_num holds the address of num. The dereference *p_num therefore evaluates to 42. Finally, writing *p_num = 99 modifies the memory at address 0x1000, so num itself becomes 99. The variable name num and the expression *p_num are, after the assignment, two different ways of referring to the same storage location.
Takeaway: Assigning an address to a pointer creates an alias; any change made through *ptr is immediately visible through the original variable name, and vice versa.
4.4 The NULL Pointer
When a pointer is declared without initialisation its value is indeterminate—it may point to any arbitrary location in memory. Dereferencing such a pointer invokes undefined behaviour, which can crash the programme or silently corrupt data. To guard against this, C provides the constant NULL (defined in <stdio.h> and <stdlib.h>), which represents a pointer that intentionally points to nothing.
Good practice dictates:
- Initialise every pointer to
NULLunless you are assigning a valid address immediately. - Before dereferencing, check whether the pointer is
NULL. - After calling
free(), reset the pointer toNULLto prevent dangling-pointer errors.
Minimal example
File: p07_c.c
Build: gcc p07_c.c -o p07_c
Run: ./p07_c
#include <stdio.h>
int main(void) {
int num = 42;
int *ptr = NULL; /* safe initialisation */
if (ptr == NULL) {
printf("Pointer is NULL. Assigning address...\n");
ptr = #
}
printf("Value pointed to by ptr: %d\n", *ptr);
ptr = NULL; /* reset after use */
return 0;
}
What you should see: A message indicating that the pointer was NULL, followed by Value pointed to by ptr: 42.
Takeaway: Always initialise pointers to NULL and check before dereferencing; this single habit prevents a large class of run-time errors.
4.5 Pointers and Arrays
In C, the name of an array, when used in most expression contexts, decays to a pointer to its first element. That is, given int arr[5];, the expression arr is equivalent to &arr[0]. Consequently, a pointer initialised with ptr = arr; can be used to traverse the entire array.
There are two principal notations for accessing the i-th element through a pointer:
| Notation | Meaning |
|---|---|
*(ptr + i) | Add i units (each of size sizeof(int)) to the base address, then dereference. |
ptr[i] | Syntactic sugar for *(ptr + i); entirely equivalent. |
Both *(ptr + i) and ptr[i] yield the same machine code in virtually all compilers.
Minimal example
File: p07_d.c
Build: gcc p07_d.c -o p07_d
Run: ./p07_d
#include <stdio.h>
int main(void) {
int arr[] = {10, 20, 30};
int *ptr = arr; /* equivalent to &arr[0] */
for (int i = 0; i < 3; i++) {
printf("%d ", *(ptr + i));
}
printf("\n");
return 0;
}
What you should see: 10 20 30
Takeaway: An array name decays to a pointer to its first element; pointer notation *(ptr + i) and subscript notation ptr[i] are interchangeable.
4.6 Pointer Arithmetic
C permits adding or subtracting integer values to/from a pointer. The compiler automatically scales the offset by the size of the pointed-to type. For example, if ptr is an int * and sizeof(int) is 4 bytes, then ptr + 1 advances the address by 4 bytes—exactly one int element. Similarly, ptr++ advances the pointer to the next element, and ptr-- moves it to the previous one.
This mechanism is what makes pointer-based array traversal both natural and efficient. However, the programmer must ensure that the pointer remains within the bounds of the allocated object (or exactly one past the end); out-of-bounds pointer arithmetic is undefined behaviour.
Minimal example
File: p07_e.c
Build: gcc p07_e.c -o p07_e
Run: ./p07_e
#include <stdio.h>
int main(void) {
int arr[] = {10, 20, 30};
int *ptr = arr;
for (int i = 0; i < 3; i++) {
printf("%d ", *ptr);
ptr++; /* advance to next element */
}
printf("\n");
return 0;
}
What you should see: 10 20 30
Takeaway: Pointer arithmetic is scaled by sizeof(*ptr); incrementing an int * advances the address by sizeof(int) bytes, which is precisely one array element.
4.7 Using Pointers to Read Input into an Array
Because scanf() requires the address of the destination variable, pointer notation and scanf() work together seamlessly. Given int *p = arr;, the expression p + i already yields the address of arr[i], so one may write scanf("%d", p + i) without the & operator.
Minimal example
File: p07_f.c
Build: gcc p07_f.c -o p07_f
Run: ./p07_f
#include <stdio.h>
int main(void) {
int arr[5];
int *p = arr;
printf("Enter 5 integers:\n");
for (int i = 0; i < 5; i++) {
scanf("%d", p + i); /* p + i == &arr[i] */
}
printf("You entered:\n");
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i));
}
printf("\n");
return 0;
}
What you should see: The five integers entered by the user, echoed back on a single line.
Takeaway: Since p + i is already an address, scanf needs no additional & when receiving input through pointer notation.
4.8 Pointers as Function Parameters (Call by Reference)
C passes all function arguments by value: the function receives a copy of the argument, so modifications within the function do not affect the original. To allow a function to modify the caller's variable, one passes the address of the variable (using &) and declares the parameter as a pointer. Inside the function, dereferencing the pointer accesses—and can modify—the original data. This idiom is commonly called call by reference (or, more precisely, simulated call by reference).
The general form is:
return_type function_name(data_type *param) {
/* use *param to read or write the original variable */
}
Minimal example — swap two integers
File: p07_g.c
Build: gcc p07_g.c -o p07_g
Run: ./p07_g
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(void) {
int x = 5, y = 10;
printf("Before swap: x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("After swap: x = %d, y = %d\n", x, y);
return 0;
}
What you should see: Before swap: x = 5, y = 10 followed by After swap: x = 10, y = 5.
Takeaway: Passing a pointer to a function gives the function the ability to modify the caller's data; this is the standard C mechanism for returning multiple values or altering arguments in place.
4.9 Dynamic Memory Allocation
All variables declared inside a function reside on the stack and are automatically destroyed when the function returns. When the required amount of memory is not known at compile time, or when data must outlive the function that creates it, the programmer must allocate memory on the heap using malloc() (declared in <stdlib.h>).
malloc(size) returns a void * pointing to a block of size bytes, or NULL if the system cannot satisfy the request. The returned pointer must be cast (or implicitly converted) to the appropriate type. Once the memory is no longer needed, free() must be called to release it; failure to do so causes a memory leak.
A robust allocation pattern is:
int *arr = malloc(n * sizeof(int));
if (arr == NULL) {
fprintf(stderr, "Memory allocation failed!\n");
return 1; /* or exit(1) */
}
/* ... use arr ... */
free(arr);
arr = NULL; /* prevent dangling pointer */
Minimal example
File: p07_h.c
Build: gcc p07_h.c -o p07_h
Run: ./p07_h
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *arr = malloc(5 * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
for (int i = 0; i < 5; i++) {
arr[i] = i * i; /* 0, 1, 4, 9, 16 */
}
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
arr = NULL;
return 0;
}
What you should see: 0 1 4 9 16
Takeaway: Use malloc() for run-time-sized allocations, always check for NULL, and always free() when finished.
4.10 Pointer to Pointer
Just as a pointer stores the address of an ordinary variable, a pointer to a pointer (double pointer) stores the address of another pointer. It is declared with two asterisks: int **pp;. To access the ultimate value, one applies the dereference operator twice: **pp.
Double pointers are used in practice when a function must modify a pointer passed to it (e.g., to re-allocate a buffer), and they also underlie the representation of two-dimensional dynamic arrays.
Minimal example
File: p07_i.c
Build: gcc p07_i.c -o p07_i
Run: ./p07_i
#include <stdio.h>
int main(void) {
int val = 42;
int *p = &val; /* p holds the address of val */
int **pp = &p; /* pp holds the address of p */
printf("val via **pp: %d\n", **pp);
return 0;
}
What you should see: val via **pp: 42
Takeaway: A pointer to a pointer adds one level of indirection; **pp follows two addresses to reach the underlying value.
4.11 Objects versus Functions in C
The C standard classifies every entity as either an object or a function. An object is a named region of storage that holds data (e.g., int, char[], struct, arrays, pointers). A function is a named block of code that performs a computation. Crucially, both objects and functions have addresses, and therefore both can be referenced by pointers. However, the two categories of pointers are distinct: an object pointer (e.g., int *) and a function pointer (e.g., int (*)(int, int)) are not interchangeable.
Takeaway: Understanding that functions, like variables, reside at memory addresses is the key insight that makes function pointers possible.
4.12 Function Pointers
A function pointer is a variable that stores the address of a function. It may then be used to call that function indirectly. The declaration syntax is:
return_type (*pointer_name)(parameter_list);
Note the parentheses around *pointer_name; without them, the declaration would instead describe a function returning a pointer. Assignment is straightforward: pointer_name = function_name; (the function name, without parentheses, decays to a pointer to the function—analogous to an array name decaying to a pointer to its first element). Invocation uses the same call syntax: pointer_name(args).
Function pointers enable callbacks, dispatch tables, and strategy patterns, making code more flexible and modular.
Minimal example
File: p07_j.c
Build: gcc p07_j.c -o p07_j
Run: ./p07_j
#include <stdio.h>
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
int main(void) {
int (*op)(int, int); /* declare function pointer */
op = add;
printf("add(2, 3) = %d\n", op(2, 3));
op = multiply;
printf("multiply(2, 3) = %d\n", op(2, 3));
return 0;
}
What you should see: add(2, 3) = 5 followed by multiply(2, 3) = 6.
Takeaway: Function pointers store the address of a function and allow the target function to be selected at run-time, providing a powerful mechanism for writing generic, extensible code.
5. Worked Examples
Example 1: Reverse an Array In-Place Using Pointers
Problem: Write a function void reverse(int *arr, int size) that reverses the elements of an integer array in-place. For input {1, 2, 3, 4, 5} the array should become {5, 4, 3, 2, 1}.
Steps:
- Maintain two pointers:
leftstarting at the first element andrightstarting at the last. - Swap the values at
*leftand*right. - Advance
leftforward andrightbackward; repeat until the two pointers meet or cross.
Reference code
File: p07_ex1.c
Build: gcc p07_ex1.c -o p07_ex1
Run: ./p07_ex1
#include <stdio.h>
void reverse(int *arr, int size) {
int *left = arr;
int *right = arr + size - 1;
while (left < right) {
int temp = *left;
*left = *right;
*right = temp;
left++;
right--;
}
}
int main(void) {
int arr[] = {1, 2, 3, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
reverse(arr, n);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
Output:
5 4 3 2 1
Why it works: The left and right pointers converge towards the centre. At each iteration the two pointed-to values are swapped via a temporary variable, using pointer dereference for both reading and writing. Because the function receives a pointer to the array (not a copy), the modifications are visible in main().
Example 2: Simple Calculator Using Function Pointers
Problem: Implement a calculator that supports +, -, *, /, and a square-root operator @. The programme reads two double operands and a character operator, then calls the appropriate function through a function pointer. Division by zero should return NAN.
Steps:
- Define five functions:
add,subtract,multiply,divide(each taking twodoubleparameters), andsquare_root(which ignores the second parameter and returnssqrtof the first). - Parse the input; select the correct function and assign it to a function pointer.
- Invoke the function through the pointer and print the result.
Reference code
File: p07_ex2.c
Build: gcc p07_ex2.c -o p07_ex2 -lm
Run: ./p07_ex2
#include <stdio.h>
#include <math.h>
double add(double a, double b) { return a + b; }
double subtract(double a, double b) { return a - b; }
double multiply(double a, double b) { return a * b; }
double divide(double a, double b) {
if (b == 0.0) return NAN;
return a / b;
}
double square_root(double a, double b) {
(void)b; /* unused parameter */
return sqrt(a);
}
int main(void) {
double a, b;
char op;
scanf("%lf %c %lf", &a, &op, &b);
double (*func)(double, double) = NULL;
switch (op) {
case '+': func = add; break;
case '-': func = subtract; break;
case '*': func = multiply; break;
case '/': func = divide; break;
case '@': func = square_root; break;
default:
printf("Unknown operator.\n");
return 1;
}
printf("%.3f\n", func(a, b));
return 0;
}
Output (for input 4.5 + 2.5):
7.000
Output (for input 3.0 @ 4.0):
1.732
Why it works: A single function pointer func is assigned the address of whichever operation matches the user's input. The call func(a, b) then dispatches to the correct function at run-time. For @, only the first operand is used; the second is silently ignored. Note: the square root of 3.0 is approximately 1.732.
Example 3: Sum Array Elements Using Pointer Arithmetic
Problem: Write a function that accepts an array and its size, and returns the sum of its elements using pointer arithmetic (i.e., advancing the pointer with ++ rather than using subscript notation).
Steps:
- Initialise a local pointer to the start of the array.
- Iterate
sizetimes, adding*ptrto an accumulator and advancingptr++. - Return the accumulated sum.
Reference code
File: p07_ex3.c
Build: gcc p07_ex3.c -o p07_ex3
Run: ./p07_ex3
#include <stdio.h>
int sum_array(int *arr, int size) {
int sum = 0;
int *ptr = arr;
for (int i = 0; i < size; i++) {
sum += *ptr;
ptr++;
}
return sum;
}
int main(void) {
int arr[] = {5, 6, 7, 8, 9, 10};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Sum = %d\n", sum_array(arr, n));
return 0;
}
Output:
Sum = 45
Why it works: The pointer ptr starts at the base of the array. On each iteration *ptr reads the current element, and ptr++ advances the pointer by sizeof(int) bytes to the next element. The running total accumulates all six values.
6. Common Mistakes
-
Using an uninitialised pointer: Declaring
int *p;and immediately writing*p = 10;dereferences an indeterminate address, causing undefined behaviour. Always initialise pointers—either toNULLor to a valid address—before use. -
Confusing
*in declarations versus expressions: In the declarationint *p;, the*indicates thatpis a pointer. In the expression*p = 5;, the*dereferencesp. Writingint *p = 5;does not store 5 in the pointed-to location; it attempts to assign the address 5 top, which is almost certainly invalid. -
Forgetting
&when passing to a function that expects a pointer: Callingswap(x, y);instead ofswap(&x, &y);passes copies of the integer values, not their addresses. The compiler may warn about incompatible types; heed such warnings. -
Arithmetic on
void *pointers: The typevoid *carries no size information, sovoid_ptr + 1is not defined by the standard (although some compilers permit it as an extension). Always cast or assign to a typed pointer before performing arithmetic. -
Memory leaks and dangling pointers: Calling
malloc()without a correspondingfree()leaks memory. Conversely, continuing to use a pointer afterfree()accesses memory that may already have been reallocated. Always free dynamically allocated memory and set the pointer toNULLafterwards. -
Declaring multiple pointers on one line incorrectly:
int *a, b;declaresaas a pointer andbas a plainint. To declare two pointers, writeint *a, *b;. -
Ignoring the return value of
malloc(): Ifmalloc()returnsNULL(e.g., due to insufficient memory) and the programme proceeds to dereference the pointer, a segmentation fault will occur. Always check:if (ptr == NULL) { /* handle error */ }.
7. Practice
7.1 Basic
-
P1: Print Variable Value Using Pointer
Declare an integern = 42. Declare a pointer tointand assign the address ofn. Print the value ofnusing the pointer.
Acceptance: Output is42. -
P2: Modify Value via Pointer Write a programme that takes an integer input and modifies its value by tripling it, using a pointer.
Input/Output: Input5→ OutputAfter tripling: 15.
Acceptance: Programme compiles without warnings; output matches for any valid integer input. -
P3: Array Access via Pointer
Declareint arr[] = {3, 6, 9, 12, 15};. Use a pointer and the expression*(ptr + i)in a loop to print each element.
Acceptance: Output is3 6 9 12 15.
7.2 Required
-
P4: Sum Array Elements Using Pointer Arithmetic
Write a function that accepts an array and its size, and returns the sum of its elements usingpointer_name++. Initialise the array with{5, 6, 7, 8, 9, 10}.
Acceptance: Output isSum = 45. -
P5: Reverse Array Using Pointers
Write a functionvoid reverse(int *arr, int size)that reverses an array of 5 integers in-place using pointers.
Input:1 2 3 4 5
Acceptance: Output is5 4 3 2 1. -
P6: Swap Two Values Using Function and Pointers
Write a functionvoid swap(int *a, int *b)that swaps two values using dereferencing. Call it frommain()to swap two user inputs.
Input:3 5
Acceptance: Output is5 3. -
P7: Memory Allocation and NULL Check
Usemalloc()to allocate space for an array of 5 integers. Check forNULL; if allocation fails, print"Memory allocation failed!"and exit with code 1. Otherwise, initialise the array with the square of each index (0, 1, 4, 9, 16), print the values, and free the memory.
Acceptance: Output is0 1 4 9 16. -
P8: Pointer to Pointer
Declare anintvariable with value 42. Create a pointerpto it, then a pointerpptop. Use**ppto print the value.
Acceptance: Output is42.
7.3 Optional
-
P9: Bit Mask with Pointers
Write a programme that takes anunsigned charand a bit position (0–7). Use a pointer and a bitwise operator to toggle the bit at that position. Print the result in hexadecimal.
Input:65 0
Acceptance: Output is0x40(65 is0b01000001; toggling bit 0 yields0b01000000=0x40). -
P10: Simple Calculator Using Function Pointers
Implement a calculator withadd,subtract,multiply,divide, andsquare_root(operator@). Use a function pointer to dispatch the correct function. Handle division by zero withNAN.
Input:4.5 + 2.5→ Output:7.000
Input:3.0 @ 4.0→ Output:1.732
Acceptance: Programme compiles with-lm; output matches the expected results to three decimal places.
8. Mini Task
Goal: Build a small pointer toolkit programme that demonstrates the key concepts of this chapter in a single, cohesive application. The programme shall read an array of integers from the user, display the array, reverse it in-place using a pointer-based function, display the reversed array, and finally compute and print the sum using pointer arithmetic.
Deliverables:
README.md: Instructions for compiling and running the programme, together with sample input and expected output.src/pointer_toolkit.c: The complete source file containingmain(),reverse(), andsum_array().report.md: A short reflection (150–300 words) describing one difficulty encountered during implementation and how it was resolved.
9. Further Reading
- Kernighan, B. W. & Ritchie, D. M., The C Programming Language, 2nd edn (Prentice Hall, 1988): Chapter 5 — "Pointers and Arrays".
- King, K. N., C Programming: A Modern Approach, 2nd edn (W. W. Norton, 2008): Chapters 11–12 — "Pointers" and "Pointers and Arrays".
- ISO/IEC 9899:2018, Programming Languages — C (C18 Standard): §6.5.3.2 (Address and indirection operators), §6.5.6 (Additive operators / pointer arithmetic), §7.22.3 (Memory management functions).
10. Appendix
A. Quick Reference — Pointer Operators
| Operator | Name | Example | Meaning |
|---|---|---|---|
& | address-of | &x | Yields the memory address of x. |
* (expr) | dereference | *ptr | Yields the value stored at the address in ptr. |
* (decl) | pointer declaration | int *p; | Declares p as a pointer to int. |
++ / -- | pointer increment / decrement | ptr++ | Advances (or retreats) the pointer by one element. |
B. Compilation Notes
Several exercises in this chapter require the mathematics library. When using functions such as sqrt() or the constant NAN, link with -lm:
gcc p07_ex2.c -o p07_ex2 -lm
C. Memory Layout Diagram
Stack (high addresses)
┌─────────────────────┐
│ local variables, │
│ function parameters│
├─────────────────────┤
│ ... │
├─────────────────────┤
│ Heap (malloc/free) │
├─────────────────────┤
│ Global / Static │
├─────────────────────┤
│ Code (Text) │
└─────────────────────┘
(low addresses)
Variables declared inside functions reside on the stack; memory obtained via malloc() resides on the heap. Understanding this distinction is essential when working with pointers and dynamic allocation.