Learning Notes on C Language Pointers

Click to follow the above “Stephen“,

Set as “Starred”, to receive valuable content promptly

Learning Notes on C Language Pointers

Introduction

The pointer is one of the most important and also the most difficult concepts in the C language. It is not only a core feature of C but also the foundation for many advanced programming techniques. This tutorial will start from basic concepts and gradually delve deeper, helping you to comprehensively master the use of pointers in C.

Chapter 1: Basic Concepts of Pointers

1.1 What is a Pointer

A pointer is a variable that holds the address of another variable. In other words, a pointer stores a memory address, allowing us to indirectly access the data stored at that address.

#include <stdio.h>

int main() {
    int num = 42;        // Regular variable
    int *ptr = &num;     // Pointer variable, storing the address of num

    printf("num's value: %d\n", num);
    printf("num's address: %p\n", &num);
    printf("ptr's value (address): %p\n", ptr);
    printf("Value pointed to by ptr: %d\n", *ptr);

    return 0;
}

1.2 Declaration and Initialization of Pointers

The syntax for declaring a pointer variable is:

DataType *pointerVariableName;
int *ptr1;      // Pointer to int type
char *ptr2;     // Pointer to char type
double *ptr3;   // Pointer to double type

Note: Pointer variables themselves also occupy memory space, typically 4 bytes in a 32-bit system and 8 bytes in a 64-bit system.

1.3 Null Pointer

A null pointer is a pointer that does not point to any valid memory address, represented by NULL:

int *ptr = NULL;

if (ptr == NULL) {
    printf("This is a null pointer\n");
}

Benefits of using a null pointer:

  • Can indicate that the pointer has not been initialized
  • Can be used as a function return value to indicate failure
  • Can help avoid dangling pointers

Chapter 2: Pointer Arithmetic

2.1 Arithmetic Operations on Pointers

Pointers support the following operations:

  • Pointer addition: <span>ptr + n</span>
  • Pointer subtraction: <span>ptr - n</span>
  • Pointer increment: <span>ptr++</span>
  • Pointer decrement: <span>ptr--</span>
#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr = arr;  // Pointing to the first element of the array

    printf("Array elements:\n");
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d (address: %p)\n", i, *(ptr + i), (ptr + i));
    }

    return 0;
}

2.2 Operations Between Pointers

Two pointers pointing to the same array can be subtracted, resulting in the number of elements between the two pointers:

int arr[] = {1, 2, 3, 4, 5};
int *ptr1 = &arr[0];
int *ptr2 = &arr[3];

int diff = ptr2 - ptr1;  // Result is 3

Chapter 3: Pointers and Arrays

3.1 Array Names are Pointers

In C, the name of an array is actually a constant pointer to the first element of the array:

int arr[5] = {1, 2, 3, 4, 5};
// arr is equivalent to &arr[0]

3.2 Accessing Array Elements

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;

// The following two access methods are completely equivalent
arr[2] = 10;     // Array method
*(ptr + 2) = 10; // Pointer method

3.3 Two-Dimensional Arrays and Pointers

