C in 100 Seconds: String Functions — strcat, strchr, strstr
Video: C in 100 Seconds: String Functions — strcat, strchr, strstr | Episode 32 by Taught by Celeste AI - AI Coding Coach
C String Functions: strcat, strchr, strstr
strcat(dest, src)to append.strchr(s, c)to find a character.strstr(s, sub)to find a substring. Three more<string.h>essentials.
We covered strlen, strcpy, strcmp in episode 11. Today: three more — concatenation, char search, substring search.
The basic shape
#include <stdio.h>
#include <string.h>
int main() {
char greeting[50] = "Hello";
strcat(greeting, " World");
printf("strcat: %s\n", greeting); // Hello World
char buf[20] = "Hi";
strncat(buf, " there, friend!", 6);
printf("strncat: %s\n", buf); // Hi there
char path[] = "/home/user/docs/file.txt";
char *dot = strchr(path, '.');
printf("strchr: %s\n", dot); // .txt
char sentence[] = "The quick brown fox";
char *found = strstr(sentence, "brown");
printf("strstr: %s\n", found); // brown fox
return 0;
}
strcat — concatenate
char *strcat(char *dest, const char *src);
Appends src to dest. The destination must have enough space for both strings + null terminator.
char greeting[50] = "Hello";
strcat(greeting, " World");
// greeting is now "Hello World" (11 chars + \0 = 12 bytes used)
Buffer overflow risk: if src doesn't fit, strcat writes past the end of dest. Catastrophic — same security issue as strcpy.
strncat — bounded concatenate
char *strncat(char *dest, const char *src, size_t n);
Appends at most n chars from src. n is the chars from src, not the total buffer size.
char buf[20] = "Hi";
strncat(buf, " there, friend!", 6); // appends " there"
// buf is now "Hi there"
The function appends min(n, strlen(src)) chars and adds a \0. The danger: the resulting string can be up to strlen(dest) + n bytes. Make sure dest has room.
For truly safe append, the modern idiom is snprintf:
char buf[20] = "Hi";
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s", " there, friend!");
// always null-terminated, never overflows
Verbose but bulletproof.
strchr — find first character
char *strchr(const char *s, int c);
Returns pointer to the first occurrence of c in s, or NULL if not found.
char path[] = "/home/user/docs/file.txt";
char *dot = strchr(path, '.');
// dot points to the '.' before "txt"
printf("%s\n", dot); // ".txt" (from the dot to the end)
The returned pointer is into the original string — not a copy. Modifying through it modifies the original.
For finding the last occurrence, use strrchr (note the extra r):
char *last_dot = strrchr(path, '.');
// finds the final '.' if there are multiple
strchr(s, '\0') returns a pointer to the null terminator — useful for finding the end of a string.
strstr — find substring
char *strstr(const char *haystack, const char *needle);
Returns pointer to the first occurrence of needle in haystack, or NULL.
char sentence[] = "The quick brown fox";
char *found = strstr(sentence, "brown");
// found points to the 'b' in "brown"
printf("%s\n", found); // "brown fox"
For empty needle, returns haystack (the whole string matches).
For case-insensitive search, strcasestr (POSIX, not standard C):
char *p = strcasestr(text, "hello"); // matches "Hello", "HELLO", etc.
Combining
// Get filename extension
char path[] = "/home/user/docs/file.txt";
char *ext = strrchr(path, '.');
if (ext != NULL) {
ext++; // skip the dot
printf("Extension: %s\n", ext); // "txt"
}
// Replace word in place (only if same length)
char text[] = "The quick brown fox";
char *p = strstr(text, "brown");
if (p) {
memcpy(p, "gray ", 5); // same length
}
Other string functions worth knowing
strdup(s)— malloc and copy a string. Free when done.strspn(s, chars)/strcspn(s, chars)— count chars matching / not matching a set.strpbrk(s, chars)— find any of several chars.strtok(s, delim)— tokenize (covered in episode 33; has thread-safety quirks).
For Unicode, <string.h> is byte-oriented — it works for UTF-8 only because UTF-8's design avoids embedded zeros and lets ASCII operations work on ASCII subsets. For real multi-byte work, use ICU or roll your own.
strcat in a loop is O(n²)
char buf[1000] = "";
for (int i = 0; i < 100; i++) {
strcat(buf, "x"); // strcat walks to the end each time
}
Each strcat does strlen(buf) to find the end, then copies. So 100 calls = 100 × O(len) = O(n²) total. For long strings, very slow.
The fix: track the length:
char buf[1000] = "";
size_t len = 0;
for (int i = 0; i < 100; i++) {
buf[len++] = 'x';
}
buf[len] = '\0';
Or use snprintf which gives you the position:
char buf[1000] = "";
size_t pos = 0;
for (int i = 0; i < 100; i++) {
pos += snprintf(buf + pos, sizeof(buf) - pos, "x");
}
Common mistakes
Buffer too small for strcat. Classic overflow. Always check sizes.
strncat's n is from src, not total. A common confusion. The actual total can exceed n.
Forgetting to check NULL from strchr/strstr. "Pointer to char not found" = NULL. Dereferencing crashes.
Modifying through const char *. strchr on const char * returns char * (legacy ABI quirk). The returned pointer is technically writable, but if the source was actually const, modifying is undefined.
strcat quadratic behaviour. Each call walks to the null terminator. For loops, track position manually or use snprintf.
What's next
Episode 33: strtok — string tokenizing. Split a string by delimiters. Has thread-safety surprises.
Recap
strcat(dest, src) — append, unsafe; use snprintf(dest+len, room, "%s", src) for safety. strncat(dest, src, n) — at most n chars from src; total still grows. strchr(s, c) — first occurrence of char (NULL if missing). strrchr — last occurrence. strstr(haystack, needle) — first occurrence of substring. All return pointers into the original string. Watch for buffer overflows on writes; check NULL on searches.
Next episode: strtok.