Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 4: Advanced Topics

4.1 Pointers and memory management

Pointers let you access and change memory directly. They are one of the most powerful features of C, and you need to understand them to program in C effectively.

4.1.1 Understanding pointers

A pointer is a variable that stores the memory address of another variable.

Python:

x = 10
y = x  # y references the same value as x
print(x, y)  # Output: 10 10

C:

#include <stdio.h>

int main() {
    int x = 10;
    int *ptr = &x;  // ptr stores the address of x
    printf("Value of x: %d\n", x);
    printf("Address of x: %p\n", &x);
    printf("Value of ptr: %p\n", ptr);
    printf("Value pointed to by ptr: %d\n", *ptr);
    return 0;
}

Explanation:

  • int *ptr = &x; declares a pointer ptr that stores the address of x.
  • *ptr dereferences the pointer to access the value stored at the address.
4.1.2 Dynamic memory allocation

Dynamic memory allocation lets you allocate memory while the program runs, with functions such as malloc, calloc, realloc, and free.

Example:

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

int main() {
    int *arr;
    int n = 5;

    // Allocate memory for an array of 5 integers
    arr = (int *)malloc(n * sizeof(int));

    // Check if the memory has been successfully allocated by malloc
    if (arr == NULL) {
        printf("Memory not allocated.\n");
        exit(0);
    } else {
        // Memory has been successfully allocated
        for (int i = 0; i < n; ++i) {
            arr[i] = i + 1;
        }

        // Print the elements of the array
        printf("The elements of the array are: ");
        for (int i = 0; i < n; ++i) {
            printf("%d ", arr[i]);
        }
    }

    // Free the allocated memory
    free(arr);

    return 0;
}

Explanation:

  • malloc allocates a block of memory of the size you ask for and returns a pointer to it.
  • free releases memory that malloc allocated earlier.
4.1.3 Common pointer pitfalls

Used carelessly, pointers can cause several problems:

  • A dangling pointer still points to a memory location that has been freed.
  • A memory leak happens when allocated memory is never freed.
  • A buffer overflow happens when a program writes more data to a buffer than it can hold.

Example of a dangling pointer:

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

int main() {
    int *ptr = (int *)malloc(sizeof(int));
    *ptr = 10;

    printf("Value: %d\n", *ptr);

    free(ptr);
    // ptr is now a dangling pointer
    // printf("Value after free: %d\n", *ptr); // This would lead to undefined behavior

    return 0;
}

4.2 Structs and enums

Structs and enums let you create complex data types in C.

4.2.1 Defining and using structs

A struct is a collection of variables (of different types) under a single name.

Python:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(1, 2)
print(p.x, p.y)  # Output: 1 2

C:

#include <stdio.h>

// Define a struct
struct Point {
    int x;
    int y;
};

int main() {
    struct Point p1;
    p1.x = 1;
    p1.y = 2;

    printf("Point p1: (%d, %d)\n", p1.x, p1.y);

    return 0;
}

Explanation:

  • struct Point defines a structure named Point.
  • p1 is an instance of Point.
4.2.2 Enumerated types

An enum gives names to integral constants, which makes a program easier to read and maintain.

Example:

#include <stdio.h>

// Define an enum
enum Weekday { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

int main() {
    enum Weekday today;
    today = Wednesday;

    printf("Day %d\n", today);  // Output: Day 3

    return 0;
}

Explanation:

  • enum Weekday defines an enumerated type.
  • today is a variable of type enum Weekday.

4.3 File I/O

C handles file input and output through file pointers and functions such as fopen, fclose, fprintf, and fscanf.

4.3.1 Reading from and writing to files

Example:

#include <stdio.h>

int main() {
    FILE *fptr;

    // Writing to a file
    fptr = fopen("file.txt", "w");
    if (fptr == NULL) {
        printf("Error opening file!\n");
        return 1;
    }
    fprintf(fptr, "Hello, World!\n");
    fclose(fptr);

    // Reading from a file
    char buffer[255];
    fptr = fopen("file.txt", "r");
    if (fptr == NULL) {
        printf("Error opening file!\n");
        return 1;
    }
    while (fgets(buffer, 255, fptr)) {
        printf("%s", buffer);
    }
    fclose(fptr);

    return 0;
}

Explanation:

  • fopen opens a file. Mode "w" is for writing and mode "r" is for reading.
  • fprintf writes formatted output to a file.
  • fgets reads a line from a file.
  • fclose closes the file.
4.3.2 Working with binary files

Example:

#include <stdio.h>

int main() {
    FILE *fptr;
    int num = 1234;

    // Writing to a binary file
    fptr = fopen("file.bin", "wb");
    if (fptr == NULL) {
        printf("Error opening file!\n");
        return 1;
    }
    fwrite(&num, sizeof(int), 1, fptr);
    fclose(fptr);

    // Reading from a binary file
    int read_num;
    fptr = fopen("file.bin", "rb");
    if (fptr == NULL) {
        printf("Error opening file!\n");
        return 1;
    }
    fread(&read_num, sizeof(int), 1, fptr);
    printf("Read number: %d\n", read_num);
    fclose(fptr);

    return 0;
}

Explanation:

  • fwrite writes binary data to a file.
  • fread reads binary data from a file.
  • Mode "wb" opens a binary file for writing, and mode "rb" opens one for reading.