String functions
In the last lesson we wrote our own loop to count a string's length. Everyday jobs like this are already written for you in C's standard library, in <string.h>. #include it first:
1#include <stdio.h>2#include <string.h>3 4int main(void) {5 char word[] = "keyboard";6 printf("%s has %zu letters\n", word, strlen(word));7 return 0;8}
keyboard has 8 letters
This lesson covers the four you'll use most:
| Function | What it does |
|---|---|
strlen(s) | how many characters s has (not counting '\0') |
strcpy(to, from) | copies from into to |
strcat(to, from) | adds from onto the end of to |
strcmp(a, b) | compares a and b: 0 if they're equal |
strlen and sizeof
strlen counts up to the '\0': it's how long the text is. sizeof is how big the array is. They are often different:
1#include <stdio.h>2#include <string.h>3 4int main(void) {5 char name[20] = "Maria";6 printf("strlen: %zu\n", strlen(name));7 printf("sizeof: %zu\n", sizeof name);8 printf("Accent: %zu\n", strlen("café"));9 return 0;10}
strlen: 5 sizeof: 20 Accent: 5
name has 20 bytes and uses only 6 of them (5 letters plus '\0'). strlen counts bytes: é takes 2 bytes in UTF-8, so "café" gives 5.
Copying: strcpy
You can't assign a whole array with = (lesson 11), and strings are no different. This doesn't compile:
1#include <stdio.h>2 3int main(void) {4 char status[10] = "off";5 status = "on";6 printf("%s\n", status);7 return 0;8}
main.c: In function 'main':
main.c:5:12: error: assignment to expression with array type
5 | status = "on";
| ^assignment to expression with array type means you can't assign to an array. To replace what a string holds, use strcpy. It copies the characters over one by one, '\0' included:
1#include <stdio.h>2#include <string.h>3 4int main(void) {5 char status[10] = "off";6 strcpy(status, "on");7 printf("%s\n", status);8 printf("%zu\n", strlen(status));9 return 0;10}
on 2
Mind the order: where to copy to comes first, then where to copy from, the same direction as status = "on".
Joining: strcat
strcat finds the '\0' of the first string and adds the second one from there. A common pattern is to strcpy the first piece in, then strcat the rest one piece at a time:
1#include <stdio.h>2#include <string.h>3 4int main(void) {5 char path[30];6 strcpy(path, "photos");7 strcat(path, "/");8 strcat(path, "beach.jpg");9 printf("%s\n", path);10 return 0;11}
photos/beach.jpg
Press Step and watch the cells of path fill up piece by piece, with the '\0' moving further along each time.
Comparing: strcmp
You can't use == to check whether two strings are the same:
1#include <stdio.h>2 3int main(void) {4 char saved[] = "secret";5 char typed[] = "secret";6 if (typed == saved) {7 printf("Same\n");8 } else {9 printf("Different\n");10 }11 return 0;12}
Different
The letters match, yet it prints Different. == doesn't compare the letters inside; it asks "is this the same array?" These are two arrays, so they are never equal (lesson 17 explains why). To compare the letters, use strcmp: it returns 0 if the two are the same, a negative number if the first comes before the second, and a positive number if it comes after.
1#include <stdio.h>2#include <string.h>3 4int main(void) {5 char saved[] = "secret";6 char typed[] = "secret";7 if (strcmp(typed, saved) == 0) {8 printf("Password correct\n");9 }10 if (strcmp("apple", "banana") < 0) {11 printf("apple comes before banana\n");12 }13 return 0;14}
Password correct apple comes before banana
strcmp compares the codes character by character from the start; the first character that differs decides which comes first.
Which comes first, and what does this print?
1#include <stdio.h>2#include <string.h>3 4int main(void) {5 if (strcmp("Zoo", "apple") < 0) {6 printf("Zoo\n");7 } else {8 printf("apple\n");9 }10 return 0;11}
It compares codes, not dictionary order. 'Z' is 90 and 'a' is 97, so every uppercase letter comes before every lowercase one, and "Zoo" comes first.
When it doesn't fit
strcpy and strcat don't know how big the target array is. They just keep writing until they've written the '\0'. If the target is too small, they write past the end of it:
1#include <stdio.h>2#include <string.h>3 4int main(void) {5 char name[6];6 strcpy(name, "Alexandra");7 printf("%s\n", name);8 return 0;9}
main.c: In function 'main': main.c:6:5: warning: '__builtin_memcpy' writing 10 bytes into a region of size 6 overflows the destination [-Wstringop-overflow=] 6 | strcpy(name, "Alexandra"); | ^~~~~~~~~~~~~~~~~~~~~~~~~ main.c:5:10: note: destination object 'name' of size 6 5 | char name[6]; | ^~~~ buffer overflow
What happened: Stopped at line 6. This goes past the end of an array (or a string). Real C would read or overwrite whatever is next in memory.
"Alexandra" needs 10 bytes with its '\0', and name has only 6. This is a buffer overflow, one of the most common and most dangerous bugs in C programs: the extra bytes overwrite whatever is next to the array, and attackers can even use it to take over a program. This time gcc sees it while compiling: the first lines of the output are its warning. But a warning doesn't stop anything; the program still compiles and runs. Our engine stops on this line and tells you how many bytes were written and how many there was room for. Press "see how it went wrong" to take a look.
Tip: Before strcpy or strcat, do the math: the result's strlen plus 1 must not be bigger than the array's sizeof.
Character tools: ctype.h
<ctype.h> has functions for single characters. toupper turns a lowercase letter into uppercase and gives any other character back unchanged; tolower does the opposite. isdigit asks "is it a digit?" and isalpha asks "is it a letter?": they return a number that isn't 0 (true) if so, and 0 if not:
1#include <stdio.h>2#include <ctype.h>3 4int main(void) {5 char code[] = "ab-42c";6 for (int i = 0; code[i] != '\0'; i++) {7 if (isdigit(code[i])) {8 printf("%c is a digit\n", code[i]);9 } else {10 printf("%c becomes %c\n", code[i], toupper(code[i]));11 }12 }13 return 0;14}
a becomes A b becomes B - becomes - 4 is a digit 2 is a digit c becomes C
In the last lesson we made uppercase letters ourselves with - ('a' - 'A'). toupper is shorter, and you don't have to check for a lowercase letter first.
Practice
Write the code, then press Check. A hint costs nothing; the answer is there when you need it.
Full name
✓Use strcpy and strcat to put first, a space and last into full, then print the full name and its length: Ada Lovelace (12 characters).
Ada Lovelace (12 characters)
1#include <stdio.h>2#include <string.h>3 4int main(void) {5 char first[] = "Ada";6 char last[] = "Lovelace";7 char full[30];8 9 return 0;10}
How many digits
✓Use isdigit to count the digit characters in text, and print 5 digits.
5 digits
1#include <stdio.h>2#include <ctype.h>3 4int main(void) {5 char text[] = "Order 66, room 404";6 7 return 0;8}
Fix it
✓This program goes out of bounds, and the engine stops it. Make msg bigger so it prints Hi, Sam.
Hi, Sam
1#include <stdio.h>2#include <string.h>3 4int main(void) {5 char msg[6];6 strcpy(msg, "Hi, ");7 strcat(msg, "Sam");8 printf("%s\n", msg);9 return 0;10}
What you learned
<string.h>:strlengives the length (not counting'\0'),strcpycopies,strcatjoins,strcmpcompares.strlenis the length of the text,sizeofthe size of the array;strlencounts bytes.- You can't assign a string with
=: usestrcpy. You can't compare with==: usestrcmp(a, b) == 0. strcmpcompares codes, so uppercase letters come before lowercase ones.strcpyandstrcatdon't check whether the array is big enough: if it doesn't fit, it's a buffer overflow.<ctype.h>:toupper,tolower,isdigitandisalphawork on single characters.