String Functions in C: Complete Guide

Introduction to String Functions

In C, strings are arrays of characters terminated by a null character (\0). The C standard library provides a rich set of functions for string manipulation through the <string.h> header. Understanding these functions is crucial for effective C programming.


String Functions Overview

String Functions Categories
├── String Length
│   └── strlen()
├── String Copy
│   ├── strcpy()
│   ├── strncpy()
│   └── memcpy()
├── String Concatenation
│   ├── strcat()
│   └── strncat()
├── String Comparison
│   ├── strcmp()
│   ├── strncmp()
│   └── strcasecmp() / stricmp()
├── String Search
│   ├── strchr()
│   ├── strrchr()
│   ├── strstr()
│   └── strpbrk()
├── String Tokenization
│   └── strtok()
└── Memory Operations
├── memcpy()
├── memmove()
├── memset()
└── memcmp()

String Length Functions

strlen() - Get String Length

#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "Hello";
char str2[] = "Hello, World!";
char str3[] = "";
printf("String: \"%s\"\n", str1);
printf("Length: %zu\n\n", strlen(str1));
printf("String: \"%s\"\n", str2);
printf("Length: %zu\n\n", strlen(str2));
printf("String: \"%s\" (empty)\n", str3);
printf("Length: %zu\n\n", strlen(str3));
// Manual implementation of strlen
size_t myStrlen(const char *s) {
const char *p = s;
while (*p) p++;
return p - s;
}
printf("Using custom strlen: %zu\n", myStrlen("Test"));
return 0;
}

Output:

String: "Hello"
Length: 5
String: "Hello, World!"
Length: 13
String: "" (empty)
Length: 0
Using custom strlen: 4

String Copy Functions

strcpy() - Copy String

#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello, World!";
char dest[20];  // Make sure destination is large enough
printf("Source: \"%s\"\n", src);
strcpy(dest, src);
printf("Destination after strcpy: \"%s\"\n", dest);
// Warning: strcpy doesn't check buffer size
char small[5];
// strcpy(small, src);  // BUFFER OVERFLOW! Don't do this
// Safe alternative using strncpy
char safe[10];
strncpy(safe, src, sizeof(safe) - 1);
safe[sizeof(safe) - 1] = '\0';  // Ensure null termination
printf("Safe copy (truncated): \"%s\"\n", safe);
return 0;
}

Output:

Source: "Hello, World!"
Destination after strcpy: "Hello, World!"
Safe copy (truncated): "Hello, Wo"

strncpy() - Copy with Length Limit

#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello, World!";
char dest1[10];
char dest2[20];
printf("Source: \"%s\" (length: %zu)\n\n", src, strlen(src));
// Copy with limit
strncpy(dest1, src, sizeof(dest1) - 1);
dest1[sizeof(dest1) - 1] = '\0';
printf("dest1 (size 10): \"%s\"\n", dest1);
// Copy full string
strncpy(dest2, src, sizeof(dest2) - 1);
dest2[sizeof(dest2) - 1] = '\0';
printf("dest2 (size 20): \"%s\"\n", dest2);
// Note: strncpy doesn't add null terminator if limit reached
char dest3[5];
strncpy(dest3, "Hello", 5);  // No null terminator!
dest3[4] = '\0';  // Must add manually
printf("dest3 (exact fit): \"%s\"\n", dest3);
return 0;
}

Output:

Source: "Hello, World!" (length: 13)
dest1 (size 10): "Hello, Wo"
dest2 (size 20): "Hello, World!"
dest3 (exact fit): "Hell"

String Concatenation Functions

strcat() - Concatenate Strings

#include <stdio.h>
#include <string.h>
int main() {
char str1[50] = "Hello";
char str2[] = ", ";
char str3[] = "World";
char str4[] = "!";
printf("Initial: \"%s\"\n", str1);
strcat(str1, str2);
printf("After strcat(str2): \"%s\"\n", str1);
strcat(str1, str3);
printf("After strcat(str3): \"%s\"\n", str1);
strcat(str1, str4);
printf("After strcat(str4): \"%s\"\n", str1);
// Safe concatenation with strncat
char safe[20] = "Hello";
strncat(safe, ", World!", sizeof(safe) - strlen(safe) - 1);
printf("Safe concatenation: \"%s\"\n", safe);
return 0;
}

Output:

