String Functions in C — Complete Guide + Write Your Own Function from Scratch (2026)

String Function in C — Write Your Own Function From Scratch

Every string function you have ever used — strlen, strcpy, strcmp, strcat — is not magic. There is no secret compiler trick behind them. No special hardware instruction. Each one is a simple loop that walks through characters one by one, performs a straightforward operation, and stops when it hits ‘\0’.

That is it. Only you truly see this — once you write these functions yourself and watch them work, two things happen. First, you understand strings at a level most developers never reach. Second, you stop making the mistakes that cause buffer overflows, crashes, and security vulnerabilities, because you understand exactly what is happening at every step.

This guide does both. We cover every major function from <string.h> — what it does, how it works internally, where it fails, and when to use safer alternatives. Then we build our own versions from scratch. Clean, working, explained line by line.

This is part of our C programming series. This guide builds on:

1. How string functions actually work — the mental model

Before touching a single function, lock this mental model in place. It makes everything else in this guide obvious. A C string is a sequence of characters in memory. The last character is always ‘\0’. Every string function works by exploiting this one fact — it starts at the beginning and keeps moving forward until it either finds ‘\0’ or finds what it is looking for.

Every function you will learn in this guide is built on one of these three patterns:

Pattern 1 — Walk until null (strlen, puts)

while(str[i] != '\0') {
  do something;
  i++;
}

Pattern 2 — Walk and copy until null (strcpy, strcat)

while((*dest++ = *src++) != '\0');

Pattern 3 — Walk and compare until null or mismatch (strcmp)

while(*a && *a == *b) {
  a++;
  b++;
}

Every single standard string function is a variation of one of these three patterns. Keep this in mind, and nothing in this guide will feel complicated.

2. The string.h library — What is inside

Including <string.h> gives you access to over 20 functions. Here are the ones every C programmer must know, grouped by purpose:

Length and information

strlen — get string length

Copying

strcpy, strncpy — copy a string.

Concatenation

strcat, strncat — join two strings

Comparison

strcmp, strncmp, strcasecmp — compare strings

Searching

strchr, strrchr, strstr, strpbrk, strcspn, strspn — find characters or substrings.

Splitting

strtok, strtok_r — split by delimiter

Memory operations

memcpy, memset, memcmp, memmove — raw memory manipulation

Conversion (from stdlib.h and stdio.h)

atoi, atof, sprintf, snprintf — convert between strings and numbers

We will cover the most important ones with real depth. Not just “here is the syntax” — but how they work, where they break, and what to use instead when they are dangerous.

3. strlen — Length of a string

Returns the number of characters in a string, not counting the null terminator.

Syntax

size_t strlen(const char *str);

Basic usage

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

int main() {
    char name[] = "Embediax";
    printf("Length: %zu\n", strlen(name));   // 8

    char empty[] = "";
    printf("Empty: %zu\n", strlen(empty));   // 0

    char city[] = "New Delhi";
    printf("City: %zu\n", strlen(city));     // 9

    return 0;
}

Output

Length: 8
Empty: 0
City: 9

How it works internally

// strlen starts at index 0 and counts until '\0'
// "Programming" → E-m-b-e-d-i-a-x-\0
//                 1 2 3 4 5 6 7 8 9  stop

The critical distinction — strlen vs sizeof

char buf[100] = "Embediax";

strlen(buf);    // 8  — characters in the string
sizeof(buf);    // 100 — total buffer size allocated

These are different numbers, and they serve different purposes. strlen tells you how many characters are in the buffer. sizeof tells you how big the buffer is. You need to know both.

Performance trap — never call strlen in a loop condition

// SLOW — strlen scans the entire string on every iteration
for (int i = 0; i < strlen(str); i++) { }

// FAST — compute once, reuse
size_t len = strlen(str);
for (int i = 0; i < len; i++) { }

strlen is O(n) — it scans every character. Calling it in a loop makes the whole loop O(n²). For short strings, this does not matter. For large data processing, it can be the difference between fast and unusably slow.

