Kodewick
Lesson 14 of 30Contents

String functions

Lesson 14 of 30 · Part 2 · Functions and arrays · about 15 minutes

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:

main.c
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}
Output
keyboard has 8 letters

This lesson covers the four you'll use most:

FunctionWhat 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:

main.c
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}
Output
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:

main.c
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}
Output
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:

main.c
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}
Output
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:

main.c
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}
Output
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:

main.c
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}
Output
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.

main.c
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}
Output
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.

Guess 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}

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:

main.c
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}
Output
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:

main.c
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}
Output
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.

Exercise 1

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).

It should print
Ada Lovelace (12 characters)
exercise1.c
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}
Output
Press Run or Ctrl+Enter
Exercise 2

How many digits

Use isdigit to count the digit characters in text, and print 5 digits.

It should print
5 digits
exercise2.c
1#include <stdio.h>2#include <ctype.h>3 4int main(void) {5    char text[] = "Order 66, room 404";6 7    return 0;8}
Output
Press Run or Ctrl+Enter
Exercise 3

Fix it

This program goes out of bounds, and the engine stops it. Make msg bigger so it prints Hi, Sam.

It should print
Hi, Sam
exercise3.c
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}
Output
Press Run or Ctrl+Enter

What you learned

  • <string.h>: strlen gives the length (not counting '\0'), strcpy copies, strcat joins, strcmp compares.
  • strlen is the length of the text, sizeof the size of the array; strlen counts bytes.
  • You can't assign a string with =: use strcpy. You can't compare with ==: use strcmp(a, b) == 0.
  • strcmp compares codes, so uppercase letters come before lowercase ones.
  • strcpy and strcat don't check whether the array is big enough: if it doesn't fit, it's a buffer overflow.
  • <ctype.h>: toupper, tolower, isdigit and isalpha work on single characters.