Initial: "Hello"
After strcat(str2): "Hello, "
After strcat(str3): "Hello, World"
After strcat(str4): "Hello, World!"
Safe concatenation: "Hello, World!"

Building Strings with strncat()

#include <stdio.h>
#include <string.h>
int main() {
char buffer[50] = "";
char *words[] = {"The", "quick", "brown", "fox", "jumps"};
int wordCount = sizeof(words) / sizeof(words[0]);
printf("Building a sentence:\n");
for (int i = 0; i < wordCount; i++) {
// Add space before each word except first
if (i > 0) {
strncat(buffer, " ", sizeof(buffer) - strlen(buffer) - 1);
}
strncat(buffer, words[i], sizeof(buffer) - strlen(buffer) - 1);
printf("Step %d: \"%s\"\n", i + 1, buffer);
}
// Add period
strncat(buffer, ".", sizeof(buffer) - strlen(buffer) - 1);
printf("\nFinal: \"%s\"\n", buffer);
return 0;
}

String Comparison Functions

strcmp() - Compare Strings

#include <stdio.h>
#include <string.h>
void compareStrings(const char *s1, const char *s2) {
int result = strcmp(s1, s2);
printf("\"%s\" vs \"%s\": ", s1, s2);
if (result < 0) {
printf("\"%s\" comes before \"%s\"\n", s1, s2);
} else if (result > 0) {
printf("\"%s\" comes after \"%s\"\n", s1, s2);
} else {
printf("Strings are equal\n");
}
}
int main() {
compareStrings("apple", "banana");
compareStrings("banana", "apple");
compareStrings("hello", "hello");
compareStrings("Hello", "hello");  // Case sensitive
printf("\nUsing strncmp (compare first n characters):\n");
printf("strncmp(\"Hello\", \"Help\", 3) = %d\n", strncmp("Hello", "Help", 3));
printf("strncmp(\"Hello\", \"Help\", 4) = %d\n", strncmp("Hello", "Help", 4));
return 0;
}

Output:

"apple" vs "banana": "apple" comes before "banana"
"banana" vs "apple": "banana" comes after "apple"
"hello" vs "hello": Strings are equal
"Hello" vs "hello": "Hello" comes before "hello"
Using strncmp (compare first n characters):
strncmp("Hello", "Help", 3) = 0
strncmp("Hello", "Help", 4) = -1

Case-Insensitive Comparison

#include <stdio.h>
#include <string.h>
#include <ctype.h>
// Case-insensitive string comparison (not standard)
int strcasecmp_custom(const char *s1, const char *s2) {
while (*s1 && *s2) {
int c1 = tolower(*s1);
int c2 = tolower(*s2);
if (c1 != c2) {
return c1 - c2;
}
s1++;
s2++;
}
return tolower(*s1) - tolower(*s2);
}
int main() {
char *str1 = "Hello World";
char *str2 = "HELLO WORLD";
char *str3 = "Hello There";
printf("Case-sensitive comparison:\n");
printf("strcmp(\"%s\", \"%s\") = %d\n", str1, str2, strcmp(str1, str2));
printf("strcmp(\"%s\", \"%s\") = %d\n\n", str1, str3, strcmp(str1, str3));
printf("Case-insensitive comparison:\n");
printf("strcasecmp_custom(\"%s\", \"%s\") = %d\n", 
str1, str2, strcasecmp_custom(str1, str2));
printf("strcasecmp_custom(\"%s\", \"%s\") = %d\n", 
str1, str3, strcasecmp_custom(str1, str3));
return 0;
}

String Search Functions

strchr() and strrchr() - Find Character

#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello, World! Welcome to C programming.";
char ch = 'o';
printf("String: \"%s\"\n", str);
printf("Searching for character: '%c'\n\n", ch);
// Find first occurrence
char *first = strchr(str, ch);
if (first) {
printf("First occurrence at position: %ld\n", first - str);
printf("From first occurrence: \"%s\"\n", first);
}
// Find last occurrence
char *last = strrchr(str, ch);
if (last) {
printf("Last occurrence at position: %ld\n", last - str);
printf("From last occurrence: \"%s\"\n", last);
}
// Find all occurrences
printf("\nAll occurrences of '%c' at positions: ", ch);
char *ptr = str;
while ((ptr = strchr(ptr, ch)) != NULL) {
printf("%ld ", ptr - str);
ptr++;
}
printf("\n");
return 0;
}

Output:

String: "Hello, World! Welcome to C programming."
Searching for character: 'o'
First occurrence at position: 4
From first occurrence: "o, World! Welcome to C programming."
Last occurrence at position: 28
From last occurrence: "ogramming."
All occurrences of 'o' at positions: 4 8 18 25 28

strstr() - Find Substring

#include <stdio.h>
#include <string.h>
int main() {
char text[] = "The quick brown fox jumps over the lazy dog.";
char search[] = "fox";
char replace[] = "cat";
printf("Text: \"%s\"\n", text);
printf("Searching for: \"%s\"\n\n", search);
char *found = strstr(text, search);
if (found) {
printf("Found at position: %ld\n", found - text);
printf("Context: ...%s\n", found);
// Simple replacement (not safe for different lengths)
char result[100];
strncpy(result, text, found - text);
result[found - text] = '\0';
strcat(result, replace);
strcat(result, found + strlen(search));
printf("After replacement: \"%s\"\n", result);
} else {
printf("Substring not found\n");
}
// Find all occurrences
printf("\nAll occurrences of 'the' (case-sensitive):\n");
char *ptr = text;
while ((ptr = strstr(ptr, "the")) != NULL) {
printf("Found at position: %ld\n", ptr - text);
ptr++;
}
return 0;
}

Output:

Text: "The quick brown fox jumps over the lazy dog."
Searching for: "fox"
Found at position: 16
Context: ...fox jumps over the lazy dog.
After replacement: "The quick brown cat jumps over the lazy dog."
All occurrences of 'the' (case-sensitive):
Found at position: 31

strpbrk() - Find Any of Multiple Characters

#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello, World! How are you? 123";
char vowels[] = "aeiouAEIOU";
char punctuation[] = ".,!?;:";
char digits[] = "0123456789";
printf("String: \"%s\"\n\n", str);
// Find first vowel
char *vowel = strpbrk(str, vowels);
if (vowel) {
printf("First vowel: '%c' at position %ld\n", *vowel, vowel - str);
}
// Find first punctuation
char *punct = strpbrk(str, punctuation);
if (punct) {
printf("First punctuation: '%c' at position %ld\n", *punct, punct - str);
}
// Find first digit
char *digit = strpbrk(str, digits);
if (digit) {
printf("First digit: '%c' at position %ld\n", *digit, digit - str);
}
// Find all punctuation
printf("\nAll punctuation marks:\n");
char *ptr = str;
while ((ptr = strpbrk(ptr, punctuation)) != NULL) {
printf("Found '%c' at position %ld\n", *ptr, ptr - str);
ptr++;
}
return 0;
}

String Tokenization

strtok() - Split String into Tokens

#include <stdio.h>
#include <string.h>
int main() {
char str[] = "apple,banana,orange,grape,kiwi";
char str2[] = "Hello|World|How|Are|You";
char str3[] = "one,two,three;four:five";
printf("=== Basic Tokenization ===\n");
printf("Original: \"%s\"\n", str);
// strtok modifies the original string - make a copy if needed
char *token = strtok(str, ",");
int count = 1;
while (token != NULL) {
printf("Token %d: \"%s\"\n", count++, token);
token = strtok(NULL, ",");
}
printf("\n=== Multiple Delimiters ===\n");
printf("Original: \"%s\"\n", str3);
char copy[100];
strcpy(copy, str3);
token = strtok(copy, ",;:");
count = 1;
while (token != NULL) {
printf("Token %d: \"%s\"\n", count++, token);
token = strtok(NULL, ",;:");
}
return 0;
}

Output:

=== Basic Tokenization ===
Original: "apple,banana,orange,grape,kiwi"
Token 1: "apple"
Token 2: "banana"
Token 3: "orange"
Token 4: "grape"
Token 5: "kiwi"
=== Multiple Delimiters ===
Original: "one,two,three;four:five"
Token 1: "one"
Token 2: "two"
Token 3: "three"
Token 4: "four"
Token 5: "five"

Advanced Tokenization Example

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Function to count tokens
int countTokens(char *str, const char *delim) {
char copy[256];
strcpy(copy, str);
int count = 0;
char *token = strtok(copy, delim);
while (token != NULL) {
count++;
token = strtok(NULL, delim);
}
return count;
}
// Function to extract tokens into array
char** tokenize(char *str, const char *delim, int *tokenCount) {
*tokenCount = countTokens(str, delim);
char **tokens = malloc(*tokenCount * sizeof(char*));
if (tokens == NULL) return NULL;
char copy[256];
strcpy(copy, str);
int i = 0;
char *token = strtok(copy, delim);
while (token != NULL) {
tokens[i] = malloc(strlen(token) + 1);
strcpy(tokens[i], token);
i++;
token = strtok(NULL, delim);
}
return tokens;
}
void freeTokens(char **tokens, int count) {
for (int i = 0; i < count; i++) {
free(tokens[i]);
}
free(tokens);
}
int main() {
char input[] = "user:password@host:port/database?param=value";
printf("Parsing connection string: \"%s\"\n\n", input);
char *copy = malloc(strlen(input) + 1);
strcpy(copy, input);
// Parse different parts
char *userInfo = strtok(copy, "@");
char *hostPart = strtok(NULL, "/");
char *dbPart = strtok(NULL, "?");
char *params = strtok(NULL, "");
if (userInfo) {
char *user = strtok(userInfo, ":");
char *pass = strtok(NULL, ":");
printf("User: %s\n", user);
printf("Password: %s\n", pass);
}
if (hostPart) {
char *host = strtok(hostPart, ":");
char *port = strtok(NULL, ":");
printf("Host: %s\n", host);
if (port) printf("Port: %s\n", port);
}
if (dbPart) printf("Database: %s\n", dbPart);
if (params) printf("Parameters: %s\n", params);
free(copy);
return 0;
}

Memory Operations

memcpy() and memmove() - Copy Memory

#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello, World!";
char dest1[20];
char dest2[20];
printf("memcpy - copies memory (no overlap allowed):\n");
memcpy(dest1, src, strlen(src) + 1);  // +1 for null terminator
printf("dest1: \"%s\"\n\n", dest1);
printf("memmove - handles overlapping regions:\n");
char overlap[20] = "123456789";
printf("Original: \"%s\"\n", overlap);
// Copy overlapping regions (memcpy would fail)
memmove(overlap + 2, overlap, 5);
printf("After memmove: \"%s\"\n", overlap);
return 0;
}

Output:

memcpy - copies memory (no overlap allowed):
dest1: "Hello, World!"
memmove - handles overlapping regions:
Original: "123456789"
After memmove: "121234589"

memset() - Set Memory

#include <stdio.h>
#include <string.h>
int main() {
char buffer[50];
// Fill with zeros
memset(buffer, 0, sizeof(buffer));
printf("After memset zero: \"%s\"\n", buffer);
// Fill with 'A'
memset(buffer, 'A', 10);
buffer[10] = '\0';
printf("After memset 'A'x10: \"%s\"\n", buffer);
// Fill with '-'
memset(buffer, '-', 20);
buffer[20] = '\0';
printf("After memset '-'x20: \"%s\"\n", buffer);
// Practical use: zero out sensitive data
char password[20] = "secret123";
printf("\nPassword: \"%s\"\n", password);
// Clear password from memory
memset(password, 0, sizeof(password));
printf("After clearing: \"%s\"\n", password);
return 0;
}

memcmp() - Compare Memory

#include <stdio.h>
#include <string.h>
int main() {
char arr1[] = {1, 2, 3, 4, 5};
char arr2[] = {1, 2, 3, 4, 5};
char arr3[] = {1, 2, 3, 4, 6};
int result1 = memcmp(arr1, arr2, sizeof(arr1));
int result2 = memcmp(arr1, arr3, sizeof(arr1));
printf("arr1 vs arr2: %d (", result1);
if (result1 == 0) printf("equal");
printf(")\n");
printf("arr1 vs arr3: %d (", result2);
if (result2 < 0) printf("arr1 < arr3");
else if (result2 > 0) printf("arr1 > arr3");
printf(")\n");
// Compare first 4 bytes
int result3 = memcmp(arr1, arr3, 4);
printf("First 4 bytes: %d\n", result3);
return 0;
}

Practical String Examples

1. String Reversal