4. strcpy and strncpy — Copying strings

Copy the contents of one string into another buffer.

Syntax

char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);

Basic strcpy

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

int main() {
    char src[]  = "Hello, Embediax!";
    char dest[20];

    strcpy(dest, src);
    printf("%s\n", dest);   // Hello, Embediax!

    return 0;
}

Output

Hello, Embediax!

How strcpy works — character by character

src:  [ H ][ e ][ l ][ l ][ o ][ , ][ E ][ m ][ b ][ e ][ d ][ i ][ a ][ x ][ ! ][\0 ]
dest: [ H ][ e ][ l ][ l ][ o ][ , ][ E ][ m ][ b ][ e ][ d ][ i ][ a ][ x ][ ! ][\0 ]
       ↑ copied one by one until '\0' is copied

The danger of strcpy

char small[5];
strcpy(small, "Hello World");   // Buffer overflow — writes 12 bytes into 5-byte buffer

strcpy has no idea how big dest is. It copies until it hits ‘\0’ in src — no questions asked. If dest is too small, it writes into memory it does not own.

Safe alternative — strncpy

char dest[10];
char src[] = "Hello World";

strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';   // ALWAYS manually null-terminate

printf("%s\n", dest);   // Hello Wor (truncated safely)

Important warning about strncpy

If src is longer than n, strncpy copies n characters but does not add ‘\0’. You must always add it manually. This trips up even experienced developers.

Best modern alternative — snprintf

snprintf(dest, sizeof(dest), "%s", src);
// Always null-terminated, always safe, no manual '\0' needed

Use snprintf over strncpy in new code. It is safer, clearer, and handles the null terminator automatically.

5. strcat and strncat — Joining strings

Append one string to the end of another.

Syntax

char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);

Basic usage

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

int main() {
    char greeting[30] = "Hello";
    strcat(greeting, ", Embediax");
    strcat(greeting, "!");
    printf("%s\n", greeting);   // Hello, Embediax!

    return 0;
}

Output

Hello, Embediax!

How strcat works

Before:
dest: [ H ][ e ][ l ][ l ][ o ][\0 ][ ? ][ ? ]...
                                ↑ strcat finds this '\0'

After strcat(dest, " Embediax"):
dest: [ H ][ e ][ l ][ l ][ o ][ ][ E ][ m ][ b ][ e ][ d ][ i ][ a ][ x ][\0 ]
                                ↑ starts writing here, overwrites '\0'

strcat first walks to the end of dest (finding ‘\0’), then copies src starting from that point. It is essentially a strlen followed by a strcpy. This means it is O(n + m) — the longer your strings, the more work it does finding the end.

The danger — same as strcpy, no bounds checking

char buf[10] = "Hello";
strcat(buf, " Embediax!!!");   // Overflow — "Hello Embediax!!!" needs 15 bytes

Sefe alternative — strncat

char dest[20] = "Hello";
strncat(dest, " Embediax!!!", sizeof(dest) - strlen(dest) - 1);
printf("%s\n", dest);   // Hello Embediax!!! (or truncated safely)

The formula sizeof(dest) - strlen(dest) - 1 calculates exactly how many characters are left in the buffer. This is the correct pattern.

6. strcmp and strncmp — Comparing strings

Compare two strings lexicographically (dictionary order).

Syntax

int strcmp(const char *str1, const char *str2);
int strncmp(const char *str1, const char *str2, size_t n);

Return values

  • 0 — strings are equal
  • < 0 — str1 comes before str2
  • > 0 — str1 comes after str2

Basic usage

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

int main() {
    printf("%d\n", strcmp("apple", "apple"));    //  0 — equal
    printf("%d\n", strcmp("apple", "banana"));   // <0 — apple before banana
    printf("%d\n", strcmp("mango", "apple"));    // >0 — mango after apple
    printf("%d\n", strcmp("abc", "abd"));        // <0 — differs at index 2
    return 0;
}

Output

0
-1
1
-1

How strcmp works

