Arrays & Strings
Fixed-size arrays, null-terminated strings, and the functions that operate on them
Quick check before you start: Can you explain what happens when you access
arr[10]on a size-5 array in Java? If you said “ArrayIndexOutOfBoundsException,” you already know the safety net C does not provide. Read on.
After this lesson, you will be able to:
- Declare and initialize C arrays, including zero-filling with
= {0} - Use
sizeof(arr) / sizeof(arr[0])to compute element count and explain why it fails inside functions - Explain that C strings are
chararrays terminated by'\0' - Use
strlen,strcmp,strcpy, andstrcatfrom<string.h> - Read string input safely with
fgetsand strip the trailing newline
Arrays: No Safety Net
In Java, accessing arr[100] on a size-10 array throws ArrayIndexOutOfBoundsException. In C, the same access does… something. Maybe it reads garbage. Maybe it overwrites another variable. Maybe it crashes. C does not check array bounds. Ever.
Declaring Arrays
int scores[5]; // 5 ints, UNINITIALIZED (garbage!)
int grades[5] = {90, 85, 77, 92, 88}; // Initialized
int zeros[100] = {0}; // First element 0, rest auto-filled with 0
Common Pitfall: An uninitialized C array contains whatever garbage data happened to be in that memory. Always initialize your arrays — either with values or with
= {0}to zero-fill.
No .length Property
Unlike Java, C arrays do not know their own size. You must track it yourself:
int grades[5] = {90, 85, 77, 92, 88};
int size = sizeof(grades) / sizeof(grades[0]); // 20 / 4 = 5
for (int i = 0; i < size; i++)
{
printf("%d\n", grades[i]);
}
The sizeof trick divides the total bytes of the array by the size of one element. But this only works where the array was declared — not inside functions that receive the array as a parameter. Inside a function, sizeof(arr) returns the size of a pointer (8 bytes on 64-bit systems), not the array.
Arrays and Functions
When you pass an array to a function, you must also pass its size:
void print_array(const int arr[], int size)
{
for (int i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
From Java: In Java, arrays are objects with a
.lengthfield and bounds checking. In C, arrays are contiguous blocks of memory. No length, no checking, no exceptions. You pass the size as a separate parameter, and you are responsible for staying within bounds.
When you pass an array to a function, the function receives a pointer to the original array, not a copy. Modifying array elements inside the function affects the caller’s array. Use const when a function only reads the array.
Strings: Character Arrays with a Null Terminator
C has no String class. A “string” in C is an array of char that ends with a special byte: '\0' (the null terminator).
char greeting[] = "Hello";
C stores this in memory:
Index: [0] [1] [2] [3] [4] [5]
Value: 'H' 'e' 'l' 'l' 'o' '\0'
The array is 6 bytes — 5 characters plus the null terminator. Every string function scans forward until it hits that '\0'. If it is missing, your program reads off into random memory.
String Library Functions (<string.h>)
strlen — string length (not counting '\0'):
char name[] = "Alice";
printf("%zu\n", strlen(name)); // 5 (not 6!)
strcmp — compare two strings:
if (strcmp(name, "Alice") == 0) // Equal
if (strcmp(name, "Bob") < 0) // name comes before "Bob" alphabetically
Common Pitfall: Never use
==to compare strings in C.if (str1 == str2)compares memory addresses, not contents. Two identical strings stored in different locations would compare as “not equal.” Always usestrcmp(str1, str2) == 0.
strcpy — copy one string to another:
char dest[50];
strcpy(dest, "Hello"); // dest is now "Hello"
strcat — concatenate (append) strings:
char greeting[50] = "Hello, ";
strcat(greeting, "world!"); // greeting is now "Hello, world!"
Both strcpy and strcat do not check whether the destination buffer is large enough. Copying a 100-character string into a 50-character buffer overflows silently.
Safe String Input with fgets
scanf("%s") stops at whitespace and has no overflow protection. Use fgets instead:
char line[100];
fgets(line, sizeof(line), stdin);
fgets reads at most sizeof(line) - 1 characters, always adds '\0', and includes the newline if the line fits. Strip the trailing \n:
line[strcspn(line, "\n")] = '\0';
Java vs. C Strings
| Operation | Java | C |
|---|---|---|
| Declare | String s = "hello"; |
char s[50] = "hello"; |
| Length | s.length() |
strlen(s) |
| Compare | s.equals("hello") |
strcmp(s, "hello") == 0 |
| Copy | s2 = s; |
strcpy(s2, s); |
| Concatenate | s + " world" |
strcat(s, " world"); |
| Read line | scanner.nextLine() |
fgets(s, size, stdin); |
From Java: Java’s
Stringis an immutable object with methods. C’s “string” is a mutablechararray with library functions. There is no garbage collection, no automatic resizing, no bounds checking. You manage the buffer, you check the bounds, you track the null terminator.
char name[5] = "Alice";. What happens when you call printf("%s", name);?"Alice" requires 6 bytes: 5 for the characters plus 1 for the null terminator '\0'. A char[5] array can only hold 5 bytes, so the null terminator is missing. printf("%s") scans forward until it finds a '\0', reading past the array into whatever garbage is in adjacent memory. This is undefined behavior. Use char name[6] or char name[] = "Alice"; to let the compiler allocate the correct size.
What Comes Next
You now know how C handles collections (arrays) and text (null-terminated character arrays). In the next lesson, you will learn how to split programs across multiple files with headers and Makefiles, and how to accept command-line arguments with argc and argv.