#include <stdio.h>
#include <string.h>
void reverseString(char *str) {
int length = strlen(str);
int start = 0;
int end = length - 1;
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
void reverseWords(char *str) {
// First reverse the entire string
reverseString(str);
// Then reverse each word
char *wordStart = str;
char *wordEnd = str;
while (*wordEnd) {
if (*wordEnd == ' ') {
char temp = *wordEnd;
*wordEnd = '\0';
reverseString(wordStart);
*wordEnd = temp;
wordStart = wordEnd + 1;
}
wordEnd++;
}
// Reverse last word
reverseString(wordStart);
}
int main() {
char str1[] = "Hello, World!";
char str2[] = "The quick brown fox";
printf("Original: \"%s\"\n", str1);
reverseString(str1);
printf("Reversed: \"%s\"\n\n", str1);
printf("Original: \"%s\"\n", str2);
reverseWords(str2);
printf("Words reversed: \"%s\"\n", str2);
return 0;
}

2. Palindrome Checker

#include <stdio.h>
#include <string.h>
#include <ctype.h>
int isPalindrome(const char *str) {
int left = 0;
int right = strlen(str) - 1;
while (left < right) {
// Skip non-alphanumeric characters
while (left < right && !isalnum(str[left])) left++;
while (left < right && !isalnum(str[right])) right--;
if (tolower(str[left]) != tolower(str[right])) {
return 0;
}
left++;
right--;
}
return 1;
}
int main() {
char *testStrings[] = {
"racecar",
"A man, a plan, a canal: Panama",
"Hello World",
"Madam",
"Was it a car or a cat I saw?"
};
int count = sizeof(testStrings) / sizeof(testStrings[0]);
for (int i = 0; i < count; i++) {
printf("\"%s\"\n", testStrings[i]);
printf("  -> %s\n\n", isPalindrome(testStrings[i]) ? "Palindrome" : "Not a palindrome");
}
return 0;
}

3. String Compression

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* compressString(const char *str) {
int len = strlen(str);
char *compressed = malloc(2 * len + 1);  // Worst case
int index = 0;
for (int i = 0; i < len; i++) {
int count = 1;
// Count consecutive characters
while (i + 1 < len && str[i] == str[i + 1]) {
count++;
i++;
}
// Append character and count
compressed[index++] = str[i];
if (count > 1) {
index += sprintf(compressed + index, "%d", count);
}
}
compressed[index] = '\0';
// Return original if compression doesn't help
if (strlen(compressed) >= len) {
free(compressed);
return strdup(str);
}
return compressed;
}
int main() {
char *testStrings[] = {
"aabcccccaaa",
"abcdef",
"aaabbbccc",
"aabbcc"
};
int count = sizeof(testStrings) / sizeof(testStrings[0]);
for (int i = 0; i < count; i++) {
char *compressed = compressString(testStrings[i]);
printf("Original: \"%s\" (%zu chars)\n", 
testStrings[i], strlen(testStrings[i]));
printf("Compressed: \"%s\" (%zu chars)\n", 
compressed, strlen(compressed));
printf("\n");
if (compressed != testStrings[i]) {
free(compressed);
}
}
return 0;
}

4. String Formatting

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
// Custom string formatter (simplified)
char* formatString(const char *format, ...) {
static char buffer[1024];
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);  // vsnprintf would be safer
va_end(args);
return buffer;
}
int main() {
char name[] = "Alice";
int age = 25;
double salary = 54321.50;
char *result = formatString("Name: %s, Age: %d, Salary: $%.2f", 
name, age, salary);
printf("%s\n", result);
// Building formatted strings with sprintf
char logEntry[200];
sprintf(logEntry, "[%s] User %s logged in at %d", 
"INFO", "bob", 123456);
printf("Log entry: %s\n", logEntry);
// Using snprintf for safety
char safeBuffer[50];
int written = snprintf(safeBuffer, sizeof(safeBuffer), 
"This is a very long string that might overflow");
printf("Safe buffer: \"%s\" (wrote %d chars)\n", safeBuffer, written);
return 0;
}

String Utilities Library

string_utils.h

#ifndef STRING_UTILS_H
#define STRING_UTILS_H
#include <stdbool.h>
// String utilities
int string_length(const char *str);
void string_copy(char *dest, const char *src, int max_len);
void string_concat(char *dest, const char *src, int max_len);
int string_compare(const char *s1, const char *s2);
int string_compare_ignore_case(const char *s1, const char *s2);
char* string_find_char(const char *str, char ch);
char* string_find_substring(const char *str, const char *sub);
void string_reverse(char *str);
void string_to_upper(char *str);
void string_to_lower(char *str);
bool string_starts_with(const char *str, const char *prefix);
bool string_ends_with(const char *str, const char *suffix);
char* string_trim(char *str);
char** string_split(const char *str, const char *delimiter, int *count);
void string_free_split(char **tokens, int count);
char* string_replace(const char *str, const char *old, const char *new);
#endif

