

# 背景と質問

以下のようなプログラムを見掛けました。

[code]

    char *token1 = strtok(str, " ");
    char *token2 = strtok(NULL, " ");
    char *token3 = strtok(NULL, " ");
    
    if(token1 == NULL) {
        // トークンがない場合の処理
    }
    else if(token2 == NULL) {
        // トークンが1個の場合の処理
        ...
    }
    else if(token3 == NULL) {
        // トークンが2個の場合の処理
        ...
    }
    else {
        // トークンが3個以上の場合の処理
        ...
    }
    
[/code]

このプログラムでは、`strtok()` が一度 `NULL` を返した後、続けて `strtok(NULL, ...)`
を呼ぶことができる、ということが前提になっています。  
こういった使い方は今まで思い付かず、考えたことがない、というより無意識のうちに未定義と思い込んでいたのですが、このような使い方は規格上保証されているものなのでしょうか。

# 規格調査

検証のために、規格を調べてみました。  
ISO/IEC 9899:2011 の 7.24.5.8 The `strtok` function から引用します (強調は引用者による)。  
この引用中の `s1`、`s2` は、それぞれ `strtok` の第1引数、第2引数を表します。

> 3  
>  The first call in the sequence searches the string pointed to by `s1` for
> the first character  
>  that is not contained in the current separator string pointed to by `s2`.
> If no such character  
>  is found, then there are no tokens in the string pointed to by `s1` and the
> **`strtok` function  
>  returns a null pointer**. If such a character is found, it is the start of
> the first token.
>
> 4  
>  The `strtok` function then searches from there for a character that is
> contained in the  
>  current separator string. If no such character is found, the current token
> extends to the  
>  end of the string pointed to by `s1`, and **subsequent searches for a token
> will return a null  
>  pointer**. If such a character is found, it is overwritten by a null
> character, which  
>  terminates the current token. **The`strtok` function saves a pointer to the
> following  
>  character**, from which the next search for a token will start.
>
> 5  
>  Each subsequent call, with a null pointer as the value of the first
> argument, starts  
>  searching from the saved pointer and behaves as described above.

この記述について、以下のように場合分けして考えてみました。

ここで、パラグラフ5の書き方は曖昧に感じましたが、`strtok(NULL, ...)` 呼び出しについては、パラグラフ3と4の `s1` を
パラグラフ4で保存したポインタに置き換えて実行する、という意味であると解釈しました。

## ケース1: `s1` = `"abc def"` のように、トークン文字で終わる場合

`"def"` が返った後の呼び出しで、パラグラフ 4 の「subsequent **searches** for a token will return
a null pointer」のパターンに合致し、`NULL` が返ります。「search」が複数形となっているため、何度 `strtok(NULL,
...)` を呼んでも `NULL` が返ります。

## ケース2: `s1` = `"abc def "` のように、トークン列の末尾に区切り文字がある場合

`"def"` が返った後の呼び出しで、パラグラフ 3 の「`strtok` function returns a null
pointer」のパターンに合致し、`NULL` が返ります。何かしら保存したポインタ (このケースでは文字列末尾)
があり、そのポインタのアップデートがないため、何度呼んでもパラグラフ3のパターンに合致して `NULL` が返ります。

## ケース3: `s1` = `" "` のように、トークン列がない場合

前項と同様、パラグラフ3のパターンに合致して `NULL` が返ります。この場合、保存したポインタというものがないため、
**後続の`strtok(NULL, ...)` は未定義になりそう**です。

以上、場合によっては `NULL` が返った後の `strtok(NULL, ...)` は `NULL`
が返り、別の場合では未定義になりそうに思えました。  
しかし、それでは何とも中途半端で気持ち悪く感じます。  
これをどう考えるか、ということで、以下の2通りの可能性を考えました。

  * 上記の解釈に間違いがあり、`NULL` が返るか未定義かはどちらか一方に定まる。
  * 基本的に未定義と考えるべき。パラグラフ4で 「search」が複数形になっていることに特に意味はない。

しかし、何が正しいのかという確証は取れませんでした。

