strtok_r(3)

πŸ’– strtok_r(3) β€” string tokenize reentrant πŸ’–

okay bestie, buckle up, we're talking about strtok_r from the Library Functions Manual ✨ it's the reentrant, no-drama, thread-safe glow-up of strtok, and honestly? she ate. πŸŽ€

It lives in the Standard C library, which is libc, which is -lc. iykyk πŸ¦‹


✨ SYNOPSIS ✨

#include <string.h>

char *strtok_r(char *_Nullable restrict str, const char *restrict delim,
               char **restrict saveptr);

🧚 Feature Test Macro Requirements for glibc 🧚

(wanna know what feature test macros even are? go read feature_test_macros(7), bestie πŸ’œ)

  • strtok_r():
    • _POSIX_C_SOURCE
    • || /* glibc <= 2.19: */ _BSD_SOURCE || _SVID_SOURCE

so like, yeah, if you're on old glibc you need one of those older macros too. vintage behavior, kinda iconic honestly 🌸


🌈 DESCRIPTION 🌈

So strtok_r() is literally just a reentrant version of strtok(3). that's the whole vibe. reentrant means she doesn't keep her state in some hidden global, she keeps it in a place you hand her, so multiple people can use her at once without chaos. main character energy but polite about it πŸ’…

The saveptr argument is a pointer to a char * variable. strtok_r() uses it internally to keep track of context between calls that are parsing the same string. think of it as her little diary πŸŽ€

On the first call to strtok_r(), str should point to the string you wanna parse, and the value of *saveptr is ignored β€” but fr fr, see VERSIONS below, there's a lil caveat there πŸ‘€

On subsequent calls, str should be NULL, and saveptr (plus the buffer it points to) should be unchanged since the previous call. don't touch the diary. the diary stays where it is. 😀

And here's the slay part: different strings can be parsed concurrently using sequences of calls to strtok_r() that specify different saveptr arguments. nested loops? multiple parses at once? she said "bet." πŸ”₯


πŸ’« RETURN VALUE πŸ’«

strtok_r() returns a pointer to the next token, or NULL if there are no more tokens. that's it, that's the whole return value. no hidden extra errno nonsense, just token or nothing. periodt. πŸ’…


πŸ‘‘ ATTRIBUTES πŸ‘‘

for an explanation of the terms used in this here section, go check attributes(7) β€” she explains it all, bestie 🫢

  • Interface: strtok_r()
  • Attribute: Thread safety
  • Value: MT-Safe

yup. MT-Safe. she's safe to use across threads and that is the personality trait, no cap ⭐


πŸ¦‹ VERSIONS πŸ¦‹

okay so on some implementations, *saveptr is required to be NULL on the first call to strtok_r() that's being used to parse str. this is why I told you to peep down here. if you're writing portable code, just set it to NULL on the first call and you'll never get caught out πŸ™ˆ


🌸 STANDARDS 🌸

POSIX.1-2008.


✨ HISTORY ✨

POSIX.1-2001.

she's been around. a legend. πŸ’œ


πŸ“ EXAMPLES πŸ“

the program down below uses nested loops that employ strtok_r() to break a string into a two-level hierarchy of tokens. so like tokens made of subtokens. very inception, very cute 🌈

  • the first command-line argument specifies the string to be parsed
  • the second argument specifies the delimiter byte(s) used to separate that string into "major" tokens
  • the third argument specifies the delimiter byte(s) used to separate the "major" tokens into subtokens

an example of the output produced by this program is the following:

$ ./a.out 'a/bbb///cc;xxx:yyy:' ':;' '/'
1: a/bbb///cc
        --> a
        --> bbb
        --> cc
2: xxx
        --> xxx
3: yyy
        --> yyy

look at her go 😭 so clean. the major delimiters split on :; and then each piece gets split again on /. triple slashes? handled. vibes? immaculate ✨

πŸŽ€ Program source πŸŽ€

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(int argc, char *argv[])
{
    char *str1, *str2, *token, *subtoken;
    char *saveptr1, *saveptr2;
    int j;

    if (argc != 4) {
        fprintf(stderr, "Usage: %s string delim subdelim\n",
                argv[0]);
        exit(EXIT_FAILURE);
    }

    for (j = 1, str1 = argv[1]; ; j++, str1 = NULL) {
        token = strtok_r(str1, argv[2], &saveptr1);
        if (token == NULL)
            break;
        printf("%d: %s\n", j, token);

        for (str2 = token; ; str2 = NULL) {
            subtoken = strtok_r(str2, argv[3], &saveptr2);
            if (subtoken == NULL)
                break;
            printf("\t --> %s\n", subtoken);
        }
    }

    exit(EXIT_SUCCESS);
}

see how the outer loop passes argv[1] on the first pass and then NULL after? and the inner loop does the same thing with token and NULL? that's the entire trick, bestie. two separate saveptrs, two separate diaries, zero interference. she's literally built for this πŸ’…


πŸ’€ SEE ALSO πŸ’€


lowkey reminder: the original strtok(3) is not thread safe, which is the whole reason strtok_r exists. so if you're in a multithreaded situation, use the _r one. no cap. πŸ™ˆ

that's the whole page, girlies β€” this was strtok_r(3) from Linux man-pages 6.20, dated 2026-02-25, and yes the title is still strtok_r(3) in case you got lost in my paragraphs πŸ₯° mwah, go tokenize something responsibly πŸ’–βœ¨πŸŽ€πŸ¦‹πŸŒΈπŸ’…πŸ«Ά

πŸ“¦ where this page comes from
  • arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • πŸ“œ the page's license: Linux-man-pages-copyleft
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms