CCommon String Functions

Common String Functions

This page walks through worked, correct examples of the <string.h> functions you will reach for most often, along with the subtle behaviors that trip people up — particularly around null-termination, comparison results, and strtok's hidden state.
strcpy and strncpy

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

int main(void) {
    char dest[10];

    strcpy(dest, "hello");         // OK: 5 chars + terminator = 6 bytes, fits in 10
    printf("%s\n", dest);          // hello

    strncpy(dest, "hi", sizeof(dest)); // copies "hi" + terminator, rest untouched (implementation detail)
    printf("%s\n", dest);              // hi
    return 0;
}
strncpy does not guarantee null-termination
If the source string passed to strncpy(dest, src, n) is n characters long or longer, strncpy writes exactly n bytes and does not add a null terminator at all. The resulting dest may then be missing its terminator, and any subsequent function that reads it as a string (like printf("%s", ...)) will read past the end of the buffer. Always either manually null-terminate the destination afterward, or make sure n comfortably exceeds the longest source you expect.

C
char dest[5];
strncpy(dest, "hello world", sizeof(dest)); // writes 5 bytes, NO terminator added!
dest[sizeof(dest) - 1] = '\0';              // manually ensure termination
strcat and strncat

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

int main(void) {
    char message[30] = "Hello, ";

    strcat(message, "world!");      // appends -- caller must ensure message has room
    printf("%s\n", message);        // Hello, world!

    char safer[20] = "Count: ";
    strncat(safer, "12345678901234567890", 5); // appends at most 5 chars, always null-terminates
    printf("%s\n", safer);                     // Count: 12345
    return 0;
}
strcmp: Not a Boolean
A very common misunderstanding is treating strcmp like it returns a true/false answer. It does not — it returns negative, zero, or positive, based on lexicographic (dictionary-style) order. To check for equality, compare the result against 0 explicitly.

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

int main(void) {
    if (strcmp("apple", "apple") == 0) {
        printf("Equal!\n");             // this runs -- 0 means equal
    }

    printf("%d\n", strcmp("apple", "banana")); // negative -- "apple" < "banana"
    printf("%d\n", strcmp("banana", "apple")); // positive -- "banana" > "apple"

    /* if (strcmp("apple", "apple")) { ... } */ // BUG: this is TRUE when strings differ,
                                                  // the opposite of what a beginner might expect
    return 0;
}
strchr and strstr: Searching

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

int main(void) {
    const char *sentence = "the quick brown fox";

    char *p = strchr(sentence, 'q');
    if (p != NULL) {
        printf("Found 'q' at: %s\n", p); // quick brown fox
    }

    char *sub = strstr(sentence, "brown");
    if (sub != NULL) {
        printf("Found substring: %s\n", sub); // brown fox
    }
    return 0;
}
strtok: Splitting a String
strtok breaks a string into tokens separated by any character from a given delimiter set. You call it once with the string to split, then repeatedly with NULL to get subsequent tokens.

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

int main(void) {
    char csv[] = "red,green,blue";

    char *token = strtok(csv, ",");
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok(NULL, ","); // NULL means "continue from where we left off"
    }
    return 0;
}
strtok modifies the string and is not thread-safe
strtok writes null terminators directly into the string you pass it, splitting it in place — the original string is destroyed as a side effect, so never call it on a string literal or on data you still need intact. It also relies on hidden internal static state to remember its position between calls, which means it is not safe to use from multiple threads, and you cannot interleave two separate tokenizing operations at once. Where available, prefer strtok_r, the reentrant version that takes an extra pointer argument to track state explicitly instead of relying on hidden globals.
Always check for NULL
strchr, strstr, and each call to strtok return NULL when nothing is found or there are no more tokens. Dereferencing that NULL result without checking it first is undefined behavior and a common crash.
  • strncpy does not null-terminate if the source is n characters or longer -- terminate manually afterward if unsure

  • strncat always null-terminates, appending at most the given number of characters

  • strcmp returns negative/zero/positive, not true/false -- compare with == 0 for equality

  • strchr and strstr return NULL when the search target is not found -- always check before using the result

  • strtok mutates the original string and keeps hidden static state -- not thread-safe; prefer strtok_r where available