C言語のPOSIX定義関数のlfindで配列要素の検索がうまくできているか自信がない
こちらの質問 ([c - C言語でPOSIX規定関数のlfind関数で配列要素にマッチした文字列の参照方法 - スタック・オーバーフロー](C言語でPOSIX規定関数のlfind関数で配列要素にマッチした文字列の参照方法"c - C言語でPOSIX規定関数のlfind関数で配列要素にマッチした文字列の参照方法 - スタック・オーバーフロー")) の関連質問です。
Ubuntu 16.04+gcc 5.4.0で,以下のサンプルコードでC言語のlfind関数により,文字列配列とint型の配列から指定した要素 ("break"と2)を検索しています。
/// \file example_lfind.c
#include <stdio.h>
#include <string.h>
#include <search.h>
int main(void) {
// lfind for string array.
char *tab1[] = {"auto", "break", "continue"};
size_t size1 = sizeof(tab1)/sizeof(tab1[0]);
char *key1 = "break";
char *entry1 = lfind(&key1, tab1, &size1, sizeof(tab1[0]), (int (*)(const void *, const void*))strcmp);
if (entry1) {
printf("array: %p:%s\n", (void *)&tab1[1], tab1[1] );
printf("found: %p:%s\n", entry1, entry1);
} else {
puts("STR NOT FOUND");
}
// lfind for int array.
int tab2[] = {1, 2, 3};
size_t size2 = sizeof(tab2)/sizeof(tab2[0]);
int key2 = 2;
int *entry2 = lfind(&key2, tab2, &size2, sizeof(tab2[0]), (int (*)(const void *, const void*))strcmp);
if (entry2) {
printf("array: %p:%d\n", (void *)&tab2[1], tab2[1] );
printf("found: %p:%d\n", (void *)entry2, *entry2);
} else {
puts("INT NOT FOUND");
}
return 0;
}
実行結果例は以下となります。
array: 0x7ffe40601408:break
found: 0x7ffe40601408:@
array: 0x7ffe406013f4:2
found: 0x7ffe406013f4:2
冒頭にあげた質問で,文字列配列 (tab1) でマッチした entry1 はlfindの第5引数に指定する検索に使用する比較関数に渡される引数と,結果を受け取るポインター(entry1)のデータ型が合っていないので,正しくマッチ後の値を参照できずに,"break"ではなく"@" (NULL?)が表示されていることがわかりました。
ただ,マッチ自体はうまくできているようにみえます。int型の配列の検索結果entry2に至っては,検索した値 (2) も検索結果のentry2からきちんと参照できています。
しかし,先の質問で以下の通り,たまたまうまくいっているだけとの指摘をいただきました。
あなたのコードではポインタの内容の4または8バイトの内容をstrcmpで比較してしまっています。ポインタそのものを表すバイトの途中に0x00が現れれば、誤った結果を出すでしょうし、逆に0x00がどこにもない領域に配列が置かれていたら、メモリ未割り当ての領域アクセスで異常終了するかもしれません。
そこで,2点の質問です。
- 今回の比較関数にstrcmpを使ったchar* 配列のtab1とint配列のtab2の検索でうまくいかない具体的なパターンを教えてほしい。
- 配列の検索は頻出事項であり,可能であれば準標準関数であるlfindとlfindの第5引数の比較関数に採用可能な標準関数(strcmpなど)だけで(できれば自前での比較用関数の実装は避けたい),手短に文字列配列,数値配列 (int, float/double)の検索を実現したい。なにかいい方法があれば教えてほしい (文字配列はstrchr単独で検索可能なため除外)。
片方だけの回答でも歓迎です。どうぞよろしくお願いします。