This episode expands C's data type toolkit: one- and multi-dimensional arrays, strings as character arrays, struct, union, and enum as composite types, plus typedef for aliases that clarify code intent and simple data structures.

Basic data types are only enough for simple programs. Real applications need collections of values and more complex structures. Episode 6 covers arrays for storing many values, strings that are arrays of characters, and struct, union, and enum for modeling real-world data.
A key concept to understand: in C, an array is a contiguous block of memory, and the array's name is a pointer to its first element. This understanding will be the bridge to pointers in episode 7 and data structures in episode 9.
With composite types, you can model entities like students, products, or network packets in a single clear data type. This is how C builds large systems from small bricks.
Arrays store many values of the same type in one contiguous block of memory. The declaration int nilai[5] creates an array of 5 ints, with indices from 0 to 4:
cat > array.c <<'EOF'
#include <stdio.h>
int main(void) {
int nilai[5] = {80, 90, 75, 88, 92};
int total = 0;
for (int i = 0; i < 5; i++) {
total += nilai[i];
}
printf("Rata-rata: %d\n", total / 5);
return 0;
}
EOF
gcc -Wall -Wextra array.c -o array && ./arrayThe initialization int nilai[5] = {80, 90, 75, 88, 92} fills the array directly. Elements are accessed by index, e.g. nilai[0] for the first value. Going beyond the array bounds is not caught by the compiler — this is a major source of bugs and an important topic in episode 13.
A two-dimensional array is an array of arrays, like a table with rows and columns:
int matriks[2][3] = {
{1, 2, 3},
{4, 5, 6},
};
for (int baris = 0; baris < 2; baris++) {
for (int kolom = 0; kolom < 3; kolom++) {
printf("%d ", matriks[baris][kolom]);
}
printf("\n");
}Accessing matriks[baris][kolom] uses two indices: the row first, then the column. In memory, elements are stored row by row contiguously — a fact that affects cache performance in episode 15.
In C, a string is a char array terminated by the null character '\0'. Library functions like strlen and strcpy work by counting until they find this null character:
cat > string.c <<'EOF'
#include <stdio.h>
#include <string.h>
int main(void) {
char nama[32] = "Belajar C";
printf("Panjang: %zu\n", strlen(nama));
strcat(nama, " Language");
printf("%s\n", nama);
return 0;
}
EOF
gcc -Wall -Wextra string.c -o string && ./stringstrlen(nama) counts characters up to the null terminator, while strcat appends the second string to the end of the first. The nama[32] buffer must be large enough to hold the concatenated result; otherwise an overflow occurs, which we'll cover in episode 13.
String literals like "halo" are placed in a read-only segment. Modifying a literal's contents through a pointer causes a crash. Use a char[] array if you want to modify its contents. Understand the difference between char *p = "halo", which points to read-only data, and char p[] = "halo", which copies the literal into a local array.
A struct groups several values of different types into a single entity. Here's an example modeling student data:
cat > struktur_data.c <<'EOF'
#include <stdio.h>
enum Semester { GANJIL = 1, GENAP = 2 };
struct Mahasiswa {
char nama[32];
int angkatan;
enum Semester semester;
};
int main(void) {
struct Mahasiswa mhs = {"Arman", 2024, GANJIL};
printf("%s angkatan %d\n", mhs.nama, mhs.angkatan);
return 0;
}
EOF
gcc -Wall -Wextra struktur_data.c -o struktur_data && ./struktur_dataStruct members are accessed with the dot operator: mhs.nama and mhs.angkatan. A struct can contain basic types, arrays, and even other structs. The enum above gives names to sequential constant values.
A union stores several members at the same memory address. Its size is the size of the largest member, and only one member is meaningful at a time. Unions are useful for modeling data that can be one of several different types, such as a protocol message whose type is determined at runtime.
An enum defines a set of named constants that automatically take the values 0, 1, 2, and so on, or can be set explicitly. Using enums makes code more readable than magic numbers. The compiler can also warn about a switch that doesn't handle all enum members.
The typedef keyword creates an alias for an existing type, shortening the writing and clarifying intent:
typedef unsigned long size_byte;
typedef struct Mahasiswa Mahasiswa;After typedef struct Mahasiswa Mahasiswa;, you can write Mahasiswa m; without the struct keyword. An alias like size_byte also hides the type detail so it can be changed in one place.
With a combination of structs, arrays, and pointers, you can build basic data structures. A simple example is an array of structs holding several students sorted by cohort year. Pointers play an important role in dynamic structures like linked lists — a topic we'll cover in depth in episode 9.
Tip
When a struct contains arrays, copying the struct with the assignment operator copies the entire array contents too. For large objects, it's more efficient to pass a pointer to the struct than to copy the whole struct repeatedly.
Key takeaways:
In the next episode 7 we will discuss pointers and memory management — pointer basics, dereferencing, and memory addresses, pointers to arrays, strings, and functions, dynamic memory allocation with malloc, calloc, realloc, and free, and pointer safety and common bugs like use-after-free.