Arrays, Strings, and Algorithmic Basics
Manipulate collections of data and reason about elementary searching and sorting.
Content
Arrays and Indexing
Versions:
Watch & Learn
AI-discovered learning video
Sign in to watch the learning video for this topic.
Arrays and Indexing (CS50): How to Think Like a Memory Librarian
Imagine a row of lockers labeled 0, 1, 2... You reach for locker 10 and smash into someone else's shoes. Welcome to arrays and indexing in C: small mistakes, big feelings.
This builds on your C toolchain and syntax knowledge from earlier topics like Makefiles, gdb, and style. Compile with debugging symbols (remember your Makefile target with -g and -Wall), and when the program crashes, use gdb to see which locker you tried to open.
What is an array in C, really?
An array is a contiguous block of memory that stores elements of the same type. Think of it as a row of identical lockers, each with an index. The index tells you which locker to open.
Key points
- Indexing starts at 0. The first element is at index 0, not 1. This is the classic off-by-one trap.
- Fixed size at compile time for C-style static arrays (or defined at runtime for dynamically allocated arrays).
- Contiguous memory means arithmetic on indices maps directly to addresses.
#include <stdio.h>
int main(void)
{
int nums[5] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; i++)
printf("nums[%d] = %d\n", i, nums[i]);
}
Micro explanation: nums is the name of the block of lockers. nums[i] is the value you find when you open locker i.
Common errors and how to avoid them
Off-by-one errors
- If you loop
for (int i = 0; i <= 5; i++)you try to open locker 5 when there are only 0..4. That causes undefined behavior.
- If you loop
Out-of-bounds access
- Accessing
nums[5]is undefined. It may seem to work, crash immediately, or corrupt memory later. - When it crashes, use gdb to inspect the faulting line and memory addresses.
- Accessing
Misusing sizeof
sizeof(nums)gives the total bytes for an array but decays when passed to a function. You can compute length safely inside the same scope:
size_t len = sizeof(nums) / sizeof(nums[0]); // only works when nums is an actual array here
Arrays vs. Pointers: the subtle dance
In many contexts arrays decay to pointers to their first element. But they are not identical.
| Feature | Array | Pointer |
|---|---|---|
| Declaration | int a[5]; |
int *p; |
| sizeof | gives total bytes | gives pointer size |
| Modifiable address | no (name is not assignable) | yes |
int a[5];
int *p = a; // ok: a decays to &a[0]
printf("sizeof(a) = %zu\n", sizeof(a)); // 5 * sizeof(int)
printf("sizeof(p) = %zu\n", sizeof(p)); // size of pointer
Micro explanation: array name is like the label on the locker row; pointer is a Post-it note with an address that you can replace.
Strings are just char arrays with manners
A C string is a char array terminated by the null byte \0. That tiny zero means "this is the end".
char name[] = "Ada"; // actually: {'A', 'd', 'a', '\0'}
Important rules:
- Always leave space for the null terminator:
char s[4] = "Ada";is fine,char s[3] = "Ada";is not. - Prefer
fgetstogets.fgets(buffer, sizeof buffer, stdin)prevents buffer overflow. - Use
strlenfromstring.hto get string length (notsizeof).
Example showing buffer caution:
char buf[10];
if (fgets(buf, sizeof buf, stdin))
{
// fgets stores at most sizeof(buf)-1 characters and always NUL-terminates
}
How indexing maps to memory (a little pointer math)
If a is an array of ints and the address of a[0] is 0x1000 and sizeof(int) is 4:
a[0]at 0x1000a[1]at 0x1004a[i]at 0x1000 + 4*i
So *(a + i) is the same as a[i].
int x = a[2]; // same as *(a + 2)
Micro explanation: indexing is sugar for pointer arithmetic.
Debugging tips (leveraging previous lessons)
- Compile with warnings and debug symbols: add
-Wall -Wextra -gin your Makefile target. - If you get a segfault, run
gdb ./yourprog,run, thenbtto get the backtrace. - Print pointer values and array indices in gdb to see which index went out of range.
- Use AddressSanitizer for easier detection: compile with
-fsanitize=address -g.
A lot of mysterious crashes are just attempts to open non-existent lockers. gdb is your detective lamp.
Small reference: patterns to compute array length safely
- Inside the same scope as the array:
int arr[10];
size_t n = sizeof arr / sizeof arr[0];
- If you pass to a function, pass the length explicitly:
void process(int *arr, size_t n);
// call: process(arr, sizeof arr / sizeof arr[0]);
Quick checklist before you push code
- Did you allocate space for the null terminator in strings?
- Are your loops using
< lengthnot<= length? - Did you compile with
-Wall -Wextra -gand test with gdb or ASAN? - Did you avoid using
getsand uncheckedstrcpycalls?
Key takeaways
- Arrays are contiguous blocks of memory indexed from 0. Treat the first element as index 0.
- Indexing past the end is undefined behavior. It may crash now or bite you later.
- Array names can decay to pointers, but they are not interchangeable in all contexts.
sizeofbehaves differently. - C strings are char arrays terminated by
\0. Always account for the terminator. - When in doubt, debug with gdb and sanitize with ASAN. Your future self will send you a thank-you email.
Final memorable insight:
Arrays are predictable shelves. Indexing is how you reach. Respect the shelf boundaries, and your program will return the right book instead of someone else's shoes.
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to comment!