string_utils.c

#include "string_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int string_length(const char *str) {
const char *s = str;
while (*s) s++;
return s - str;
}
void string_copy(char *dest, const char *src, int max_len) {
int i = 0;
while (src[i] && i < max_len - 1) {
dest[i] = src[i];
i++;
}
dest[i] = '\0';
}
void string_concat(char *dest, const char *src, int max_len) {
int dest_len = string_length(dest);
int i = 0;
while (src[i] && dest_len + i < max_len - 1) {
dest[dest_len + i] = src[i];
i++;
}
dest[dest_len + i] = '\0';
}
int string_compare(const char *s1, const char *s2) {
while (*s1 && *s2 && *s1 == *s2) {
s1++;
s2++;
}
return *s1 - *s2;
}
int string_compare_ignore_case(const char *s1, const char *s2) {
while (*s1 && *s2) {
int c1 = tolower(*s1);
int c2 = tolower(*s2);
if (c1 != c2) {
return c1 - c2;
}
s1++;
s2++;
}
return tolower(*s1) - tolower(*s2);
}
char* string_find_char(const char *str, char ch) {
while (*str) {
if (*str == ch) {
return (char*)str;
}
str++;
}
return NULL;
}
char* string_find_substring(const char *str, const char *sub) {
int sub_len = string_length(sub);
if (sub_len == 0) return (char*)str;
while (*str) {
if (string_compare_n(str, sub, sub_len) == 0) {
return (char*)str;
}
str++;
}
return NULL;
}
void string_reverse(char *str) {
int len = string_length(str);
int start = 0;
int end = len - 1;
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
void string_to_upper(char *str) {
while (*str) {
*str = toupper(*str);
str++;
}
}
void string_to_lower(char *str) {
while (*str) {
*str = tolower(*str);
str++;
}
}
bool string_starts_with(const char *str, const char *prefix) {
while (*prefix) {
if (*str != *prefix) {
return false;
}
str++;
prefix++;
}
return true;
}
bool string_ends_with(const char *str, const char *suffix) {
int str_len = string_length(str);
int suffix_len = string_length(suffix);
if (suffix_len > str_len) {
return false;
}
str += str_len - suffix_len;
while (*suffix) {
if (*str != *suffix) {
return false;
}
str++;
suffix++;
}
return true;
}
char* string_trim(char *str) {
char *end;
// Trim leading space
while (isspace((unsigned char)*str)) str++;
if (*str == 0) return str;
// Trim trailing space
end = str + string_length(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator
*(end + 1) = '\0';
return str;
}
char** string_split(const char *str, const char *delimiter, int *count) {
char *copy = strdup(str);
char **result = NULL;
char *token;
int tokens = 0;
// First pass: count tokens
char *temp = strdup(str);
token = strtok(temp, delimiter);
while (token) {
tokens++;
token = strtok(NULL, delimiter);
}
free(temp);
if (tokens == 0) {
free(copy);
*count = 0;
return NULL;
}
// Allocate result array
result = malloc((tokens + 1) * sizeof(char*));
// Second pass: store tokens
int i = 0;
token = strtok(copy, delimiter);
while (token) {
result[i] = strdup(token);
i++;
token = strtok(NULL, delimiter);
}
result[i] = NULL;
*count = tokens;
return result;
}
void string_free_split(char **tokens, int count) {
for (int i = 0; i < count; i++) {
free(tokens[i]);
}
free(tokens);
}
char* string_replace(const char *str, const char *old, const char *new) {
char *result;
int i, count = 0;
int new_len = string_length(new);
int old_len = string_length(old);
// Count occurrences
for (i = 0; str[i] != '\0'; i++) {
if (strstr(&str[i], old) == &str[i]) {
count++;
i += old_len - 1;
}
}
// Allocate memory for new string
result = malloc(i + count * (new_len - old_len) + 1);
i = 0;
while (*str) {
if (strstr(str, old) == str) {
strcpy(&result[i], new);
i += new_len;
str += old_len;
} else {
result[i++] = *str++;
}
}
result[i] = '\0';
return result;
}

String Functions Comparison Table

FunctionPurposeReturn ValueSafety
strlen(s)Length of sNumber of charsSafe
strcpy(d,s)Copy s to dPointer to dUnsafe (no bounds check)
strncpy(d,s,n)Copy up to n charsPointer to dSafer (may not null-terminate)
strcat(d,s)Append s to dPointer to dUnsafe
strncat(d,s,n)Append up to n charsPointer to dSafer
strcmp(s1,s2)Compare s1 and s2<0,0,>0Safe
strncmp(s1,s2,n)Compare first n chars<0,0,>0Safe
strchr(s,c)Find first cPointer or NULLSafe
strrchr(s,c)Find last cPointer or NULLSafe
strstr(s,sub)Find substringPointer or NULLSafe
strtok(s,d)Tokenize stringToken pointerModifies string
memcpy(d,s,n)Copy memoryPointer to dNo overlap
memmove(d,s,n)Copy memoryPointer to dHandles overlap
memset(s,c,n)Set memoryPointer to sSafe
memcmp(s1,s2,n)Compare memory<0,0,>0Safe

Best Practices

Do's and Don'ts

// DO: Check buffer sizes
char dest[10];
if (strlen(src) < sizeof(dest)) {
strcpy(dest, src);
} else {
// Handle error
}
// DON'T: Ignore buffer sizes
strcpy(dest, src);  // Potential buffer overflow
// DO: Use strncpy with explicit null termination
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
// DON'T: Assume strncpy null-terminates
strncpy(dest, src, sizeof(dest));  // May not be null-terminated
// DO: Use snprintf for safe formatting
snprintf(buffer, sizeof(buffer), "%s %d", name, age);
// DON'T: Use sprintf without bounds
sprintf(buffer, "%s %d", name, age);  // Potential overflow
// DO: Check for NULL pointers
if (str != NULL) {
process(str);
}
// DON'T: Pass NULL to string functions
process(str);  // Crashes if str is NULL

Common Pitfalls

// 1. Forgetting null terminator
char str[5] = {'H', 'e', 'l', 'l', 'o'};  // No null terminator!
printf("%s\n", str);  // Undefined behavior
// 2. Using strcpy with insufficient space
char small[5];
strcpy(small, "Hello, World!");  // Buffer overflow
// 3. Assuming strncpy always null-terminates
char buf[10];
strncpy(buf, "Hello", 10);  // OK - room for null
strncpy(buf, "Hello, World!", 10);  // No null terminator!
// 4. Modifying string literals
char *str = "Hello";
str[0] = 'h';  // Undefined behavior (string literal may be read-only)
// 5. Comparing strings with ==
char s1[] = "Hello";
char s2[] = "Hello";
if (s1 == s2) {  // Compares addresses, not content!
// Won't execute
}
if (strcmp(s1, s2) == 0) {  // Correct way
// Executes
}

Performance Considerations

#include <stdio.h>
#include <string.h>
#include <time.h>
#define ITERATIONS 1000000
int main() {
char str[100] = "Hello, World!";
clock_t start, end;
// strlen called repeatedly (inefficient)
start = clock();
for (int i = 0; i < ITERATIONS; i++) {
for (int j = 0; j < strlen(str); j++) {  // strlen called each iteration
// Do nothing
}
}
end = clock();
printf("strlen in loop: %ld ticks\n", end - start);
// strlen called once (efficient)
start = clock();
int len = strlen(str);
for (int i = 0; i < ITERATIONS; i++) {
for (int j = 0; j < len; j++) {
// Do nothing
}
}
end = clock();
printf("strlen outside: %ld ticks\n", end - start);
return 0;
}

Conclusion

String functions in C provide powerful tools for text manipulation:

Key Takeaways

  1. Always check buffer sizes to prevent overflow
  2. Use safer alternatives like strncpy, strncat, snprintf
  3. Remember null termination when using bounded functions
  4. Check return values especially for search functions
  5. Be careful with string literals (read-only)
  6. Use strcmp() not == for string comparison
  7. strtok() modifies the original string
  8. Consider performance for repeated operations

When to Use Which Function

  • Copying: strcpy (safe size), strncpy (bounded)
  • Concatenation: strcat (safe size), strncat (bounded)
  • Comparison: strcmp (full), strncmp (partial)
  • Search: strchr (character), strstr (substring)
  • Tokenization: strtok (simple), custom parser (complex)
  • Memory: memcpy (no overlap), memmove (with overlap)

Mastering string functions is essential for C programming, as strings are used in virtually every application.

Leave a Reply

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


Macro Nepal Helper