An array is a fixed-size block of elements of the same type, laid out one after another in memory. That contiguous layout is the whole story: it is why indexing is instant, why arrays and pointers are so closely related, and why C can hand you the address of element zero and let you find the rest by arithmetic.
Declaring an Array
Element type, name, and a size in brackets:
int scores[5]; // 5 ints
double prices[100]; // 100 doubles
char initials[3]; // 3 chars
The size is the number of elements, and it must be known where the array is declared - usually a literal or a #defined constant. The memory is reserved immediately; nothing is allocated later.
A freshly declared local array contains garbage, not zeros:
The second row may print zeros, may print huge numbers, and may differ between runs or between a debug and a release build. Reading those values is undefined behavior; initialize before you read.
Initializing
Several forms, all useful:
The {0} idiom is the one to remember: any elements you do not list are zero-initialized, so a single zero zeroes the whole array. Designated initializers ([4] = 99) let you set specific positions and leave the rest at zero, which is handy for lookup tables that are mostly empty.
One thing you cannot do is assign an array after the fact:
int a[3] = {1, 2, 3};
int b[3];
b = a; // error: an array is not assignable
memcpy(b, a, sizeof a); // this is how you copy one
Indexing Starts at Zero
The first element is arr[0] and the last is arr[n-1]. There is no arr[n].
Run that last loop and look at the addresses: each is exactly sizeof(int) bytes past the one before. The elements really are laid out end to end:
index 0 1 2 3 4
+--------+--------+--------+--------+--------+
value | 88 | 95 | 75 | 60 | 100 |
+--------+--------+--------+--------+--------+
offset +0 +4 +8 +12 +16 bytes
^
&scores[0], which is also what `scores` decays to
Zero-based indexing is not an arbitrary choice - it makes the index an offset from the start, which is exactly what the address arithmetic needs. scores[i] is defined as *(scores + i); see pointers and arrays.
Finding the Length
C does not store an array's length anywhere at runtime, but the compiler knows it, and sizeof gives you access:
sizeof(arr) / sizeof(arr[0]) is the idiom. Dividing by arr[0] rather than by a hard-coded sizeof(double) means the line keeps working if you change the element type.
The trap: this only works where the array was declared. Pass the array to a function and the parameter is a pointer, so sizeof measures the pointer instead - typically 8 bytes, giving a "length" of 1 or 2. That is why every array-taking function in C also takes a count:
Looping Over an Array
The standard shape is a for loop from 0 while i < n:
Note i < n, not i <= n. With n elements the valid indices are 0 through n - 1, so <= runs one extra pass and reads past the end. Seeding hottest and coldest from temps[0] rather than from 0 is the other habit worth copying - starting a maximum at zero quietly breaks on all-negative data.
Out of Bounds Is Undefined Behavior
This is the part of C that surprises people coming from other languages. There is no bounds checking. None at compile time, none at runtime. arr[10] on a five-element array is not an error - it is an address computation the compiler performs without comment.
int arr[5] = {1, 2, 3, 4, 5};
arr[7] = 99; // writes 8 bytes past the end of the array
int x = arr[-1]; // reads before the start
What happens next is undefined behavior, and its symptoms are unhelpfully varied:
- it appears to work, because the memory it hit was unused;
- another variable changes for no visible reason;
- the program crashes with a segmentation fault, possibly much later;
- the behavior differs between
-O0and-O2.
"Appears to work" is the dangerous one, because it means the bug ships. Three defenses:
- Loop with
i < n. Most overruns are off-by-one errors in a loop condition. - Validate indices that come from input.
- Build with a sanitizer while developing.
gcc -fsanitize=address -g prog.cturns most overruns into an immediate report naming the file, line, and the array involved.
Arrays of Other Types
The same syntax works for any element type, including structs:
The char word[6] = "hello"; line is worth a pause: a C string is just a char array whose last meaningful byte is '\0'. That terminator is why the array needs six slots for five letters.
Variable-Length Arrays, Briefly
C99 allows an array whose size is a runtime value:
int n = get_count();
int buffer[n]; // a variable-length array
Two cautions. The size is fixed once the array is created, so a VLA is not a growable list. And the memory comes from the stack, so a large or attacker-controlled n can overflow it and crash the program - which is why many projects, including the Linux kernel, ban VLAs outright. They are also optional for C11 implementations to support.
When the size is genuinely unknown until runtime, allocate instead:
Note that arr[i] reads identically whether arr is an array or a pointer to allocated memory - the indexing syntax does not care. Full details in dynamic memory.
Frequently Asked Questions
How do you declare an array in C?
Give the element type, a name, and a size in brackets: int scores[5]; reserves room for five ints. The size must be a constant known where the array is declared (outside of variable-length arrays), and the elements start out uninitialized unless you supply values.
How do you find the length of an array in C?
sizeof(arr) / sizeof(arr[0]) - the total byte size divided by the size of one element. It only works in the scope where the array was declared: once the array is passed to a function it has decayed to a pointer and sizeof measures the pointer instead.
What happens if you go past the end of an array in C?
Nothing stops you. C performs no bounds checking, so arr[10] on a 5-element array reads or writes whatever memory sits there. That is undefined behavior: it may print garbage, corrupt another variable, or crash - and it may appear to work until it does not.
How do you initialize all elements of an array to zero in C?
int arr[100] = {0}; - any elements you do not list are zero-initialized, so one zero sets the whole array. int arr[100] = {}; also works in C23. Without any initializer, a local array's contents are indeterminate garbage.