Comparing "abc" and "abd":
'a' == 'a' → continue
'b' == 'b' → continue
'c' != 'd' → return 'c' - 'd' = 99 - 100 = -1 (negative)

It compares character by character. The moment it finds a difference, it returns the difference of those two character values. If it reaches ‘\0’ in both strings simultaneously, they are equal.

The most common mistake in all of C

char input[] = "yes";

// WRONG — compares memory addresses, almost always false
if (input == "yes") { printf("correct\n"); }

// CORRECT — compares actual characters
if (strcmp(input, "yes") == 0) { printf("correct\n"); }

This mistake is so common and so subtle that it deserves its own callout box in every C tutorial ever written. == on strings compares addresses. strcmp compares content. Always use strcmp.

Case-insensitive comparison

// Not standard C but available on most systems
#include <strings.h>   // Note: strings.h not string.h

strcasecmp("Hello", "hello");   // 0 — equal ignoring case

For strictly portable code, convert both strings to the same case first, then use strcmp.

7. strchr and strrchr — Searching for a character

strchr — finds the first occurrence of a character in a string.

strrchr — finds the last occurrence of a character in a string.

Syntax

char *strchr(const char *str, int ch);
char *strrchr(const char *str, int ch);

Both return a pointer to the found character, or NULL if not found.

Usage

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

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

    // Find first 'g'
    char *first = strchr(str, 'g');
    if (first)
        printf("First 'g' at index: %ld\n", first - str);   // 3

    // Find last 'g'
    char *last = strrchr(str, 'g');
    if (last)
        printf("Last 'g' at index: %ld\n", last - str);     // 10

    // Check if character exists
    if (strchr(str, 'z') == NULL)
        printf("'z' not found\n");

    return 0;
}

Output

First 'g' at index: 3
Last 'g' at index: 10
'z' not found

Practical use — extracting file extension

char filename[] = "report_final.pdf";
char *dot = strrchr(filename, '.');

if (dot)
    printf("Extension: %s\n", dot + 1);   // pdf

This is a real pattern used in file handling code everywhere.

8. strstr — Finding a substring

Finds the first occurrence of a substring within a string.

Syntax

char *strstr(const char *haystack, const char *needle);

Returns a pointer to where needle begins inside haystack, or NULL if not found.

Usage

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

int main() {
    char text[] = "Welcome to C programming world";
    char *found;

    found = strstr(text, "programming");
    if (found)
        printf("Found at index: %ld\n", found - text);   // 11

    found = strstr(text, "Python");
    if (found == NULL)
        printf("Python not found\n");

    // Replace a substring (manual approach)
    char sentence[] = "I love cats and cats love me";
    char *pos = strstr(sentence, "cats");
    if (pos) {
        strncpy(pos, "dogs", 4);   // Replace first "cats" with "dogs"
        printf("%s\n", sentence);  // I love dogs and cats love me
    }

    return 0;
}

Output

Found at index: 13
Python not found
I love dogs and cats love me

9. strtok — Splitting a string

Splits a string into tokens based on delimiter characters.

Syntax

char *strtok(char *str, const char *delimiters);

Usage

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

int main() {
    char csv[] = "Alice,25,Engineer,Delhi";

    char *token = strtok(csv, ",");
    while (token != NULL) {
        printf("%s\n", token);
        token = strtok(NULL, ",");   // Pass NULL for subsequent calls
    }

    return 0;
}

Output

Alice
25
Engineer
Delhi

How strtok works

"Alice,25,Engineer,Delhi"
       ↑ strtok replaces ',' with '\0', returns pointer to "Alice"

"Alice\025\0Engineer\0Delhi"
            ↑ next call returns pointer to "25"

It inserts ‘\0’ characters into your string at each delimiter. This is why strtok modifies the original string and why you should never use it on a string literal.

Three rules for strtok

  1. First call passes the string. All subsequent calls pass NULL.
  2. It modifies the original string — always work on a copy if you need the original.
  3. It is not thread-safe — use strtok_r in multi-threaded programs.
  1. 1.
