45241
C言語で，[POSIXに規定されているlfind関数](http://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/lfind.html)を使って，配列内の文字列の検索を試しています。

マッチはできるのですが，肝心のマッチした文字列を参照できなくて困っています。

検証環境は以下のとおりです。

  * Ubuntu 16.04
  * gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609

サンプルコードを以下に示します。配列要素内の，"break"にマッチします。

```

    /// \file find_array.c
    #include <stdio.h>
    #include <string.h>
    #include <search.h>
    
    int main(void) {
        char *tab[] = {"auto", "break"};
        size_t nel = sizeof(tab)/sizeof(tab[0]);
        // char *target = "break";
        char *entry = lfind(&(void *){"break"}, tab, &nel, sizeof(tab[0]), (int (*)(const void *, const void*))strcmp);
    
        if (entry) {
            printf("found: %p:%s\n", (void *)&tab[1], tab[1] );
            printf("found: %p:%s\n", entry, entry);
        } else {
            puts("NOT FOUND");
        }
    
        // 数値の場合
        // int tab[] = {1, 2, 3};
        // size_t nel = sizeof(tab)/sizeof(tab[0]);
        // void *entry = lfind(&(int){2}, tab, &nel, sizeof(tab[0]), (int (*)(const void *, const void*))strcmp);
        // アクセスは間接参照を使う
        // printf("found: %d\n", *(int *)entry);
    
        return 0;
    }
    
```

このfind_array.cをコンパイルして実行すると，以下のような出力が得られます。

```

    found: 0x7ffddffe2748:break
    found: 0x7ffddffe2748:�@
    
```

lfindはマッチした場合に，マッチした要素のアドレスを返却しており，実際，上記の結果の通り元の配列の該当要素と同じアドレスでした。

そのまま文字列も参照できるかと思ったのですが，ダメでした
(*&やキャストなどでいろいろ`entry`変数へのアクセスのしかたを試しても結局ダメ)。何か，根本的なところの理解が足りていないような気がしています。

幸い，非NULLのポインターを取得できておりマッチしているかどうかはわかるので，検索に使ったキーを使えば，間接的にマッチした文字列は参照できます。

しかし，できることならば，マッチした結果 (entry変数)
からマッチした値を参照したいのです。何かアクセスのしかたに工夫が必要なのでしょうか？どうかご教授お願いします。

なお，サンプルコードの下の方に掲載している通り，配列が数値の場合は間接参照演算子*で参照することで，マッチした値にアクセスできました。
CC BY-SA 4.0
2018-07-02T13:04:11.540
45239
2018-07-03T12:40:44.453
2018-07-03T12:40:44.453
29148
29148
question
c posix
C言語でPOSIX規定関数のlfind関数で配列要素にマッチした文字列の参照方法
成功している`int`型の配列を検索している例をよく考えてみてください。`lfind`の結果を`int
*`型、つまり`int`のポインタにキャストしてから、その参照先をアクセスしていますよね?

文字列を扱っているあなたのコードでは`char *`型の配列を検索しています。でしたら、`lfind`の結果は「`char
*`のポインタ」、別の言い方をすると`char **`にキャストしなければいけません。

```

    int main(void) {
        char *tab[] = {"auto", "break"};
        size_t nel = sizeof(tab)/sizeof(tab[0]);
        char **entry = lfind(&(void *){"break"}, tab, &nel, sizeof(tab[0]), (int (*)(const void *, const void*))strcmp);
    
        if (entry) {
            printf("found: %p:%s\n", (void *)&tab[1], tab[1] );
            printf("found: %p:%s\n", entry, *entry);
        } else {
            puts("NOT FOUND");
        }
    
        return 0;
    }
    
```

これで1件落着しそうに思うのですが、まだ少し足りません。`lfind`の第5引数である比較関数には「要素型へのポインタ」が渡されます。つまり要素型が`int`なら`int
*`、要素型が`char *`なら`char **`を引数にとるような関数を渡さないとなりません。

したがって正しくはこんな感じになります。

```

    int cmpare_cstr(char **str1, char **str2) {
        printf("comparing: %s - %s\n", *str1, *str2);
        return strcmp(*str1, *str2);
    }
    
    int main(void) {
        char *tab[] = {"auto", "break"};
        size_t nel = sizeof(tab)/sizeof(tab[0]);
        char *target = "break";
        char **entry = lfind(&target, tab, &nel, sizeof(tab[0]), (int (*)(const void *, const void*))cmpare_cstr);
    
        if (entry) {
            printf("found: %p:%s\n", (void *)&tab[1], tab[1] );
            printf("found: %p:%s\n", entry, *entry);
        } else {
            puts("NOT FOUND");
        }
    
        return 0;
    }
    
```

あなたのコードではポインタの内容の4または8バイトの内容を`strcmp`で比較してしまっています。ポインタそのものを表すバイトの途中に0x00が現れれば、誤った結果を出すでしょうし、逆に0x00がどこにもない領域に配列が置かれていたら、メモリ未割り当ての領域アクセスで異常終了するかもしれません。

  * 要素は何型なのか
  * 結果は要素型そのものなのか、要素へのポインタなのか
  * 比較関数に渡されるのは要素型そのものなのか、要素へのポインタなのか

この辺をきちんと理解して置かないと`lfind`は使えません。特にポインタへのポインタ(C言語の学習でも、中級まで頑張れた人がつまづくポイントですね)が絡んでくると難しく見えますが、基本は「要素型は何型なのか」です。そこら辺に気を使いながらいろいろ試して見られると良いでしょう。

* * *

もう一度まとめておくと、

検索対象配列の要素型を`T`とした時、

  * `lfind`の戻り値型は`T*`
  * `lfind`の第一引数の型は`T*`
  * `lfind`の第5引数である比較関数の引数型は両方とも`T*`

そして、あなたの文字列検索の場合`T`は(「`T*`は」ではないですよ)、`char *`だと言うことになります。
CC BY-SA 4.0
2018-07-02T13:50:01.003
45241
2018-07-02T14:31:30.633
2018-07-02T14:31:30.633
13972
13972
45239
answer