Arrays and Strings in C: Storing Many Values with Tested Programs (C Learning Guide, Part 6)
By Part 5 the packing tool's jobs lived in named functions, but it could still remember only one number at a time. A real day at the packing unit is eight hourly order counts, and eight separate variables named order0 to order7 would be painful to write and impossible to loop over. An array stores many values of the same type under one name, and a string is the same idea applied to text. This part gives the tool a memory.
This is Part 6 of the C Learning Guide series. Part 1 built the first working packing program, Part 2 added variables, types, printf and scanf, Part 3 made decisions with if and else, Part 4 repeated work with loops, and Part 5 named pieces of work with functions. Every program below was compiled and run for this article; the outputs you see are the captured results.
1. Many values under one name: what an array is
An array is a row of variables of the same type, stored side by side, reached through one shared name. Each slot in the row is called an element. The packing unit records orders once per hour. With plain variables you would need order0, order1 and so on, and a loop cannot touch "the next variable name". With an array you write orders once and reach each hour by its position number.
Two facts define every C array. First, all elements share one type: an int array holds only int values. Second, the size is fixed when the array is created: int orders[8] makes room for exactly eight int values, no more and no fewer. To grow or shrink, you make a new array.
- Q: Why use an array instead of many separate variables?
- One name plus a position number means a loop can visit every value. Eight separate variables cannot be counted through, and eighty would be unwritable.
- Q: Can one array hold both int and double values?
- No. A C array has exactly one element type. Mixed data needs separate arrays, or a structure, which this series covers later.
- Q: Does the computer keep the elements together?
- Yes. The elements sit side by side in memory, which is why a position number is enough to find any one of them quickly.
2. Declaring an array and using indexes
Declaring an array names the type, the name and the size in square brackets. You can also give starting values in braces. Each element is then read or written through its index - the position number in square brackets. C counts positions from 0, so the first element is orders[0] and the last of five is orders[4].
#include <stdio.h>
int main(void) {
int orders[5] = {12, 7, 20, 3, 15};
for (int i = 0; i < 5; i = i + 1) {
printf("orders[%d] = %d\n", i, orders[i]);
}
return 0;
}
Verified output:
orders[0] = 12 orders[1] = 7 orders[2] = 20 orders[3] = 3 orders[4] = 15
Think of the array as a row of labeled boxes:
| index | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| orders[index] | 12 | 7 | 20 | 3 | 15 |
The braces fill the row in order: 12 lands at index 0, 7 at index 1 and so on. If you give fewer values than the size, the remaining elements become 0. If you skip the braces completely, as in int a[5]; inside a function, the elements start with indeterminate values - they must never be read before your program writes them. An array declared outside all functions (static storage) is different: its elements start as all zeros.
- Q: Why does C start counting at index 0?
- The index is the distance from the start of the row. The first element is 0 steps away from the beginning, the second is 1 step away, and so on. This matches how the computer actually finds the element.
- Q: What is the last valid index of int a[5]?
- 4. The valid indexes of an array of size n are 0 to n-1. Index 5 of int a[5] is outside the array, and section 7 shows what that mistake costs.
- Q: What does an uninitialized array contain?
- For a local array like
int a[5];inside a function, indeterminate values - the elements must not be read before your program writes them. An array declared outside all functions (static storage) starts as all zeros instead.
3. Walking an array with a for loop
Arrays and counted for loops are made for each other: the loop counter starts at 0, the condition stops at the size, and the body uses the counter as the index. Here is the daily total for the packing unit, adding every hourly count into one sum.
#include <stdio.h>
int main(void) {
int orders[5] = {12, 7, 20, 3, 15};
int sum = 0;
for (int i = 0; i < 5; i = i + 1) {
sum = sum + orders[i];
}
printf("sum = %d\n", sum);
printf("average = %.1f\n", sum / 5.0);
return 0;
}
Verified output:
sum = 57 average = 11.4
Dry run of the adding loop, one line per pass:
| pass | i | orders[i] | sum after the pass |
|---|---|---|---|
| 1 | 0 | 12 | 12 |
| 2 | 1 | 7 | 19 |
| 3 | 2 | 20 | 39 |
| 4 | 3 | 3 | 42 |
| 5 | 4 | 15 | 57 |
The condition is i < 5, not i <= 5. With five elements the legal indexes stop at 4, and the strict less-than is the habit that keeps every walk inside the array. The average divides by 5.0 - a double - so C keeps the fraction, exactly as Part 2 explained for mixed arithmetic.
- Q: Why is the loop condition i < 5 and not i <= 5?
- Indexes run from 0 to 4. With i <= 5 the loop would also run with i = 5, one step past the last element, and C does not stop you from reading there - the result is undefined, covered in section 7.
- Q: Can printf print a whole int array in one call?
- No. There is no format code for a whole array of int. You loop and print element by element. Strings are the one exception, shown in section 5.
- Q: What is sum / 5.0 doing that sum / 5 would not?
- sum / 5 divides two int values and throws away the fraction, giving 11. sum / 5.0 divides an int by a double, so C computes a real quotient, 11.4.
4. The packing tool rebuilt with arrays
Now the tool remembers a full day: eight hourly order counts. One walk adds the total, keeps track of the busiest hour, and counts how many hours crossed the box capacity of 10. All three questions are answered in a single pass.
#include <stdio.h>
int main(void) {
int orders[8] = {4, 9, 7, 12, 6, 10, 3, 8};
int capacity = 10;
int total = 0;
int busiest = 0;
int over = 0;
for (int i = 0; i < 8; i = i + 1) {
total = total + orders[i];
if (orders[i] > orders[busiest]) {
busiest = i;
}
if (orders[i] > capacity) {
over = over + 1;
}
}
printf("total orders today: %d\n", total);
printf("busiest hour: index %d with %d orders\n", busiest, orders[busiest]);
printf("hours above capacity %d: %d\n", capacity, over);
return 0;
}
Verified output:
total orders today: 59 busiest hour: index 3 with 12 orders hours above capacity 10: 1
Two small ideas carry this program. busiest stores an index, not a value: when a bigger count appears, the program remembers where it saw it, so both the hour and its count stay known. And the busiest test uses strict >, so the earliest hour keeps the title when two hours tie.
- Q: Why store the index of the busiest hour instead of its order count?
- The index gives both facts: the position is the hour, and orders[busiest] is the count. Storing only the count loses which hour produced it.
- Q: How would this program read the eight values from the user?
- With the scanf pattern from Part 2 inside a counted loop: read into orders[i] for i from 0 to 7. Fixed values are used here so the verified output is exact.
- Q: What does the variable over count?
- Hours whose orders exceeded the capacity of 10. Only the hour with 12 orders crossed it, so the answer is 1 - the hour with exactly 10 is at capacity, not above it.
5. Strings: an array of char with a hidden stop mark
A string in C is an array of char - one character per element - with a special invisible marker at the end: the null terminator, written '\0'. It is not the letter or digit 0; it is the character with numeric value 0, and every library function that works with text uses it to know where the text stops.
#include <stdio.h>
#include <string.h>
int main(void) {
char name[] = "TechVani";
printf("name = %s\n", name);
printf("strlen(name) = %zu\n", strlen(name));
printf("name[0] = %c\n", name[0]);
printf("name[7] = %c\n", name[7]);
if (name[8] == '\0') {
printf("name[8] holds the hidden terminator '\\0'\n");
}
return 0;
}
Verified output:
name = TechVani strlen(name) = 8 name[0] = T name[7] = i name[8] holds the hidden terminator '\0'
The layout in memory is nine slots for eight visible letters:
| index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|---|
| name[index] | T | e | c | h | V | a | n | i | \0 |
strlen counts only the visible characters and stops before the terminator, so it returns 8. But the array really holds 9 chars: sizeof(name) would say 9. Strings are the exception to the printf rule from section 3 - %s prints a whole string in one call, because printf can simply walk characters until it meets '\0'.
- Q: What marks the end of a C string?
- The null terminator '\0', a character with value 0. Functions like strlen and printf with %s read characters until they reach it, which is why every proper string must have one.
- Q: What is the difference between 'a' and "a"?
- 'a' is one char. "a" is a string - an array of two chars: the letter a and the terminator '\0'. Double quotes always add the terminator for you.
- Q: How many chars does char city[5] = "Pune" really need?
- Five: P, u, n, e and the terminator. It fits exactly. "Pune!" would need six and would not fit, because the terminator needs its own slot.
6. The string toolbox: strlen, strcpy, strcat, strcmp
The header string.h provides the standard helpers. strcpy copies a string into another array, strcat appends one string onto the end of another, strcmp compares two strings letter by letter, and strlen you have already met. The receiving array must always have enough room, including the terminator.
#include <stdio.h>
#include <string.h>
int main(void) {
char a[20] = "Tech";
char b[20];
strcpy(b, a);
printf("after strcpy, b = %s\n", b);
strcat(a, "Vani");
printf("after strcat, a = %s\n", a);
printf("strlen(a) = %zu\n", strlen(a));
if (strcmp(a, b) == 0) {
printf("a and b are equal\n");
} else {
printf("a and b are different\n");
}
if (strcmp("apple", "banana") < 0) {
printf("apple comes before banana\n");
}
return 0;
}
Verified output:
after strcpy, b = Tech after strcat, a = TechVani strlen(a) = 8 a and b are different apple comes before banana
strcmp returns 0 when the strings match exactly, a negative value when the first comes earlier in dictionary order, and a positive value when it comes later. That is why the tests read strcmp(a, b) == 0 for equality and strcmp(x, y) < 0 for ordering. In this run a holds TechVani and b holds Tech, so they are different - and apple does come before banana.
- Q: Why not copy a string with b = a?
- Because arrays cannot be assigned in C - the next section shows the exact compiler error. strcpy does the copy element by element for you.
- Q: What does strcmp return when the strings are equal?
- Exactly 0. Any nonzero value means they differ, and its sign tells you which one sorts first.
- Q: Who must make room for the result of strcpy or strcat?
- You. The destination array must be big enough for every copied character plus the terminator. Here a and b both have room for 20 chars, far more than TechVani needs.
7. Wrong turns with arrays and strings
Assigning one array to another. It looks natural and it does not compile:
#include <stdio.h>
int main(void) {
int a[3] = {1, 2, 3};
int b[3];
b = a;
printf("%d\n", b[0]);
return 0;
}
The real compiler message:
error: assignment to expression with array type
An array's name is not a variable you can re-point; copying means copying every element, by a loop or by strcpy/memcpy.
Comparing strings with ==. This one compiles, runs, and quietly gives the wrong answer:
#include <stdio.h>
int main(void) {
char x[] = "abc";
char y[] = "abc";
if (x == y) {
printf("same\n");
} else {
printf("different\n");
}
return 0;
}
Verified output:
different
x and y hold the same three letters, yet the program prints different. The reason: == on two array names compares where they sit in memory, not what they contain, and x and y are two different rows. Text is always compared with strcmp.
Printing an array name as a number. The compiler warns, and the warning is a glimpse of Part 7:
#include <stdio.h>
int main(void) {
int a[3] = {1, 2, 3};
printf("%d\n", a);
return 0;
}
The real compiler warning:
warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *'
int * means "a pointer to int". Used on its own, an array's name stands for the address of its first element, and an address is what a pointer stores. That idea is the whole of Part 7.
Walking off the end. Reading orders[5] from a five-element array is not checked by C at all: the program may print a leftover value, may look fine today, and may crash tomorrow. There is no guaranteed output, so there is no example run here - the rule is to keep every index inside 0 to size-1, which is why the loop condition is always i < size.
- Q: Does C check whether an index is inside the array?
- No. C performs no bounds check. An out-of-range index is undefined behavior: the program might print garbage, corrupt other values, or crash, and none of those is promised either.
- Q: Why did x == y print different when both hold "abc"?
- Because == between two array names compares their addresses in memory. x and y are separate arrays at separate places, so the addresses differ even when the letters match. strcmp compares the letters.
- Q: What does an array's name mean when used alone?
- It stands for the address of the first element. That is why printf expected a pointer in the last example, and it is the doorway to pointers in Part 7.
8. C programs for practice with solutions
Type each solution yourself and run it before reading the explanation. You can paste any program from this part into the TechVani online compiler and run it there.
Practice 1: find the maximum and where it lives
#include <stdio.h>
int main(void) {
int marks[5] = {23, 5, 67, 12, 9};
int top = 0;
for (int i = 1; i < 5; i = i + 1) {
if (marks[i] > marks[top]) {
top = i;
}
}
printf("max = %d at index %d\n", marks[top], top);
return 0;
}
Verified output:
max = 67 at index 2
The walk keeps top as the index of the best value seen so far. It starts at index 1, because index 0 is the first candidate by default, and strict > keeps the earliest position on a tie.
Practice 2: count the values above a limit
#include <stdio.h>
int main(void) {
int marks[5] = {45, 80, 32, 90, 60};
int limit = 50;
int count = 0;
for (int i = 0; i < 5; i = i + 1) {
if (marks[i] > limit) {
count = count + 1;
}
}
printf("%d values are above %d\n", count, limit);
return 0;
}
Verified output:
3 values are above 50
A counter pattern: start at 0, add 1 on every match. 80, 90 and 60 pass the test against 50, so the answer is 3. The value 50 itself does not count, because the test is strictly above.
Practice 3: reverse a string in place
#include <stdio.h>
#include <string.h>
int main(void) {
char word[] = "table";
int n = strlen(word);
for (int i = 0; i < n / 2; i = i + 1) {
char t = word[i];
word[i] = word[n - 1 - i];
word[n - 1 - i] = t;
}
printf("reversed = %s\n", word);
return 0;
}
Verified output:
reversed = elbat
Two positions walk towards each other: i from the front, n - 1 - i from the back, swapping letters as they go. The loop stops at the middle, so each pair is swapped exactly once. A temporary char holds one side during each swap, the same three-step exchange from Part 2.
Practice 4: is the string a palindrome?
#include <stdio.h>
#include <string.h>
int main(void) {
char word[] = "level";
int n = strlen(word);
int same = 1;
for (int i = 0; i < n / 2; i = i + 1) {
if (word[i] != word[n - 1 - i]) {
same = 0;
}
}
if (same == 1) {
printf("%s is a palindrome\n", word);
} else {
printf("%s is not a palindrome\n", word);
}
return 0;
}
Verified output:
level is a palindrome
A palindrome reads the same forwards and backwards. The same two-pointer walk compares letters instead of swapping them, and one mismatch flips same to 0. level passes; try the word hello and the program prints that it is not a palindrome.
What was checked, and what comes next
All twelve programs in this part were compiled with a C17 compiler with all warnings on, and every output block above is the captured output of a real run, including the compiler error and warning quoted in section 7. Next in the series: pointers - the addresses behind array names, so a function can change a caller's variable and walk an array by position. Continue with the series index, or browse the Exam Notes.
Sources and scope
- cppreference: arrays for declaration, indexing and initialization.
- cppreference: null-terminated strings for the character array with the stop mark.
- cppreference: string.h functions for strlen, strcpy, strcat and strcmp.
- cppreference: subscript operator for what the square brackets do.
Programs were tested on 64-bit Linux with gcc, where int is 32 bits and char is one unit. String function behavior is standard C and matches on other systems.
Comments
Post a Comment