// Thread-safe version
char *saveptr;
char *token = strtok_r(str, ",", &saveptr);
while (token != NULL) {
    printf("%s\n", token);
    token = strtok_r(NULL, ",", &saveptr);
}

Image suggestion

Step-by-step diagram showing strtok inserting null terminators into a comma-separated string, with arrows showing what each call returns.

10. sprintf and snprintf — Building strings

Format and write output into a string buffer instead of printing to the screen.

Syntax

int sprintf(char *buf, const char *format, ...);
int snprintf(char *buf, size_t size, const char *format, ...);

Usage

#include <stdio.h>

int main() {
    char message[100];
    int age = 22;
    char name[] = "Arjun";

    // Build a formatted string
    snprintf(message, sizeof(message), "My name is %s and I am %d years old.", name, age);
    printf("%s\n", message);
    // My name is Arjun and I am 22 years old.

    // Convert integer to string
    char num_str[20];
    snprintf(num_str, sizeof(num_str), "%d", 12345);
    printf("String: %s\n", num_str);   // String: 12345

    // Build a filename dynamically
    char filename[50];
    int report_id = 7;
    snprintf(filename, sizeof(filename), "report_%03d.txt", report_id);
    printf("%s\n", filename);   // report_007.txt

    return 0;
}

Output

My name is Arjun and I am 22 years old.
String: 12345
report_007.txt

Always use snprintf over sprintf. The n version limits how many characters are written, preventing buffer overflow. sprintf has the same danger as strcpy — no bounds checking.

11. Write your own — strlen

Now, the part that makes this guide different from every other one. We build each function from scratch. No black boxes. Every line explained.

#include <stdio.h>

size_t my_strlen(const char *str) {
    size_t length = 0;

    // Walk forward until we hit the null terminator
    while (str[length] != '\0') {
        length++;
    }

    return length;
}

int main() {
    printf("%zu\n", my_strlen("Hello"));         // 5
    printf("%zu\n", my_strlen("C Programming")); // 13
    printf("%zu\n", my_strlen(""));              // 0
    return 0;
}

What is happening

  • Start at index 0.
  • Keep incrementing as long as the character is not ‘\0‘.
  • The moment we hit ‘\0’, length holds the count of all characters before it.
  • Return that count.

Pointer version — more idiomatic C

size_t my_strlen_ptr(const char *str) {
    const char *start = str;

    while (*str != '\0')
        str++;

    return str - start;   // Pointer subtraction = number of elements between them
}

Both versions are correct. The pointer version is how most real implementations look.

12. Write your own — strcpy

#include <stdio.h>

char *my_strcpy(char *dest, const char *src) {
    char *original_dest = dest;   // Save start position to return later

    // Copy each character including the null terminator
    while (*src != '\0') {
        *dest = *src;
        dest++;
        src++;
    }

    *dest = '\0';   // Don't forget to null-terminate the destination

    return original_dest;
}

int main() {
    char buffer[20];

    my_strcpy(buffer, "Hello World");
    printf("%s\n", buffer);   // Hello World

    my_strcpy(buffer, "C");
    printf("%s\n", buffer);   // C

    return 0;
}

Compact version using pointer idiom

char *my_strcpy_compact(char *dest, const char *src) {
    char *start = dest;
    while ((*dest++ = *src++) != '\0');
    return start;
}

This single line while ((*dest++ = *src++) != '\0') is classic C. It copies the character, increments both pointers, and checks if the copied character was ‘\0’. When it copies the null terminator, the condition becomes false and the loop ends.

Self version with bounds checking

char *my_strncpy(char *dest, const char *src, size_t n) {
    size_t i;

    for (i = 0; i < n - 1 && src[i] != '\0'; i++)
        dest[i] = src[i];

    dest[i] = '\0';   // Always null-terminate
    return dest;
}

13. Write your own — strcmp

#include <stdio.h>

