C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
98: String Searching: strchr, strstr, strtok
I see this all the time when developers move from languages like Python or JavaScript to C: they treat strtok as if it's a "split" function that returns a neat array of substrings. They assume the original string stays intact and that the function just gives them pointers to the pieces. It's a dangerous assumption that leads to some of the most frustrating "heisenbugs" in C.
strtok Doesn't Split Strings; It Mutilates Them
Let me show you exactly why that thinking is wrong. Imagine you have a configuration string that you want to process twice—perhaps once to log the original and once to parse the values.
char config[] = "timeout=30,retry=5,mode=async";
char *token = strtok(config, ",");
// Now you think config is still "timeout=30,retry=5,mode=async"
printf("Original: %s\n", config);
If you run this, you won't see the whole string. You'll see "timeout=30". Why? Because strtok doesn't just find the comma; it replaces that comma with a null terminator (\0). It literally chops your string into pieces in place. If you need the original string later, you're out of luck unless you made a copy with strdup or strcpy first. I've spent way too many hours debugging programs where a string "randomly" got shortened because some utility function called strtok under the hood.
Finding Needles in Haystacks with strchr and strstr
If you just need to find something without destroying your data, strchr and strstr are your best friends. They are "read-only" operations.
strchr looks for a single character. It's incredibly useful for finding the end of a prefix or a specific delimiter. For example, if you're parsing a file path and need to find the first slash:
char path[] = "/usr/local/bin/gcc";
char *first_slash = strchr(path, '/');
if (first_slash) {
// first_slash points to the first '/'
}
Then there's strstr, which searches for an entire substring. I use this constantly for basic protocol parsing. If you're looking for a specific header in an HTTP response, strstr(buffer, "Content-Type:") is the way to go. It returns a pointer to the start of the first occurrence of the needle in the haystack, or NULL if it's not there. Just remember: both of these return pointers into the original string. Don't try to free those pointers; you can only free the pointer returned by malloc.
Managing the Internal State of strtok
Now, back to strtok. You've probably noticed that the first call takes the string, but subsequent calls to get the rest of the tokens use NULL as the first argument. This is where it gets weird.
strtok maintains a static internal pointer to remember where it left off. This makes it "stateful." While it feels convenient, it means strtok is not thread-safe. If two different threads try to tokenize two different strings at the same time using strtok, they'll overwrite each other's internal state and your program will crash or produce garbage. (In a real-world production environment, I'd tell you to use strtok_r—the reentrant version—but let's master the basics first).
Here is the correct pattern for using it: you prime the pump with the string, then loop until you hit NULL.
char data[] = "Apple,Orange,Banana,Grape";
char *token = strtok(data, ",");
while (token != NULL) {
printf("Fruit: %s\n", token);
token = strtok(NULL, ","); // Tell it to keep going from where it stopped
}📋 Practical Task
CSV Log Entry Parser
You are building a tool to analyze server logs. Each log entry is a single string in the format: "TIMESTAMP|LEVEL|MESSAGE" (e.g., "2023-10-11 10:00:01|ERROR|Database connection failed").
Write a program that does the following:
- Defines a character array containing a log entry.
- Uses
strstrto verify that the string contains the word"ERROR". - If it is an error, uses
strtokto split the string by the'|delimiter. - Prints the extracted Timestamp and the Message separately.
Constraint: Ensure you handle the original string carefully, as strtok will modify it. Your output should look like:
Timestamp: 2023-10-11 10:00:01
Message: Database connection failed
There are no comments for now.