How strtok_r Maintains State in C
The strtok_r function is the reentrant and thread-safe
equivalent of the standard C library function strtok. While
standard strtok tracks its position in a string using a
hidden, shared static buffer—causing race conditions in multithreaded
programs—strtok_r avoids this by offloading the
state-tracking responsibility to the caller. This article explains how
strtok_r leverages an external pointer parameter to
maintain parsing context reliably across multiple invocations without
global state.
The Mechanism of
saveptr
The core difference between strtok and
strtok_r lies in the function signatures. The prototype for
strtok_r is:
char *strtok_r(char *str, const char *delim, char **saveptr);The third argument, saveptr, is a pointer to a
char * variable. This user-provided variable acts as the
storage container for the function's internal state. Because the caller
allocates this variable—typically as a local variable on the thread's
execution stack—each thread or parsing task maintains its own isolated
state context.
Step-by-Step Execution Lifecycle
The parsing process with strtok_r follows a specific
protocol across consecutive calls:
Initial Call: On the first invocation, the caller supplies the address of the target string in
strand the address of the tracking pointer insaveptr(e.g.,&saveptr). The function scansstrto locate the first token, skips any leading delimiters defined indelim, and determines the end of the token. It replaces the first delimiter character following the token with a null terminator (\0) and updates the memory location pointed to bysaveptrto store the address of the next character in the buffer. The function then returns a pointer to the start of the found token.Subsequent Calls: For subsequent tokens from the same string, the caller passes
NULLas thestrargument while continuing to pass the samesaveptraddress. Detecting thatstrisNULL,strtok_rreads the address stored inside*saveptras its starting position rather than starting over. It repeats the process of scanning, null-terminating, advancing*saveptr, and returning the next token.Termination: Once the end of the string is reached and no more tokens remain,
strtok_rupdates*saveptrto point to the end of the buffer (or sets it toNULL) and returnsNULL. This signals to the calling loop that tokenization is complete.
Code Example
Below is a standard pattern demonstrating how the caller manages the parsing state:
#include <stdio.h>
#include <string.h>
int main(void) {
char input[] = "apple,banana,cherry";
const char delim[] = ",";
char *saveptr;
// First call: pass the string to tokenize
char *token = strtok_r(input, delim, &saveptr);
// Subsequent calls: pass NULL to continue using saveptr
while (token != NULL) {
printf("Token: %s\n", token);
token = strtok_r(NULL, delim, &saveptr);
}
return 0;
}Why This Design Ensures Thread Safety
Because standard strtok uses an internal
static char * variable, concurrent calls from different
threads overwrite each other's progress through their respective
strings. By requiring the user to supply saveptr,
strtok_r removes all global and static state from the
function itself. Multiple threads can safely execute
strtok_r simultaneously, provided each thread supplies its
own distinct char * tracking variable.