Code of the Day
BeginnerArrays and strings

C strings: char arrays and the null terminator

Understand how C represents text as null-terminated char arrays — declaration, the null terminator, string literals, and basic iteration.

CBeginner11 min read
Recommended first
By the end of this lesson you will be able to:
  • Explain that a C string is a char array terminated by a null byte
  • Declare a string as a char array and as a pointer to a string literal
  • Iterate over a string character by character
  • Use strlen to find a string's length

Text in C is stored as an array of char values with a special sentinel byte at the end: the null terminator ('\0', value 0). This design is elegant in its simplicity and a source of many of C's most famous bugs. Understanding it thoroughly is essential for writing safe C.

The null terminator

Every C string ends with the byte '\0' (null, ASCII value 0). This is how functions like printf, strlen, and strcpy know where the string ends — they walk forward through memory until they hit the zero byte.

"Hello" in memory:
H  e  l  l  o  \0
72 101 108 108 111 0

A char array of length 6 stores the 5-character string "Hello" plus the null terminator.

Declaring strings

As a char array with initialisation:

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

More commonly, string literal syntax handles the null terminator automatically:

char greeting[6] = "Hello"; /* compiler adds '\0' automatically */
char greeting2[] = "Hello"; /* compiler infers size = 6 */

As a pointer to a string literal:

const char *message = "Hello, World!";

This is different from the array form. The string "Hello, World!" is stored in read-only memory. The pointer message points to it. You should mark it const because modifying a string literal is undefined behaviour.

Array strings and pointer strings are different. char greeting[] = "Hello" creates a writable copy of the string on the stack. const char *message = "Hello" points to read-only data. You can modify greeting[0] = 'J' to change it to "Jello", but modifying through the pointer version is undefined behaviour and typically causes a crash.

Iterating over a string

Since strings end with '\0', you iterate until you hit it:

#include <stdio.h>

int main(void) {
    char str[] = "Hello";

    /* Method 1: index-based */
    for (int i = 0; str[i] != '\0'; i++) {
        printf("str[%d] = '%c' (ASCII %d)\n", i, str[i], str[i]);
    }

    /* Method 2: pointer-based (preview of pointers) */
    for (char *p = str; *p != '\0'; p++) {
        printf("%c", *p);
    }
    printf("\n");

    return 0;
}

Output:

str[0] = 'H' (ASCII 72)
str[1] = 'e' (ASCII 101)
str[2] = 'l' (ASCII 108)
str[3] = 'l' (ASCII 108)
str[4] = 'o' (ASCII 111)
Hello

String length with strlen

strlen from <string.h> returns the number of characters before the null terminator:

#include <stdio.h>
#include <string.h>

int main(void) {
    char str[] = "Hello";
    size_t len = strlen(str);
    printf("Length: %zu\n", len); /* 5, not 6 */
    return 0;
}

strlen does not count the null terminator. The array needs len + 1 bytes to store the string including the terminator.

Printing strings

char name[] = "Alice";
printf("Hello, %s!\n", name);  /* %s for strings */
puts(name);                     /* puts adds a newline automatically */

%s tells printf to print characters until it hits '\0'. puts is simpler for just printing a string with a newline.

Building strings manually

#include <stdio.h>

int main(void) {
    char upper[20] = "hello";

    for (int i = 0; upper[i] != '\0'; i++) {
        if (upper[i] >= 'a' && upper[i] <= 'z') {
            upper[i] -= 32; /* lowercase is 32 more than uppercase in ASCII */
        }
    }

    printf("%s\n", upper); /* HELLO */
    return 0;
}

Or using <ctype.h>:

#include <ctype.h>
for (int i = 0; upper[i]; i++) {
    upper[i] = toupper((unsigned char)upper[i]);
}

Cast to unsigned char before calling ctype.h functions. Functions like toupper and isdigit take an int whose value must be representable as unsigned char or be EOF. Passing a plain char (which may be signed) can pass negative values on platforms where char is signed, causing undefined behaviour.

Where to go next

Next: string functions from <string.h>strcpy, strcat, strcmp, strncpy, and others that let you copy, concatenate, and compare strings without writing the loops yourself.

Finished reading? Mark it complete to track your progress.

On this page