Topic 67 of 100 Arrays and Strings in C Interactive Tutorial

C String Functions Reference: Common Operations and Examples

13 mins read Last updated: 22 Aug 2026 30,200 views
C Compiler 0.00%

What is C String Functions Reference? Core Concept & Direct Summary

Core Objective & Problem Statement

What are the standard string manipulation functions in `<string.h>` (`strlen`, `strcpy`, `strcat`, `strcmp`, `strchr`, `strstr`)?

Direct Answer Summary

C has no built-in string type. What it has instead is a convention — a char array ending in a '\0' — plus a library of functions in string.h that operate on those arrays. That library is small, sharp, and used in essentially every C program ever written.

This page is your reference: the functions you'll actually use, what each one does, the syntax, runnable examples, and — importantly — the safety notes that separate code that works in testing from code that works in production. A few of these functions have sharp edges that have caused real security incidents, and knowing which ones is part of being a competent C programmer.

(Suggested image: the hero graphic — a char array showing "H E L L O \0" with a highlighted "null terminator" box, plus a sidebar listing the function names)

How Does C String Functions Reference Work? In-Depth Mechanics & Architecture

The Foundation: What a C String Actually Is

Before the functions make sense, the underlying model has to be clear:

In memory, that's:

main.c
Run Online
'H' 'e' 'l' 'l' 'o' '\0' 0 1 2 3 4 5

Five characters, six slots. The '\0' (null terminator) marks the end. Every string function on this page walks the array looking for that '\0' to know where the string stops — which is exactly why:

  • Forgetting the terminator makes functions run off the end into garbage
  • sizeof gives capacity while strlen gives content length (from the array-size tutorial)
  • Buffers must be sized with room for the terminator

You need #include <string.h> for everything below.


Quick Reference Table

Bookmark this one:

FunctionPurposeReturns
strlen(s)Length (excluding '\0')size_t count
strcpy(dest, src)Copy stringdest
strncpy(dest, src, n)Copy at most n charsdest
strcat(dest, src)Append stringdest
strncat(dest, src, n)Append at most n charsdest
strcmp(s1, s2)Compare strings<0, 0, >0
strncmp(s1, s2, n)Compare first n chars<0, 0, >0
strchr(s, c)Find first occurrence of charpointer or NULL
strrchr(s, c)Find last occurrence of charpointer or NULL
strstr(s, sub)Find substringpointer or NULL
strtok(s, delims)Split into tokenspointer or NULL
strspn(s, chars)Length of initial matching runcount
strcspn(s, chars)Length before first matchcount
strrev(s)Reverse (non-standard)pointer

(Suggested image: THE key graphic for this page — the table above styled as a clean poster-style reference. This is your most linkable and shareable asset on the page.)


1. strlen() — The Length

main.c
Run Online
#include <stdio.h>
#include <string.h>
int main(void) {
    char name[] = "Programming";
    printf("Length: %zu\n", strlen(name));
    // 11    printf("Size  : %zu\n", sizeof(name));      // 12 (includes '\0')
    return 0;
}

Output:

strlen counts characters until the null terminator — not including it. That's the difference from sizeof, which measures the whole array including the terminator.

Performance note worth knowing:strlen walks the whole string every time you call it. In a loop like for (i = 0; i < strlen(s); i++), that turns an O(n) loop into O(n²). Cache it:

main.c
Run Online
int len = strlen(s);
for (int i = 0;
i < len;
i++) {
    ...
}
// ✓ much faster on long strings

A small habit that matters once your strings get long.


2. strcpy() and strncpy() — Copying

strcpy(dest, src) copies the full string including the terminator:

main.c
Run Online
char source[] = "Hello";
char dest[20];
strcpy(dest, source);
printf("%s\n", dest);
// Hello

⚠️ strcpy does zero bounds checking. If source is longer than dest, you write straight past the end of the buffer — the classic overflow we first met with gets(). Never use strcpy with untrusted or unpredictable-length input.

strncpy(dest, src, n) limits how much it copies:

main.c
Run Online
char dest[10];
strncpy(dest, "This is a very long string", 9);
dest[9] = '\0';
// ← YOU must terminate it!printf("%s\n", dest);        // "This is a"

Note the trap: strncpy doesn't guarantee a null terminator when the source is longer than n. It copies exactly n characters and stops — leaving dest unterminated unless you add '\0' yourself. This surprises people constantly, because strncpylooks like the safe strcpy but has this quirk.

The relaxed modern approach — snprintf:

main.c
Run Online
char dest[10];
snprintf(dest, sizeof(dest), "%s", "This is a very long string");
printf("%s\n", dest);
// "This is a" — always terminated ✓

snprintf (from stdio.h) always terminates and is genuinely safe. Many modern style guides recommend it over strncpy for exactly this reason.

(Suggested image: side-by-side comparison of strcpy / strncpy / snprintf with the overflow and missing-terminator problems highlighted)


3. strcat() and strncat() — Joining Strings

strcat(dest, src) appends src to the end of dest:

main.c
Run Online
char greeting[50] = "Hello, ";
char name[] = "Ravi";
strcat(greeting, name);
printf("%s\n", greeting);
// "Hello, Ravi"

Two requirements: dest must have enough remaining room for both strings plus the terminator, and it must already be a valid string. strcat overwrites the '\0' at the end of dest and starts writing there — no checking.

⚠️ A classic first-time bug:

main.c
Run Online
char s[20];
// uninitialized — contains garbage!strcat(s, "hello");
// ✗ undefined behavior

strcat needs a valid string to append to. Uninitialized, s has no terminator — strcat hunts for one through garbage memory. Initialize first:

main.c
Run Online
char s[20] = "";
// ✓ empty but valid stringstrcat(s, "hello");

strncat(dest, src, n) appends at most n characters and always terminates — a genuinely better behaved function than strncpy:

main.c
Run Online
char s[20] = "Hello";
strncat(s, " world and more", 6);
printf("%s\n", s);
// "Hello world"

Use strncat over strcat whenever the input length isn't fully under your control.


4. strcmp() — Comparing

Here's the one that trips up everyone coming from other languages. You cannot compare strings with ==:

main.c
Run Online
char a[] = "hello";
char b[] = "hello";
if (a == b) {
    ...
}
// ✗ compares ADDRESSES — almost always false!if (strcmp(a, b) == 0) {
...
}
// ✓ compares CONTENTS

The rule: strcmp returns 0 when the strings are equal. That's counterintuitive (you'd expect "true"), but it exists because strcmp is designed for sorting — it returns:

Return valueMeaning
0Strings are equal
< 0First string comes before (alphabetically)
> 0First string comes after
main.c
Run Online
printf("%d\n", strcmp("apple", "banana"));
// negative — 'a' < 'b'printf("%d\n", strcmp("banana", "apple"));
// positiveprintf("%d\n", strcmp("apple", "apple"));
// 0printf("%d\n", strcmp("Apple", "apple"));
// negative — uppercase sorts first

That last line reveals the comparison is character-code based (ASCII), not alphabetical-case-insensitive. 'A' (65) < 'a' (97), so "Apple" sorts before "apple". For case-insensitive comparison you need strcasecmp (POSIX, not standard C) or a manual loop with tolower.

Common forms you'll write:

main.c
Run Online
if (strcmp(name, "admin") == 0) {
    ...
}
// equalityif (strcmp(a, b) != 0) {
...
}
// inequalityif (strcmp(a, b) < 0) {
...
}
// alphabetical order

strncmp(s1, s2, n) compares only the first n characters — useful for prefix checks:

main.c
Run Online
if (strncmp(input, "GET ", 4) == 0) {
    puts("This is an HTTP GET request");
}