# 実験

参考までに、手持ちの環境ではどうなのか実験してみました。

## 環境:

  * OS: Gentoo Linux
  * コンパイラ: gcc 4.8.4, clang 3.5.0
  * ライブラリ: glibc 2.20

## プログラム:

[code]

    #include <stdio.h>
    #include <string.h>
    
    // トークンの内容を出力。ただし NULL の場合は "(null)" を出力。
    #define PR_TOKEN(token)  do { printf(#token " = %s\n", (token)? token: "(null)"); } while(0)
    
    int main(void)
    {
        // ケース1: 文字列がトークン文字で終わる
        char str1[] = "abc def";
        char *token1_1 = strtok(str1, " ");
        char *token1_2 = strtok(NULL, " ");
        char *token1_3 = strtok(NULL, " ");
        char *token1_4 = strtok(NULL, " ");
        PR_TOKEN(token1_1);
        PR_TOKEN(token1_2);
        PR_TOKEN(token1_3);
        PR_TOKEN(token1_4);
    
        // ケース2: 文字列が区切り文字で終わる
        char str2[] = "abc def ";
        char *token2_1 = strtok(str2, " ");
        char *token2_2 = strtok(NULL, " ");
        char *token2_3 = strtok(NULL, " ");
        char *token2_4 = strtok(NULL, " ");
        PR_TOKEN(token2_1);
        PR_TOKEN(token2_2);
        PR_TOKEN(token2_3);
        PR_TOKEN(token2_4);
    
        // ケース3: トークン列がない
        char str3[] = " ";
        char *token3_1 = strtok(str3, " ");
        char *token3_2 = strtok(NULL, " ");
        char *token3_3 = strtok(NULL, " ");
        char *token3_4 = strtok(NULL, " ");
        PR_TOKEN(token3_1);
        PR_TOKEN(token3_2);
        PR_TOKEN(token3_3);
        PR_TOKEN(token3_4);
    
        return 0;
    }
    
[/code]

## 結果:

[code]

    token1_1 = abc
    token1_2 = def
    token1_3 = (null)
    token1_4 = (null)
    token2_1 = abc
    token2_2 = def
    token2_3 = (null)
    token2_4 = (null)
    token3_1 = (null)
    token3_2 = (null)
    token3_3 = (null)
    token3_4 = (null)
    
[/code]

すべてのケースについて、一度 `NULL` が返った後の `strtok()` 呼び出しで `NULL` が返っています。  
今回、正当性について疑問が残るケース3について、ケース1
、ケース2で保存していた情報が残っているため、たまたまうまく動いている、ということを疑い、ケース3のみを実行したり、ケース2の最初の `strtok()`
呼び出しの直後にケース3に入ってみても同様でした。


CC BY-SA 3.0
2015-07-03T17:39:14.040
12001
2015-07-03T21:26:12.550
7831
question
c
strtok() が NULL を返した後、さらに strtok(NULL, ...) を呼んでもよいのか。


> 7  
>  The strtok function returns a pointer to the first character of a token, or
> a null pointer if there is no token.

とありますので、該当するトークンがない場合にはNULLを返すと考えて良いと思います。

参考までに、  
Ｃリファレンスマニュアル第５版の解説によると、

> すべての文字がsetに属していればstrtokはナルポインタを返し内部状態ポインタをナルポインタに設定する．

とありますので、ケース３の場合も内部ポインタがナルに設定される。のが質問者の想定と違います。  
また、

>
> strと内部状態ポインタが共にナルポインタであれば，strtokはナルポインタを返し，内部状態ポインタはそのままにする．（すべての字句を返してもなおstrtokが呼ばれた時の備えである）

（※strは第一引数setは第２引数のこと）  
とあるので、  
「strtok() が一度 NULL を返した後、続けて strtok(NULL, ...) を呼ぶことができる」  
は、前提としてよく、NULLを返すということになります。


CC BY-SA 3.0
2015-07-03T21:26:12.550
12004
2015-07-03T21:26:12.550
5044
12001
answer