CS50x - Lecture 4 - Memory
Watch on YouTube →
Overview
CS50's Lecture 4 delves into computer memory, explaining hexadecimal notation (base-16) as a human-friendly way to represent memory addresses and byte values, contrasting it with binary and decimal. The lecture introduces pointers, variables that store memory addresses, and demonstrates their use in C for dynamic memory allocation (malloc/free), string manipulation, and file I/O (fopen, fprintf, fclose). It highlights common memory errors like buffer overflows and uninitialized pointers, emphasizing the importance of careful memory management and tools like Valgrind for debugging.
Key takeaways
- Hexadecimal (base-16) is a crucial shorthand for representing memory addresses and byte values, simplifying binary interpretation.
- Pointers (`char *`, `int *`) store memory addresses, enabling dynamic memory allocation (`malloc`), indirect data access (dereferencing `*`), and efficient data manipulation.
- C requires manual memory management: `malloc` allocates memory, `free` releases it; forgetting `free` causes memory leaks.
- Pass-by-value copies arguments; pass-by-reference (using pointers) allows functions to modify original variables, essential for operations like swapping.
- File I/O functions (`fopen`, `fprintf`, `fread`, `fwrite`) allow programs to read from and write to persistent storage, enabling data persistence.
- Buffer overflows occur when writing past allocated memory boundaries (e.g., with `scanf("%s", ...)`), leading to crashes or security vulnerabilities.
Chapters
- Images are stored as grids of pixels, each with a color.
- Low-resolution images can be represented by 0s (black) and 1s (white).
- Modern images use multiple bits per pixel for millions of colors (RGB).
- Hexadecimal uses 16 digits: 0-9 and A-F.
- Each hex digit represents 4 bits (a nibble).
- Two hex digits (e.g., FF) represent 8 bits (1 byte), ranging from 0 to 255.
- Hexadecimal is convenient for representing memory addresses and byte values.
- Addresses are often prefixed with '0x' (e.g., 0x10 represents decimal 16).
- Programmers use hex for readability, not complex calculations.
- The '&' operator retrieves the memory address of a variable.
- The '%p' format specifier in printf displays memory addresses.
- Variable 'n' (value 50) is stored at a specific hexadecimal memory address.
- A pointer is a variable that stores a memory address.
- Declaring a pointer uses an asterisk (e.g., `int *p;`).
- Pointers allow indirect access to data stored at a memory address.
- The '*' operator (dereference) accesses the value at the address stored in a pointer.
- Example: `*p = 50;` stores 50 at the memory location pointed to by `p`.
- Attempting to dereference an uninitialized pointer causes errors.
- In C, strings are null-terminated arrays of characters (`char`).
- A string variable (e.g., `char *s`) is a pointer to the first character.
- String literals like "hi" are stored contiguously in memory.
- Pointer arithmetic allows moving through memory addresses.
- Incrementing a pointer (`s + 1`) moves to the next byte (character).
- Array indexing (`s[i]`) is syntactic sugar for pointer arithmetic (`*(s + i)`).
- The CS50 library's `string` type is a `typedef` for `char *`.
- Standard C uses `char *` for strings, requiring manual memory management.
- Printf's `%s` format specifier expects a `char *` (address of a string).
- Integer comparison (`==`) compares values directly.
- String comparison (`s == t`) compares memory addresses (pointers), not content.
- The `strcmp` function from `<string.h>` compares string content character by character.
- String variables (`char *s`) store the memory address of the first character.
- The actual string data (characters and null terminator) resides elsewhere in memory.
- Each `GetString()` call allocates separate memory for the input string.
- Use `malloc` from `<stdlib.h>` to request memory from the heap.
- `malloc(n)` allocates `n` bytes.
- Calculate required bytes using `strlen(s) + 1` for strings (including null terminator).
- Iterate from `i = 0` up to `strlen(s)` (inclusive of null terminator).
- Copy characters one by one: `t[i] = s[i];`.
- Ensure sufficient memory is allocated for the destination (`t`).
- The `strcpy(destination, source)` function copies a string.
- Requires destination buffer to be large enough.
- Destination argument comes first: `strcpy(t, s);`.
- Functions like `GetString`, `malloc`, and `fopen` can return `NULL` on error.
- `NULL` represents an invalid memory address (typically 0x0).
- Always check return values for `NULL` to prevent crashes and undefined behavior.
- Check `strlen(s) > 0` before accessing `s[0]` to avoid errors with empty strings.
- Input validation prevents unexpected behavior and potential security vulnerabilities.
- CS50's `GetString` handles memory allocation dynamically, avoiding fixed buffer issues.
- Memory allocated with `malloc` must be explicitly released using `free`.
- Failure to `free` memory leads to memory leaks, consuming system resources.
- CS50's `GetString` automatically frees its allocated memory.
- Valgrind is a tool for detecting memory errors (leaks, invalid access).
- Run Valgrind with `./program` to analyze execution.
- Output highlights errors like 'invalid write' and 'definitely lost' bytes, referencing line numbers.
- Incorrect array indexing (e.g., accessing index 3 in a 3-element array) causes invalid writes.
- Forgetting to `free` memory allocated with `malloc` causes memory leaks.
- Valgrind helps pinpoint the source code lines responsible for these errors.
- Heap memory (via `malloc`) is dynamically allocated and managed by the programmer.
- Stack memory is used for function calls and local variables, growing downwards.
- Heap grows upwards, stack grows downwards; collision causes stack/heap overflow.
Summary, takeaways, and chapters were generated by AI from the video's transcript and may contain errors. The video belongs to its creator, CS50.