(Suggested image: a "0 means equal?!" callout graphic with a memory diagram showing why == compares addresses rather than characters — this is the #1 string confusion and worth an excellent visual)


5. strchr(), strrchr(), strstr() — Searching

strchr(s, c) finds the first occurrence of a character, returning a pointer to it (or NULL):

main.c
Run Online
char text[] = "banana";
char *found = strchr(text, 'n');
if (found != NULL) {
    printf("Found at index %ld\n", found - text);
    // 2
}

Note that found - text — pointer subtraction gives you the index. (That's pointer arithmetic, coming in the pointers cluster — but it's the standard way to convert a search result into a position.)

strrchr(s, c) finds the last occurrence:

main.c
Run Online
char path[] = "/home/user/file.txt";
char *ext = strrchr(path, '.');
if (ext != NULL) {
    printf("Extension: %s\n", ext);
    // ".txt"
}

This is the standard way to extract a file extension or filename — search from the right.

strstr(s, sub) finds a substring:

main.c
Run Online
char sentence[] = "The quick brown fox";
char *pos = strstr(sentence, "brown");
if (pos != NULL) {
    printf("Found at index %ld\n", pos - sentence);
    // 10    printf("From there: %s\n", pos);
    // "brown fox"
}

Always check for NULL before using the result. Forgetting that check is a reliable way to crash your program, since NULL dereferences are immediate segfaults.


6. strtok() — Splitting Strings

Parsing input inevitably means splitting it into pieces — comma-separated values, space-separated words, path components. strtok does this:

main.c
Run Online
#include <stdio.h>
#include <string.h>
int main(void) {
    char data[] = "apple,banana,cherry,date";
    char *token;
    token = strtok(data, ",");
    // first call: pass the string    while (token != NULL) {        printf("%s\n", token);        token = strtok(NULL, ",");      // later calls: pass NULL    }
    return 0;
}

Output:

Three things you must know about strtok:

  1. The first call takes the string; every subsequent call takes NULL. That's how it remembers where it left off — it keeps internal state between calls.
  2. It modifies the original string, replacing each delimiter with '\0'. That's why data must be a modifiable char array, never a string literal (strtok("a,b,c", ",") crashes).
  3. It is not thread-safe and not reentrant — internal static state means two interleaved strtok loops will corrupt each other. The POSIX strtok_r variant takes an explicit state pointer and solves this.
main.c
Run Online
// The full split idiom with both strtok and length counting:char csv[] = "2026,September,25";char *fields[3];int count = 0;
char *tok = strtok(csv, ",");
while (tok != NULL && count < 3) {
    fields[count++] = tok;
    tok = strtok(NULL, ",");
}
for (int i = 0; i < count; i++) {
    printf("Field %d: %s\n", i, fields[i]);
}

(Suggested image: the strtok state diagram — the string with commas being replaced by '\0' markers, and the "first call passes string, rest pass NULL" rule highlighted)


7. strspn() and strcspn() — Measuring Runs

These two are lesser known but genuinely handy.

strcspn(s, chars) — length of the initial segment before any of the listed characters:

main.c
Run Online
char line[] = "Hello\nWorld";
size_t len = strcspn(line, "\n");
printf("%zu\n", len);
// 5 — characters before the newline

This is the standard way to strip the trailing newline from fgets input — the exact idiom you've seen throughout this series:

main.c
Run Online
fgets(line, sizeof(line), stdin);
line[strcspn(line, "\n")] = '\0';
// remove the newline safely

strspn(s, chars) — length of the initial segment consisting only of those characters:

main.c
Run Online
char mixed[] = "aabbcc123";
printf("%zu\n", strspn(mixed, "abc"));
// 6 — "aabbcc"

Handy for skipping over known prefixes or validating leading characters.


8. Case Conversion — toupper() and tolower()

These live in <ctype.h>, not <string.h>, because they work on single characters:

main.c
Run Online
#include <ctype.h>
#include <string.h>
char s[] = "Hello World";
for (int i = 0; i < strlen(s); i++) {
    s[i] = tolower(s[i]);
}
printf("%s\n", s);
// "hello world" — but careful with the loop condition!

⚠️ That loop has the strlen performance problem. Better:

main.c
Run Online
for (int i = 0;
s[i] != '\0';
i++) {
    // ✓ walks the string once    s[i] = (char)tolower((unsigned char)s[i]);
}

The (unsigned char) cast matters more than it looks: passing a negative char value to tolower is undefined behavior. It's a subtle correctness detail that most tutorials skip — good to have in your reference.

Useful ctype.h predicates alongside them: isalpha, isdigit, isspace, isupper, islower, isalnum, and ispunct. Each takes a character and returns non-zero (true) or zero (false).


Putting It Together: A Word Counter

main.c
Run Online
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_LINE 256
int main(void) {
    char line[MAX_LINE];
    char original[MAX_LINE];
    printf("Enter a sentence: ");
    if (fgets(line, sizeof(line), stdin) == NULL) {
        puts("No input.");
        return 1;
    }
    line[strcspn(line, "\n")] = '\0';
    // strip newline    strncpy(original, line, MAX_LINE - 1);     // keep a copy (bounded copy)    original[MAX_LINE - 1] = '\0';             // ensure termination
    int chars =(int)strlen(line);
    // --- Count words using strtok ---    int words = 0;    char *tok = strtok(line, " \t");    while (tok != NULL) {        words++;        tok = strtok(NULL, " \t");    }
    // --- Count vowels in the original ---    int vowels = 0;    const char *v = "aeiou";    for (int i = 0; original[i] != '\0'; i++) {        char c = (char)tolower((unsigned char)original[i]);        if (strchr(v, c) != NULL && c != '\0') {            vowels++;        }    }
    // --- Count digits in the original ---    int digits = 0;    for (int i = 0; original[i] != '\0'; i++) {        if (isdigit((unsigned char)original[i])) {            digits++;        }    }
    puts("\n===== TEXT ANALYSIS =====");
    printf("Original : %s\n", original);
    printf("Length   : %d characters\n", chars);
    printf("Words    : %d\n", words);
    printf("Vowels   : %d\n", vowels);
    printf("Digits   : %d\n", digits);
    return 0;
}

Sample Run:

main.c
Run Online
Enter a sentence: C programming is fun in 2026
===== TEXT ANALYSIS =====
Original : C programming is fun in 2026
Length : 30 characters
Words : 6
Vowels : 8
Digits : 4

This one program uses fgets, strcspn, strncpy, strlen, strtok, strchr, tolower, and isdigit — eight functions from the reference, assembled into something genuinely useful. Note the original copy: strtok destroys the string it's given, so if you need the text afterward, keep a copy first. That's a real pattern, not busywork.


Safety Notes: Which Functions to Watch

FunctionRiskSafer alternative
strcpyNo bounds check — overflowsnprintf or strncpy + manual terminator
strcatNo bounds check — overflowstrncat or snprintf
strncpyMay not terminate the stringsnprintf
strtokNot thread-safe, destroys inputstrtok_r (POSIX)
strlen in a loopHidden O(n²) performanceCache the length
strcmp(a, b) == trueWrong — returns 0 for equalCompare with == 0
Any search returning pointerNULL not checked → crashAlways check != NULL

(Suggested image: a "safe vs risky" color-coded table graphic — this is genuinely useful and pairs well with your earlier gets() safety page)


Common String Function Mistakes

  1. Comparing with == — compares addresses, not contents. Use strcmp(...) == 0.
  2. Expecting strcmp to return 1 for equal — it returns 0. The <0 / 0 / >0 convention is for sorting.
  3. Forgetting to include <string.h> — implicit declaration warnings and potential runtime chaos.
  4. Copying into an uninitialized or too-small buffer — the overflow family. Always size for content + terminator.
  5. Forgetting the '\0' after strncpy — the string isn't terminated, and everything downstream misbehaves.
  6. Passing a string literal to strtok — it modifies the string, and literals are read-only; crash.
  7. Not checking for NULL from strchr/strstr/strtok/fgets — dereferencing NULL is an instant crash.
  8. Using strlen in a loop condition — O(n²) on long strings. Cache it.
  9. Calling toupper/tolower without the unsigned char cast — undefined behavior for negative char values.
  10. Assuming strtok leaves the original intact — it doesn't; delimiters become '\0' permanently.

(Suggested image: "10 String Function Mistakes" checklist graphic — consistent with the series)


Final Thoughts

The C string library is small but sharp. Few functions, straightforward purposes, and a handful of edge cases that matter a great deal: bounds checking, null terminators, strcmp's zero-means-equal convention, and strtok's destructive statefulness.

If you take three things from this page, make them these: always size your buffers for the terminator, always check pointers for NULL, and compare with strcmp, never ==. Those three habits prevent the overwhelming majority of string bugs — and the overflow discipline here is the same discipline that made gets() a cautionary tale.

Next in this cluster: strings as arrays in depth (declaration, initialization, and iteration), then fgets as the safe way to read text, and finally string-to-number conversions.


Runnable C Code Example & Practical Implementation

main.c
Run Online
#include <stdio.h>
#include <string.h>
int main() {
    char greeting[32] = "Hello";
    char name[] = " World!";
    strcat(greeting, name);
    printf("Concatenated: %s\
", greeting);
    printf("String length: %zu\
", strlen(greeting));
    printf("Comparison with 'Hello': %d\
", strcmp(greeting, "Hello"));
    return 0;
}

Key Takeaways, Best Practices & Memory Safety Rules

Best Practice & Key Takeaways
Use bounded string functions (strncat, snprintf) and always ensure sufficient buffer capacity for the trailing null byte (+ 1).

Frequently Asked Questions & Technical Interview Insights

Technical Interview & Viva Questions

Frequently asked C programming interview questions
Q

Q: What does `strcmp("apple", "banana")` return?

A: A negative integer because 'a' (ASCII 97) is smaller than 'b' (ASCII 98).
Q

Q: Does `strlen()` include the null terminator `\\0` in its count?

A: No, `strlen()` returns only the number of visible characters preceding `\\0`.

Related Tools, Tutorials & Authoritative References

Recommended Interactive Tools

Authoritative Standards & Citations