Skip to content
Lesson 5 of 13

Step 1 of 5 · Reading · ~4 min

Learn

Arrays and Strings

Strings

By the end of this lesson you can read a whole line of text into a char array, see exactly which bytes landed there, and cut a string short by writing one zero byte into it.

A string is bytes, then a zero

C has no string type. A string is an array of char whose text ends at the first null terminator, the byte '\0' (value 0). Nothing stores the length; every library function walks forward until it meets that zero.

char pet[] = "owl";      /* memory: 'o' 'w' 'l' '\0'  (4 bytes) */
char note[16] = "ok";    /* 16 bytes, text uses 2, then '\0' */

An array of N chars holds at most N-1 characters of text, because the terminator needs a seat.

string.h: measure, compare, copy

#include <string.h>

strlen("owl")        /* 3: counts bytes BEFORE the '\0' */
strcmp(x, y)         /* 0 when the texts are equal, <0 or >0 by sort order */
strcpy(dst, src);    /* copies src into dst; dst must be big enough */

strlen returns a size_t, an unsigned type for sizes, so cast it with (int) to print it with %d. strcmp returns 0 for a match, so equality is strcmp(x, y) == 0. Writing x == y compiles but compares addresses, not letters. strcpy checks nothing: a too-small destination is a silent overflow.

Walking and cutting a string

Because the text ends at the first zero, the for loop from Arrays can walk it: stop when the current byte is '\0'. The body {} is empty because the loop header does all the work.

char city[] = "Lisbon";
int i;
for (i = 0; city[i] != '\0'; i++) {}   /* i == 6, the same as strlen(city) */

You can shorten a string by planting a zero yourself. The bytes after it stay in memory, but they are no longer part of the text. a && b ("and") is true only when both sides are true, and C checks the left side first: if it is false, the right side is never evaluated. This example keeps only the key of a key=value entry:

char entry[] = "color=blue";
int k;
for (k = 0; entry[k] != '\0' && entry[k] != '='; k++) {}
entry[k] = '\0';
printf("%s %d\n", entry, (int)strlen(entry));    /* color 5 */
kentry[k]not '\0'?not '='?keep going?
0cyesyesyes
4ryesyesyes
5=yesnono, stop

For "plain", which has no =, the loop stops at the terminator (k is 5) and writing '\0' there changes nothing.

Reading a whole line with fgets

scanf("%s", ...) stops at the first space, so it cannot read New York as one piece. fgets reads a line from stdin, the program's standard input (the same stream scanf reads):

char line[32] = "";
fgets(line, sizeof line, stdin);
printf("[%s]\n", line);

sizeof line is the same as sizeof(line); the parentheses are optional for a variable. There is no &: passing an array hands over the address of its first byte, as the Pointers lesson explains. For input cat this prints

[cat
]

fgets stops after the newline it reads and keeps it, so strlen(line) is 4, not 3. It stores at most sizeof line - 1 bytes, so a line that is too long, or a last line with no newline, arrives without one. With no input at all, fgets returns NULL and leaves line alone, which is why it starts as "" (length 0).

The newline, when present, is the last byte of the text, at index strlen - 1. On an empty string that index is -1, which reads memory before the array: C allows it and gives you garbage. So any test on the last byte needs the length check first, on the left of &&, where it stops the bad read.

The walking loop from the key=value example gives a second way to remove the newline, with no index arithmetic at all: walk forward until the byte is '\0' or '\n', then plant a zero where the walk stopped. If there is no newline, the walk stops at the terminator and the write changes nothing.

Common mistakes

  • Leaving the newline in. hello read by fgets measures 6, not 5. An empty line measures 1 instead of 0.
  • Chopping the last byte unconditionally. Input with no trailing newline, such as the last line of a file, loses a real character: no newline at end gives 16 instead of 17.
  • Using scanf("%s") for a line. It skips leading spaces and stops at the first space, so two leading spaces measures 3 instead of 20.
  • Skipping the empty check. Indexing [n - 1] on an empty string reads outside the array.

Your exercise

The starter reads one line with fgets and prints strlen of it, which counts the newline. Remove that newline, but only when it is really there, so the printed length is the number of characters on the line. Spaces and tabs count.

Up nextConditionalsControl Flow

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…