int my_strcmp(const char *str1, const char *str2) {

    // Walk both strings simultaneously
    while (*str1 != '\0' && *str2 != '\0') {

        if (*str1 != *str2) {
            // Found a difference — return which is larger
            return (unsigned char)*str1 - (unsigned char)*str2;
        }

        str1++;
        str2++;
    }

    // Reached end of one or both strings
    // If both ended: equal (return 0)
    // If one ended first: the shorter one is "less"
    return (unsigned char)*str1 - (unsigned char)*str2;
}

int main() {
    printf("%d\n", my_strcmp("apple", "apple"));    // 0
    printf("%d\n", my_strcmp("apple", "apply"));    // negative
    printf("%d\n", my_strcmp("zebra", "apple"));    // positive
    printf("%d\n", my_strcmp("abc", "abcd"));       // negative (shorter)
    return 0;
}

Why (unsigned char) cast? Because char can be signed on some systems. Without the cast, characters above 127 (like accented letters) would compare incorrectly. The cast ensures consistent, correct behavior with extended characters.

Case-insensitive version

#include <ctype.h>

int my_strcasecmp(const char *a, const char *b) {
    while (*a && *b) {
        int diff = tolower((unsigned char)*a) - tolower((unsigned char)*b);
        if (diff != 0) return diff;
        a++;
        b++;
    }
    return tolower((unsigned char)*a) - tolower((unsigned char)*b);
}

14. Write your own — strcat

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

char *my_strcat(char *dest, const char *src) {
    char *original_dest = dest;

    // Step 1: Walk to the end of dest (find its '\0')
    while (*dest != '\0')
        dest++;

    // Step 2: Copy src starting from that position
    while (*src != '\0') {
        *dest = *src;
        dest++;
        src++;
    }

    // Step 3: Null-terminate the result
    *dest = '\0';

    return original_dest;
}

int main() {
    char result[30] = "Hello";

    my_strcat(result, ", ");
    my_strcat(result, "World");
    my_strcat(result, "!");

    printf("%s\n", result);   // Hello, World!
    return 0;
}

What is happening visually

Start:   dest = "Hello\0............"
                       ↑ Step 1 finds this position

After:   dest = "Hello, World!\0....."
                 ↑ Step 2 writes here from the '\0' onward

The two-step process — find the end, then copy — is exactly what the standard strcat does.

15. Write your own — strrev

The standard C library does not include a string reversal function (though some compilers provide strrev as an extension). Here is a clean, portable implementation:

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

char *my_strrev(char *str) {
    int left  = 0;
    int right = strlen(str) - 1;

    while (left < right) {
        // Swap characters at left and right
        char temp   = str[left];
        str[left]   = str[right];
        str[right]  = temp;

        left++;
        right--;
    }

    return str;
}

int main() {
    char word[] = "programming";
    printf("%s\n", my_strrev(word));   // gnimmargorp

    char palindrome[] = "racecar";
    printf("%s\n", my_strrev(palindrome));   // racecar (unchanged)

    char sentence[] = "Hello";
    printf("%s\n", my_strrev(sentence));   // olleH

    return 0;
}

How the swap works

"Hello"
 H e l l o
 ↑       ↑   Swap H and o → "oellH"
   ↑   ↑     Swap e and l → "olleH"
     ↑       Middle — no swap needed
Result: "olleH"

16. Write your own — strcount

Count how many times a character appears in a string. The standard library does not have this — it is a function you will end up needing in real projects.

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

int my_strcount(const char *str, char ch) {
    int count = 0;

    while (*str != '\0') {
        if (*str == ch)
            count++;
        str++;
    }

    return count;
}

// Count substring occurrences
int my_strcount_sub(const char *str, const char *sub) {
    int count = 0;
    size_t sub_len = strlen(sub);

    while ((str = strstr(str, sub)) != NULL) {
        count++;
        str += sub_len;   // Move past this occurrence
    }

    return count;
}

