Tables of Strings
Two-dimensional char arrays as lists of names, why one subscript names a whole string, and sorting a name list with strcmp and strcpy.
One String Is Never Enough
Every program so far held a single string in a single array. Real programs hold lists: the names in a class, the cities on a route, the words in a menu. C's answer reuses something you already have. A string is a char array; a list of strings is an array of char arrays, which is the two-dimensional array of chapter 7 with char as its element type. Exam papers call it a table of strings, and it is where the null terminator and the row-major layout meet.
The Declaration
char city[5][10];
Read it the way chapter 7 taught: five rows, ten columns, 5 * 10 = 50 contiguous bytes. What is new is the interpretation of a row. Each of the five rows is a ten-byte char array, and each holds one string with its own terminator. The second dimension is therefore not "the longest name" but the longest name plus one, the same capacity arithmetic as always, now paid five times over.
Initializing from string literals looks exactly like the list you would write on paper:
row 0: Chennai length 7
row 1: Madras length 6
row 2: Bombay length 6
row 3: Delhi length 5
row 4: Kolkata length 7
city[2][0] is the character B
whole table: 50 bytes
Every row is padded: "Delhi" uses six of its ten bytes and the remaining four are set to '\0', because a partially initialized array has the rest zero-filled. Fifty bytes are spent regardless of what the names cost, which is the trade this structure makes.
One Subscript Names a String
This is the whole lesson in one line: city[i] is a string, city[i][j] is a character. Apply one subscript and you have named a row, which is a char array terminated by '\0', which is exactly what %s and every <string.h> function want. So printf("%s", city[i]) prints a name, strlen(city[i]) measures one, strcmp(city[i], city[j]) compares two, and none of them know or care that the row lives inside a larger table. Apply both subscripts and you have named one char, which is why city[2][0] printed with %c above.
The most common beginner error is printf("%s", city) for the whole table, or printf("%c", city[i]) for a row. Match the number of subscripts to what the conversion wants: %s takes one, %c takes two.
Reading a List
Reading a list is last lesson's bounded scanf inside a counting loop, with the row as the destination:
Type three words and they come back bracketed. The width is 9 because WIDTH is 10: capacity minus one, per row, exactly as for a single buffer. One honest wart, worth knowing before an exam asks: the width has to be written into the format string as a literal, so it cannot be spelled WIDTH - 1 and must be kept in step with the macro by hand. Guarding the read still matters, and now it matters per name, because a loop that stops early leaves the remaining rows uninitialized, and printing one of those with %s is undefined behaviour.
For lines containing spaces, fgets(word[i], WIDTH, stdin) takes a row just as happily, with the same newline to trim.
Sorting the List
The staple exam question is to put a list of names in alphabetical order, and it is chapter 7's bubble sort with two substitutions. The comparison becomes strcmp(...) > 0, because > on two rows would compare addresses. The swap becomes three strcpy calls through a temporary row, because arrays cannot be assigned with =:
Delhi
London
Manchester
Moscow
Paris
temp is declared char temp[WIDTH] so it can hold any row, terminator included, and the three copies are the same swap-through-a-temporary you wrote for integers, spelled in the only way arrays allow. Because strcmp orders by character code, the sort is lexicographic rather than dictionary order: in ASCII every uppercase letter sorts before every lowercase one, so "Zebra" lands before "apple". Examiners like that detail precisely because it looks wrong.
What This Structure Costs
Every row is WIDTH bytes whether it holds "Delhi" or "Manchester", so a table sized for its longest name wastes the difference on all the others, and a name one character too long has nowhere to go. That is the price of storing strings in a rectangle. The fix is an array of pointers, char *name[5], where each element points at a string of exactly its own length; it needs pointers, so it waits for chapter 11. Until then the rectangle is the tool, and sizing it is your job: longest expected name plus one.
Key Takeaways
- A table of strings is a two-dimensional char array:
char city[5][10]is five rows of ten bytes, and the second dimension must be the longest name plus one for its terminator. - Initializing from a list of string literals zero-fills the unused bytes of every row.
- One subscript names a whole string, so
city[i]goes straight to%s,strlen, andstrcmp; two subscripts name one character for%c. - Read a list with the bounded
scanfin a loop,scanf("%9s", word[i])for ten-byte rows, guarding every read because an unfilled row printed with%sis undefined behaviour. - Sort with chapter 7's bubble sort, comparing with
strcmp(a, b) > 0and swapping with threestrcpycalls through a temporary row, because arrays support neither>nor=. strcmporders by character code, so all uppercase sorts before all lowercase in ASCII.- Fixed-width rows waste the difference between the longest name and the rest; the array-of-pointers alternative arrives with pointers in chapter 11.
How did you find this lesson?
Your rating helps us improve the content.
Create an account to track your progress and access interactive exercises. Already have one? Sign in.
Tables of Strings - Quiz
Test your understanding of the lesson.
Practice Exercises
Sort a Name List
Read five names into a table of strings, sort them into alphabetical order with a bubble sort, and print them one per line. The comparison is strcmp and the swap is three strcpy calls through a temporary row, because arrays support neither > nor =. This is the exam's classic string-sorting question.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!