以前、以下で投稿した内容の関連質問となります。
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単独で検索可能なため除外)。
片方だけの回答でも歓迎です。どうぞよろしくお願いします。
cmpare_int
を、たとえキャストしてもlfind
に渡せる保証は無いので(まあ99.9%以上の処理系で問題ないが)オイラなら別の書き方をするでしょう。 – 774RR Jul 03 '18 at 23:20元々,POSIXの例で文字配列の配列 (tab1と異なり,文字ポインターの配列ではない)
char tab[TABSIZE][ELSIZE]
を検索対象に,strcmpを使った例がありました。 同じようにして他の型でもいけるのではないかと思ったのが間違いでした…キャストやポインターが絡む場合,型に注意するようにします。
– senooken Jul 04 '18 at 11:50int compare(int *x, int *y){ return (*x - *y); }
ですね。lfindの使い方の勉強中に,こちらで見つけました。lfind - Search.h - C - C++ Computing Reference with Worked Examples
ただ,bsearchの比較関数の要件も,一致したとき0,他は正か負なので,このワンライナーでも問題なさそうです。
OOPerさんが意図した簡略関数と,こちらの予想が違うのかもしれません。
– senooken Jul 04 '18 at 11:51int
が32bitのシステムを仮定して)2000000000 - (-1000000000)
を計算してみてください。(0
はどちらもちょうど9桁。)前が正、後ろが負ですから比較関数の結果として使うには、「前が大きい」を表す正の値が返らないといけないのですが、値は-1294967296
なんて負の数になってしまいます。32bitの整数減算がオーバーフローしてしまっているのが原因なので、「絶対に(int
のサイズで)オーバーフローするような値はでてこない」と言う場合にしかbsearch
用には使えないと言うことになります。 – OOPer Jul 04 '18 at 12:21printf("%d\n", (2000000000 -(-1000000000)) > 0 );
の結果 (正-(負)なので正のはず)が,1 (true) ではなく,0 (false)になりました。今まで,そこまで精度や型など細かく気にしていなかったので,たいへん参考になりました。お付き合いいただきありがとうございました! – senooken Jul 05 '18 at 13:06