HOWTO · C
How to Check if String Contains Substring in C
Check whether a C string contains an exact substring with the standard strstr function, a manual loop, or the GNU strcasestr extension for case-insensitive matching.
On this page
To check whether a null-terminated C string contains an exact, case-sensitive substring, call strstr from <string.h>. It returns a pointer to the first match or NULL when no match exists. Each example below was executed and printed Substring found..
Check for a substring with strstr
Pass the full string as the first argument and the text to find as the second. Test the returned pointer directly; do not print it as an address, since that value depends on the process. strstr is declared by the standard C header <string.h> and compares characters exactly, including their case.
This program checks for welcome in a sentence and prints a stable found/not-found message.
#include <stdio.h>
#include <string.h>
int main(void) {
const char *text = "Hello, welcome to C.";
const char *needle = "welcome";
if (strstr(text, needle) != NULL) {
puts("Substring found.");
} else {
puts("Substring not found.");
}
return 0;
}
Substring found.
The returned pointer points at the first matching character in text, so you can use it to inspect or continue from that position. A non-null pointer means a match exists. If the search text is "", strstr returns the start of text; account for that standard behavior if an empty search term should not count as a match in your application.
Search for a substring with a loop
A manual search can make the comparison process explicit or provide a place to add application-specific behavior. The function below accepts two non-null, null-terminated strings and returns 1 when the search text occurs and 0 otherwise. It checks lengths before subtracting them, avoiding an unsigned underflow when the search text is longer than the input. It treats an empty search text as a match, consistent with strstr.
#include <stdio.h>
#include <string.h>
int contains_substring(const char *text, const char *needle) {
size_t text_len = strlen(text);
size_t needle_len = strlen(needle);
if (needle_len == 0) {
return 1;
}
if (needle_len > text_len) {
return 0;
}
for (size_t i = 0; i <= text_len - needle_len; ++i) {
size_t j = 0;
while (j < needle_len && text[i + j] == needle[j]) {
++j;
}
if (j == needle_len) {
return 1;
}
}
return 0;
}
int main(void) {
const char *text = "Hello, welcome to C.";
const char *needle = "welcome";
puts(contains_substring(text, needle)
? "Substring found."
: "Substring not found.");
return 0;
}
Substring found.
The outer loop tries each starting position where the entire search text can fit. The inner loop advances only while corresponding characters match. If it reaches the search text’s length, the function has found a complete match. This straightforward method is useful for learning or customization; for ordinary exact matching, prefer the standard library function unless you have a reason to maintain your own search code.
Check for a case-insensitive substring
The C standard library does not define a case-insensitive substring function. On GNU systems, strcasestr searches without regard to case and is provided as a GNU extension. Define _GNU_SOURCE before including headers to request its declaration on systems that use this feature-test macro. This function is not portable standard C, so check that the target platform provides it before relying on it.
This complete example uses ASCII text and shows the GNU-specific call. It prints Substring found..
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
int main(void) {
const char *text = "Hello, Welcome to C.";
const char *needle = "welcome";
if (strcasestr(text, needle) != NULL) {
puts("Substring found.");
} else {
puts("Substring not found.");
}
return 0;
}
Substring found.
For portable code, implement case-insensitive matching with an explicit character policy rather than silently changing the input strings. The tolower function can help with byte-oriented text, but its argument must be EOF or representable as unsigned char, and its behavior follows the active C locale. That approach does not by itself provide Unicode case folding. Choose a library and text policy that match the data your program needs to process.
Handle edge cases and choose a method
Both strstr and the loop above search null-terminated strings and compare exact character sequences. If the needle is longer than the text, it cannot match. If the needle is empty, strstr reports a match at the start, and the loop implementation above follows the same convention; add an explicit check if your application wants a different rule. The loop requires non-null pointers, just as standard string functions require valid string arguments.
strstr returns the first occurrence. To find later occurrences, continue searching from the previous match plus the needle’s length, while ensuring that the needle is nonempty so the next search advances. If overlapping matches matter, advance by one character from the previous match instead. The choice depends on whether occurrences may share characters.
Use strstr for the normal exact, case-sensitive check: it is concise and part of standard C. Use a manual loop when you need to teach or customize the comparison, and document its input assumptions. Use strcasestr only when the GNU extension is available and its byte and locale behavior suits your text. strpbrk is not an alternative for exact substring matching: it checks whether any character from a set occurs in the input, which answers a different question.