4

比較関数は次のとおりです。

int compare(const void *a, const void *b) {
    char* *s = (char* *) a;
    char* *t = (char* *) b;
    return sort_order * strcmp(*s, *t); // sort_order is -1 or 1
}

今私の質問は、特定のタイプのへのキャストの背後にある理由は何ですか? double pointerというか、ダブル ポインター キャストが必要な理由と内部での使用方法を教えてください。

使用されるその他の変数: char **wordlist; int nbr_words;(配列要素は)char *word;

例 qsort コール:qsort(wordlist, nbr_words, sizeof(char *), compare);

4

1 に答える 1

6

の定義を示していただけると助かりますが、wordlistおそらく として定義されていchar **ます。このcompare()関数は、リストの各要素へのポインターを受け取ります。リストの各要素のタイプが のchar *場合、compare()は への 2 つのポインタchar *、つまり 2 つを受け取りchar **ます。

への変換char **(実際のキャストは不要になることに注意してください。この特定のケースでは、constvoid ポインターから非へのポインターでない場合const char **) 自体はqsort()、あらゆる種類の型で機能する必要があるため、引数void *渡される前に変換されます。a を参照することはできないvoid *ため、何かを行う前に元の型に変換する必要があります。

例えば:

#include <stdio.h>

int compare_int(void * a, void * b) {
    int * pa = a;
    int * pb = b;
    if ( *pa < *pb ) {
        return -1;
    } else if ( *pa > *pb ) {
        return 1;
    } else {
        return 0;
    }
}

int compare_double(void * a, void * b) {
    double * pa = a;
    double * pb = b;
    if ( *pa < *pb ) {
        return -1;
    } else if ( *pa > *pb ) {
        return 1;
    } else {
        return 0;
    }
}

int compare_any(void * a, void * b, int (*cfunc)(void *, void *)) {
    return cfunc(a, b);
}

int main(void) {
    int a = 1, b = 2;
    if ( compare_any(&a, &b, compare_int) ) {
        puts("a and b are not equal");
    } else {
        puts("a and b are equal");
    }

    double c = 3.0, d = 3.0;
    if ( compare_any(&c, &d, compare_double) ) {
        puts("c and d are not equal");
    } else {
        puts("c and d are equal");
    }

    return 0;
}

出力:

paul@local:~/src/c/scratch$ ./comp
a and b are not equal
c and d are equal
paul@local:~/src/c/scratch$

関数は、サポートされている任意の型 (この場合はとcompare_any()) を比較します。これは、関数ポインターを渡すことができるためです。ただし、渡される関数のシグネチャは同じでなければならないため、引数を 2 つ取り、を 2 つ取ると宣言することはできません。両方とも 2 つの引数を取るものとして宣言する必要があります。これを行う場合、それらの引数を操作する前に、それらの引数をそれらの関数内で有用なものに変換する必要があります。intdoublecompare_int()int *compare_double()double *void *void *

あなたのケースで起こっていることはまったく同じですが、データ自体はポインターであるため、ポインターをポインターに渡しているためvoid *、あなたの場合は に変換する必要がありますchar **

編集:どのように機能するかについての元の質問へのコメントの混乱を説明するためにqsort()、ここにqsort()署名があります:

void qsort(void *base, size_t nmemb, size_t size,
           int(*compar)(const void*, const void*))

baseは配列の最初の要素へのポインター、nmembはその配列のメンバーの数、sizeは各要素のサイズです。

たとえば、配列の最初と 2 番目の要素を呼び出すと、最初の要素のアドレス (つまりそれ自体) と要素のアドレス (つまり ) が送信さqsort()れます。comparbasebase + size

baseがもともと の配列として宣言されていた場合int、比較関数は、受け取ったポインタを へintのポインタとして解釈する必要がありint *ます。baseがもともと文字列の配列として宣言されていた場合char **、比較関数はそれらのポインタを へのポインタchar *、つまり として解釈する必要がありchar **ます。

いずれの場合も、compare 関数は要素へのポインターを取得します。配列がある場合は、それらのポインターを比較関数のようにint解釈する必要があります。int *配列がある場合は、char *それらを などと解釈する必要がありますchar **

この場合、単純な引数を比較関数に渡すだけで、明らかに呼び出すことができます。ただし、ジェネリックであるため、比較関数にポインターのみを渡すことができ、実際に要素の値を渡すことはできません。これを使用すると、オブジェクトポインターの任意のタイプを に変換できるため、ジェネリックにすることができますが、非ポインター値を変換できる同等のデータ型はありません。そのため、やのような通常の型と同じように動作する必要があります。strcmp()char *qsort()void *void *intdouble、ポインター、および構造体を使用しており、可能なすべての型で正しく機能させる唯一の方法は、要素自体がポインターでもある場合でも、要素自体ではなく、要素へのポインターを処理することです。このため、ここでは不必要なレベルの間接化が行われているように見えるかもしれません、実際には がqsort()一般的な方法で機能できるようにするために必要です。

compare_any()上記のコードを に似るように変更すると、これがより明確にわかりますqsort()。2 つのポインターではなく、さまざまな型の 2 要素配列への 1 つのポインターを使用します (少し不自然な例ですが、単純にしています)。 ):

#include <stdio.h>
#include <string.h>

int compare_int(void * a, void * b) {
    int * pa = a;
    int * pb = b;
    if ( *pa < *pb ) {
        return -1;
    } else if ( *pa > *pb ) {
        return 1;
    } else {
        return 0;
    }
}

int compare_double(void * a, void * b) {
    double * pa = a;
    double * pb = b;
    if ( *pa < *pb ) {
        return -1;
    } else if ( *pa > *pb ) {
        return 1;
    } else {
        return 0;
    }
}

int compare_string(void * a, void * b) {
    char ** pa = a;
    char ** pb = b;
    return strcmp(*pa, *pb);
}

int compare_any(void * arr, size_t size, int (*cfunc)(void *, void *)) {
    char * first = arr;
    char * second = first + size;
    return cfunc(first, second);
}

int main(void) {
    int n[2] = {1, 2};
    if ( compare_any(n, sizeof(*n), compare_int) ) {
        puts("a and b are not equal");
    } else {
        puts("a and b are equal");
    }

    double d[2] = {3.0, 3.0};
    if ( compare_any(d, sizeof(*d), compare_double) ) {
        puts("c and d are not equal");
    } else {
        puts("c and d are equal");
    }

    char * s[] = {"abcd", "bcde"};
    if ( compare_any(s, sizeof(*s), compare_string) ) {
        puts("'abcd' and 'bcde' are not equal");
    } else {
        puts("'abcd' and 'bcde' are equal");
    }

    return 0;
}

出力:

paul@local:~/src/c/scratch$ ./comp
a and b are not equal
c and d are equal
'abcd' and 'bcde' are not equal
paul@local:~/src/c/scratch$

ご覧のとおり、 の配列と のcompare_any()配列の両方を受け入れる方法はありません。関数が として処理する必要があるポインターを取得しないと、配列要素に対して実行されるポインター演算が原因です。その追加レベルの間接性がなければ、どちらも機能しません。intchar *compare_string()char **compare_int()compare_double()

于 2013-10-22T20:26:19.550 に答える