4

qsort標準ライブラリを使用して、ワイド文字の配列を並べ替えようとしています。

wchar_t a = L'a';
wchar_t a1 = L'ä';
wchar_t b = L'z';
wchar_t chararray[] = {b, a, a1};  
length = wcslen(chararray);

qsort(chararray, length, sizeof(wchar_t), wcscoll);

今、私は関連する関数がこれらのプロトタイプを持っていると思います:

int wcscoll(const wchar_t *ws1, const wchar_t *ws2);
void qsort(void *base, size_t num, size_t size, int (*comp_func)(const void *, const void *))

結果は完全に期待どおりですが、なぜコンパイラの警告が表示されるのですpassing argument 4 of ‘qsort’ from incompatible pointer typewcscollそして、プロトタイプに合うようにキャストするにはどうすればよいですか?

別の比較関数を定義して渡すと、警告は消えます。

int widecharcomp(const void *arg1, const void *arg2)
{
    return wcscoll(arg1, arg2);
}

...しかし、これは、引数がタイプでない場合のエラー処理が必要なようwchar_t *です。

4

4 に答える 4

8

あなたはほとんど正しい方法でやった。 のgccドキュメントはstrcollwcscollstrcoll、またはwcscollを使用する正しい方法として、これに似た例を示していますqsort

 /* This is the comparison function used with qsort. */

 int
 compare_elements (char **p1, char **p2)
 {
   return strcoll (*p1, *p2);
 }

 /* This is the entry point---the function to sort
    strings using the locale's collating sequence. */

 void
 sort_strings (char **array, int nstrings)
 {
   /* Sort temp_array by comparing the strings. */
   qsort (array, nstrings,
          sizeof (char *), compare_elements);
 }

この例では、実際には削除したい警告が発生しますが、引数のtoをにchar**変更し、明示的ににキャストすることで回避できます。const void*compare_elementsconst char**

これが型安全ではないことを観察するのは正しいですが、型安全はCの長所の1つではありません。Cにはジェネリックスやテンプレートのようなものがないため、qsortが任意の型で機能する唯一の方法は、比較関数がvoid*sを受け入れることです。予期された型ではない引数が渡される可能性があるコンテキストで比較関数が使用されないようにするのは、プログラマーの責任です。

そうは言っても、コードにエラーがあります。比較関数が受け取るのは、比較される要素ではなく、比較される要素へのポインターです。したがって、要素が文字列の場合、それはポインタからポインタへのポインタを意味します。だからあなたが書くとき

return wcscoll(arg1, arg2);

wscollを期待するwchar_t**ときに実際に通過していwchar_t*ます。警告を抑制しながらこれを行う正しい方法は次のとおりです。

int widecharcomp(const void *arg1, const void *arg2)
{
    return wcscoll(*(const w_char_t**)arg1, *(const w_char_t**)arg2);
}

それと同じくらい醜いです。

編集:

コードのトップビットをもう一度見てみました。ここでのエラーは実際には2つあります。wcscoll文字の並べ替えに使用しようとしています。これは、文字列をソートするための関数です(Cでは、ヌル文字で終了する文字シーケンスへのポインターです)。上記は、文字列を並べ替えようとしていることを前提に書かれています。文字を並べ替える場合wcscollは、使用するのに適切な関数ではありませんが、上記のすべてqsortが適用されます。

于 2010-08-16T17:28:00.087 に答える
4

2つの問題があります:あなたはとを混同しwchar_t、そしてあなたはaをとしてwchar_t*渡そうとしました。wchar_t*void*

qsortまず、の配列を並べ替えるように指示しましたwchar_t。ただし、wcscoll比較はしません。wchar_tタイプがのワイド文字列を比較しますwchar_t*。あなたの比較がうまくいったように見えるという事実は、両方の解釈の下でたまたまうまく機能しているあなたのテストデータによるものです。

文字を並べ替える場合は、適切な関数を呼び出す必要があります(ワイド文字APIは、どれを指定するのに十分かわかりません)。文字列をソートする場合は、(タイプのwchar_t *)文字列の配列を割り当てる必要があります。

さらに、の配列があったとしても、引数としてwchar_t*移植可能に渡すことはできませんでした。問題は、とが同じ表現を持っているという保証はないということです。一部のマシンには、バイトポインタとは異なる表現を持つワードポインタがあります。このようなマシンでは、配列の要素へのバイトポインタをに渡しますが、バイトポインタが必要なため、これは機能しません。解決策は、必要に応じて変換を実行する簡単なラッパー関数を作成することです。簡単なラッパーは、多くの場合、で必要になります。wcscollqsortwchar_t*void*qsortwcscollwcscollqsort

于 2010-08-16T17:36:29.550 に答える
2

ソリューションはすでにコーディングされています(ただし、使用している比較関数の選択と渡されるデータについては、このソリューションの最後にある他の回答と編集を参照してくださいqsort())。

渡した関数ポインターを適切な型にキャストすることでラッパー関数を削除することもできますがqsort()、保守性の観点からはラッパーを使用する方が良い解決策だと思います。ラッパー関数を本当に避けたい場合(おそらく、perfの問題で測定可能な実行に遭遇している場合)、次のようにキャストできます。

qsort(chararray, length, sizeof(wchar_t), (int(*)(const void*,const void*))wcscoll);

または、compare関数型のtypedefを使用して、ほぼ間違いなく読みやすくします。

typedef
int (*comp_func_t)(const void *, const void *);

/* ... */
qsort(chararray, length, sizeof(wchar_t), (comp_func_t) wcscoll);

残念ながら、ストレートCqsort()は型セーフにすることができないため、「引数がwchar_t型でない場合のエラー処理」を行うことはできません。プログラマーであるあなたは、正しいデータ、サイズ、および比較関数をに渡すことを保証する責任がありますqsort()


編集:

比較関数に渡される型に関する他の回答で言及されている問題のいくつかに対処するために、現在のロケールの照合シーケンスを使用してwchar_tをソートするために使用できるルーチンを次に示します。ライブラリにはもっと良いものがあるかもしれませんが、私は現時点ではそれを認識していません:

int wchar_t_coll( const void* p1, const void* p2)
{
    wchar_t s1[2] = {0};
    wchar_t s2[2] = {0};

    s1[0] = * (wchar_t*)p1;
    s2[0] = * (wchar_t*)p2;

    return wcscoll( s1, s2);
}

chararrayまた、渡す先wcslen()が適切に終了していないことにも注意してください。0イニシャライザの最後にが必要です。

wchar_t chararray[] = {b, a, a1, 0};  
于 2010-08-16T17:30:10.080 に答える
0

関数ポインタを別のタイプにキャストすることはできません。現在のソリューションは、それが得られるものと同じくらい優れています。

于 2010-08-16T17:29:30.470 に答える