Skip to content
UnderDunn Courses

Arrays, Strings, Pointers, and Memory

Programming FoundationsLesson 7 of 890 minutesLab included
#include <stdio.h>
int main(void) {
int values[5] = {10, 20, 30, 40, 50};
size_t count = sizeof(values) / sizeof(values[0]);
for (size_t index = 0; index < count; index++) {
printf("index=%zu value=%d address=%p\n",
index, values[index], (void *)&values[index]);
}
return 0;
}

Observe address differences. Each element is adjacent, but the numerical difference reflects sizeof(int), not one byte.

Index 5 is outside this five-element array. C does not insert a general runtime bounds check. Reading it creates undefined behavior: the language no longer promises a meaningful result, even when one run appears harmless.

char word[] = "frog";

This array contains five bytes: f, r, o, g, and zero. Functions such as strlen search for that \0 terminator. If a supposed string lacks one within valid storage, the function continues reading beyond the object.

int value = 42;
int *pointer = &value;
printf("value=%d address=%p through-pointer=%d\n",
value, (void *)pointer, *pointer);

&value obtains the address; pointer stores it; *pointer accesses the pointed-to integer. The pointer and integer are different objects with different sizes and jobs.

Inside a function parameter, an array expression usually becomes a pointer. sizeof(parameter) then reports pointer size, not the original array length. Pass lengths explicitly.

Knowledge check

Question 1 of 3

How many bytes does char word[] = "frog" require?

You can now reason about the boundaries that make C powerful and dangerous: contiguous storage, explicit lengths, terminators, and addresses. The C Memory Lab checks this model before we investigate unfamiliar code.