Phase 2 · Arrays, Strings & MemoryModule 8~44 min read

Strings & Character Handling

Understand null-terminated character arrays and use the standard library to inspect, copy, compare, and parse text safely.

What you'll learn

C has no separate built-in string type. Text is represented by character arrays that end with a zero byte. That convention is efficient and universal in C APIs, but capacity and termination are your responsibility.

By the end, you'll be able to:

  • Explain null termination, length, and capacity
  • Distinguish writable character arrays from string literals
  • Read, compare, search, build, and parse text with checked bounds
  • Recognize common string-overflow and truncation risks

The C string model

A C string is a sequence of characters followed by '\0'. Library functions walk until that terminator, so a character array without one is not a valid string.

The string "Code" inside char word[6]
'C'

[0]

'o'

[1]

'd'

[2]

'e'

[3]

'\0'

[4]

unused

[5]

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

int main(void) {
    char word[6] = {'C', 'o', 'd', 'e', '\0'};

    printf("%s\n", word);
    printf("length=%zu capacity=%zu\n", strlen(word), sizeof word);
    return 0;
}

Key idea

Length counts characters before the terminator. Capacityis the total array size. A valid string needs at least length + 1 bytes.

Literals & mutability

Initializing an array from a literal copies its characters into writable storage. A pointer can refer directly to a literal, but attempting to modify that literal is undefined behavior—use pointer-to-const to communicate the rule.

literals.c
#include <stdio.h>

int main(void) {
    char editable[] = "hello";       // writable array copy
    const char *message = "welcome"; // point at a literal; do not modify

    editable[0] = 'H';
    printf("%s %s\n", editable, message);
    return 0;
}

Watch out

Avoid char *text = "literal";. It compiles in C for historical reasons but does not make the literal writable.

Read text safely

fgets reads at most capacity - 1 characters and adds a terminator when it succeeds. If it reads the newline, that newline becomes part of the string.

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

int main(void) {
    char name[40];

    printf("Full name: ");
    if (fgets(name, sizeof name, stdin) == NULL) {
        return 1;
    }

    name[strcspn(name, "\n")] = '\0'; // remove newline if present
    printf("Hello, %s!\n", name);
    return 0;
}

Note

If no newline is present, the line may have filled the buffer. Production input code should detect that condition and consume the rest of the line before reading again.

The string library

FunctionPurposeSafety question
strlenCount characters before the terminatorIs the input definitely terminated?
strcmpLexicographically compare two stringsAre both inputs valid strings?
strchr / strstrFind a character or substringDid the function return NULL?
memcpyCopy an exact number of bytesDoes the destination have enough space?
snprintfFormat into a bounded destinationWas the result truncated?

Some classic functions such as strcpy and strcat do not know the destination capacity. Prefer designs where you calculate and check the required space.

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

int main(void) {
    char full[40] = "Master";
    const char suffix[] = " Coding";
    size_t used = strlen(full);
    size_t available = sizeof full - used;

    if (strlen(suffix) + 1 <= available) {
        memcpy(full + used, suffix, strlen(suffix) + 1);
    }

    printf("%s (%zu characters)\n", full, strlen(full));
    return 0;
}

Compare & search

Arrays cannot be compared by content with ==. Use strcmp; its result is negative, zero, or positive. Search functions return a pointer to the match orNULL.

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

int main(void) {
    const char left[] = "apple";
    const char right[] = "apricot";
    int order = strcmp(left, right);

    if (order < 0) printf("%s comes first\n", left);

    const char *found = strstr("learn C safely", "C");
    if (found != NULL) printf("Found: %s\n", found);
    return 0;
}

Tokenize & parse

Numeric conversion functions such as strtol provide more control thanatoi: they report where parsing stopped and can signal range errors througherrno.

parse.c
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char text[] = "42";
    char *end = NULL;
    errno = 0;
    long value = strtol(text, &end, 10);

    if (errno != 0 || end == text || *end != '\0' ||
        value < INT_MIN || value > INT_MAX) {
        fprintf(stderr, "Invalid integer\n");
        return 1;
    }

    printf("Parsed: %ld\n", value);
    return 0;
}

strtok modifies its input

strtok can split a writable string by replacing delimiters with terminators, but it keeps hidden state. For reusable or concurrent code, prefer explicit parsing or a reentrant platform alternative.

Capacity & safety

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

int main(void) {
    char destination[8];
    const char *source = "C language";

    int needed = snprintf(destination, sizeof destination, "%s", source);
    if (needed < 0) return 1;

    if ((size_t) needed >= sizeof destination) {
        printf("Truncated safely: %s\n", destination);
    }
    return 0;
}
  • Track destination capacity alongside every writable buffer
  • Reserve one byte for the null terminator
  • Check library return values and truncation
  • Do not treat arbitrary binary data as a string
  • Prefer length-aware interfaces for reusable functions

Recap & quick check

Key takeaways

  • A C string is a character sequence terminated by a zero byte.
  • String length and buffer capacity are different; capacity must include the terminator.
  • Writable arrays may be modified, while string literals must not be.
  • Compare contents with strcmp, not ==.
  • Bound input and output, check return values, and handle truncation explicitly.

Quick check

1. How many bytes are required to store "cat" as a C string?

2. What does strcmp return when strings are equal?

3. Why prefer strtol over atoi for validated input?

4. What must a writable-string API know?

You can now handle text deliberately. Next up: Module 9 — Pointers & Addresses, the idea that connects arrays, functions, and memory.