int main() {
    char text[] = "banana";

    printf("'a' count: %d\n", my_strcount(text, 'a'));       // 3
    printf("'n' count: %d\n", my_strcount(text, 'n'));       // 2
    printf("'z' count: %d\n", my_strcount(text, 'z'));       // 0

    char sentence[] = "the cat sat on the mat";
    printf("'the' count: %d\n", my_strcount_sub(sentence, "the"));  // 2

    return 0;
}

17. Write your own — strpalindrome

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

int my_strpalindrome(const char *str) {
    int left  = 0;
    int right = strlen(str) - 1;

    while (left < right) {
        // Case-insensitive comparison
        if (tolower((unsigned char)str[left]) !=
            tolower((unsigned char)str[right]))
            return 0;   // Not a palindrome

        left++;
        right--;
    }

    return 1;   // Is a palindrome
}

int main() {
    printf("%s\n", my_strpalindrome("racecar") ? "Palindrome" : "Not");  // Palindrome
    printf("%s\n", my_strpalindrome("Madam")   ? "Palindrome" : "Not");  // Palindrome
    printf("%s\n", my_strpalindrome("hello")   ? "Palindrome" : "Not");  // Not
    printf("%s\n", my_strpalindrome("Level")   ? "Palindrome" : "Not");  // Palindrome
    printf("%s\n", my_strpalindrome("a")       ? "Palindrome" : "Not");  // Palindrome
    return 0;
}

18. Write your own — strsplit

Split a string by a delimiter and store the results. This is something you will need constantly in real programs — reading CSV data, parsing config files, processing user input.

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

// Returns array of strings, sets count to number of tokens
// Caller must free each token and the array itself
char **my_strsplit(const char *str, char delimiter, int *count) {
    *count = 0;

    // Count how many tokens we will have
    int tokens = 1;
    for (int i = 0; str[i] != '\0'; i++)
        if (str[i] == delimiter)
            tokens++;

    // Allocate array of pointers
    char **result = (char **)malloc(tokens * sizeof(char *));
    if (!result) return NULL;

    // Make a working copy — strtok modifies the string
    char *copy = (char *)malloc(strlen(str) + 1);
    if (!copy) { free(result); return NULL; }
    strcpy(copy, str);

    // Split using strtok
    char delim_str[2] = {delimiter, '\0'};
    char *token = strtok(copy, delim_str);
    int i = 0;

    while (token != NULL) {
        result[i] = (char *)malloc(strlen(token) + 1);
        strcpy(result[i], token);
        i++;
        token = strtok(NULL, delim_str);
    }

    free(copy);
    *count = tokens;
    return result;
}

int main() {
    int count;
    char **parts = my_strsplit("Alice,25,Engineer,Delhi", ',', &count);

    printf("Found %d parts:\n", count);
    for (int i = 0; i < count; i++) {
        printf("  [%d] %s\n", i, parts[i]);
        free(parts[i]);   // Free each string
    }
    free(parts);   // Free the array

    return 0;
}

Output

Found 4 parts:
  [0] Alice
  [1] 25
  [2] Engineer
  [3] Delhi

19. Conclusion

You started this guide knowing what string functions do. You finish it knowing how they work — and more importantly, you can now write them yourself. That changes everything. When you implement strlen and watch it walk character by character to the null terminator, it stops being a mystery. When you write strcpy and manually null-terminate the destination, you understand instantly why forgetting that byte causes crashes. When you build strsplit from scratch, CSV parsing and config file reading stop feeling like black magic. Here is what you now own:

  • Every major function in <string.h> — not just the syntax but the internal logic, the failure modes, and the safer alternatives.
  • Your own implementations of strlen, strcpy, strcmp, strcat, strrev, strcount, strpalindrome, and strsplit.
  • A complete, working string utility library you can use in real projects today
  • The mental model that makes every future string problem approachable — walk the characters, find the null, do something useful

Strings are behind every input field, every file read, every network message, every configuration file. Mastering them at this level means you are equipped for real C programming — not just the kind that works in tutorials, but the kind that holds up in production. The journey through C’s core concepts is building into something powerful. You understand arrays, pointers, their relationship, strings, and now string functions at a deep level.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top