int matrix[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// Pointer declaration for a two-dimensional array
int (*ptr)[4] = matrix;  // Points to a one-dimensional array containing 4 ints

// Accessing elements
printf("%d\n", matrix[1][2]);     // 7
printf("%d\n", *(*(ptr + 1) + 2)); // 7

Chapter 4: Pointers and Functions

4.1 Pointers as Function Parameters

Passing parameters by pointer allows:

  • Modification of external variable values within the function
  • Improved efficiency in passing large data structures
#include <stdio.h>

// Value passing (does not change external variables)
void swap1(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}

// Pointer passing (changes external variables)
void swap2(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;

    printf("Before swap: x=%d, y=%d\n", x, y);
    swap2(&x, &y);
    printf("After swap: x=%d, y=%d\n", x, y);

    return 0;
}

4.2 Function Pointers

A function pointer can point to a function, used to implement callback mechanisms:

#include <stdio.h>

// Regular function
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

// Function pointer declaration
int (*operation)(int, int);

int main() {
    int result;
    operation = add;
    result = operation(10, 5);
    printf("10 + 5 = %d\n", result);

    operation = subtract;
    result = operation(10, 5);
    printf("10 - 5 = %d\n", result);

    return 0;
}

4.3 Functions Returning Pointers

A function can return a pointer, but care must be taken that the returned pointer does not point to a local variable:

// Incorrect example: returning a pointer to a local variable
int *wrong_function() {
    int local_var = 42;
    return &local_var;  // Dangerous! Local variable is destroyed after function ends
}

// Correct example: returning dynamically allocated memory
int *correct_function() {
    int *ptr = (int *)malloc(sizeof(int));
    *ptr = 42;
    return ptr;
}

Chapter 5: Advanced Applications of Pointers

5.1 Dynamic Memory Allocation

C provides functions for dynamic memory allocation:

  • <span>malloc()</span>: Allocates memory
  • <span>calloc()</span>: Allocates and initializes memory
  • <span>realloc()</span>: Reallocates memory
  • <span>free()</span>: Frees memory
#include <stdio.h>
#include <stdlib.h>

int main() {
    // Allocate memory for an int
    int *ptr = (int *)malloc(sizeof(int));
    if (ptr == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    *ptr = 100;
    printf("Dynamically allocated value: %d\n", *ptr);

    // Free memory
    free(ptr);
    ptr = NULL;  // Avoid dangling pointer

    return 0;
}

5.2 Pointer Arrays and Array Pointers

// Pointer array: array elements are pointers
char *str_array[3] = {"Hello", "World", "C"};

// Array pointer: pointer to an array
int arr[5] = {1, 2, 3, 4, 5};
int (*arr_ptr)[5] = &arr;

5.3 Strings and Pointers

#include <stdio.h>
#include <string.h>

int main() {
    // String literal
    char *str1 = "Hello World";

    // Dynamically allocated string
    char *str2 = (char *)malloc(20 * sizeof(char));
    strcpy(str2, "Dynamic String");

    printf("str1: %s\n", str1);
    printf("str2: %s\n", str2);

    free(str2);
    return 0;
}

Chapter 6: Common Pointer Errors and Debugging

6.1 Dangling Pointers

A dangling pointer points to invalid memory:

int *ptr;
// ptr is a dangling pointer, uninitialized

ptr = (int *)malloc(sizeof(int));
free(ptr);
// ptr becomes a dangling pointer, still pointing to freed memory
ptr = NULL;  // Correct approach

6.2 Null Pointers

A null pointer points to freed memory:

int *ptr = (int *)malloc(sizeof(int));
*ptr = 42;

int *ptr2 = ptr;
free(ptr);
// Now both ptr and ptr2 are dangling pointers

6.3 Boundary Checking for Pointer Arithmetic

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;

// Dangerous operation: out-of-bounds access
*(ptr + 10) = 100;  // May cause program crash or data corruption

6.4 Debugging Techniques

#include <stdio.h>
#include <assert.h>

// Use assertions to check pointer validity
void safe_access(int *ptr) {
    assert(ptr != NULL && "Pointer cannot be null");
    printf("Safe access: %d\n", *ptr);
}

// Print pointer information for debugging
void debug_pointer(int *ptr, const char *name) {
    if (ptr == NULL) {
        printf("%s is a null pointer\n", name);
    } else {
        printf("%s address: %p, value: %d\n", name, ptr, *ptr);
    }
}

Chapter 7: Practical Cases of Pointers

7.1 Implementing Dynamic Arrays

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int *data;
    size_t size;
    size_t capacity;
} DynamicArray;

DynamicArray *create_array(size_t initial_capacity) {
    DynamicArray *arr = (DynamicArray *)malloc(sizeof(DynamicArray));
    arr->data = (int *)malloc(initial_capacity * sizeof(int));
    arr->size = 0;
    arr->capacity = initial_capacity;
    return arr;
}

void push_back(DynamicArray *arr, int value) {
    if (arr->size >= arr->capacity) {
        arr->capacity *= 2;
        arr->data = (int *)realloc(arr->data, arr->capacity * sizeof(int));
    }
    arr->data[arr->size++] = value;
}

void destroy_array(DynamicArray *arr) {
    free(arr->data);
    free(arr);
}

7.2 Implementing Linked Lists

#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node *next;
} Node;

typedef struct {
    Node *head;
    size_t size;
} LinkedList;

LinkedList *create_list() {
    LinkedList *list = (LinkedList *)malloc(sizeof(LinkedList));
    list->head = NULL;
    list->size = 0;
    return list;
}

void append(LinkedList *list, int value) {
    Node *new_node = (Node *)malloc(sizeof(Node));
    new_node->data = value;
    new_node->next = NULL;

    if (list->head == NULL) {
        list->head = new_node;
    } else {
        Node *current = list->head;
        while (current->next != NULL) {
            current = current->next;
        }
        current->next = new_node;
    }
    list->size++;
}

void destroy_list(LinkedList *list) {
    Node *current = list->head;
    while (current != NULL) {
        Node *temp = current;
        current = current->next;
        free(temp);
    }
    free(list);
}

Chapter 8: Summary and Advanced Recommendations

8.1 Review of Key Learning Points

  1. Pointer Basics: Understand the concept, declaration, and initialization of pointers
  2. Pointer Arithmetic: Master arithmetic and comparison operations on pointers
  3. Pointers and Arrays: Understand the relationship between arrays and pointers
  4. Pointers and Functions: Learn to use pointers as parameters and return values
  5. Dynamic Memory: Master memory allocation and deallocation
  6. Common Errors: Avoid dangling pointers, null pointers, and other errors

8.2 Advanced Learning Recommendations

  1. Multi-level Pointers: Learn about pointers to pointers (<span>int **ptr</span>)
  2. Function Pointer Arrays: Implement more complex callback mechanisms
  3. Memory Pools: Learn custom memory management techniques
  4. Smart Pointers: Although C does not have built-in smart pointers, learn implementations in other languages
  5. Data Structures: Deepen your understanding of pointer-based data structure implementations

8.3 Programming Practice Recommendations

  • Write more code and debug frequently
  • Use debugging tools (like gdb) to observe pointer behavior
  • Read excellent C code to learn best practices for pointer usage
  • Participate in open-source projects to apply pointer techniques in practice

Exercises

  1. Write a function that takes an integer array and its length, returning a pointer to the maximum value in the array
  2. Implement a simple string copy function without using library functions
  3. Write a function that takes two string pointers and implements string concatenation
  4. Implement a dynamically sized stack structure using pointers to manage memory

Through these exercises, you will better consolidate your pointer usage skills.

END

Follow Stephen to learn and grow together.

Click“Like” to support us

Leave a Comment