3

Cの関数ポインタの*の有無に違いはありますか?

このような私の関数ポインタ宣言

typedef void (*DListVisitNode) (Node*, void*);
void DListTraverse( NodeList* , DListVisitNode , void*);

私はこれらのようなコードを持っています

void print_index( Node* node, void* ctx)
{
    printf("index:%d\n", node->index);
}

void* print_content( Node* node, void* ctx)
{
    printf("content:%s\n", node->content);
}
void DListTraverse(NodeList* nodelist, DListVisitNode visit_func, void* ctx)
{
    Node* cur_node = nodelist->headnode;
    while( cur_node != NULL)
    {
        visit_func( cur_node, ctx );
        cur_node = cur_node->nextnode;
    }
}

DListTraverse( nodelist, print_content, NULL );
DListTraverse( nodelist, print_index, NULL );

両方のDListTraverseは機能しますが、*が付いているものはこのような警告をスローします

warning: passing argument 2 of ‘DListTraverse’ from incompatible pointer type

後で*を削除するだけですが、違いは何ですか?

4

4 に答える 4

3

print_contentvoid*つまり、一般的なrawポインタを返すこととして定義されます。

print_indexvoidつまり、結果なしで戻ることとして定義されます。

これらは異なる署名です。のみprint_index一致しDListVisitNodeます。

typedef私のコーディングスタイルは、次のように署名を定義することです

  typedef void signature_t (int);

上記にはポインタが含まれていないことに注意してください。intこれは、1つの引数を持ち、結果がない関数のシグネチャに名前を付けます。

次に、上記の署名のそのような関数へのポインタが必要な場合は、signature_t*

本当のことは、関数の名前は配列の名前に似ているということです。言語はこれらを暗黙的にポインタに変換します。だからDListTraverse(nodelist, print_content, NULL)、のように理解されていますDListTraverse(nodelist, &print_content, NULL)

コンパイラですべての警告を有効にする必要があります。これは、プログラムとしてコンパイラに引数をgcc与えることを意味します。-Wall -Wextra

于 2012-09-07T06:17:08.280 に答える
2

print_content(ポインタ)を返すことを宣言しました。void *これは、と一致しないことを意味しDListVisitNodeます。ただし、関数は実際には何も返さない(returnステートメントがない)ため、それについて別の警告を受け取る必要があります。

于 2012-09-07T06:17:42.873 に答える
2

次のtypedefとの違いについて混乱するかもしれません。

typedef void (*DListVisitNode) (Node*, void*);
typedef void * (*DListVisitNode) (Node*, void*);

または同等に、次の2つのタイプの間:

  • void (*) (Node *, void *)

  • void * (*) (Node *, void *)

前者はを返す関数へのvoidポインタであり、後者はを返す関数へのポインタvoid *です。各印刷関数は、そのような関数の1つの例です。

当然、異なるタイプの関数ポインターは互換性がなく、暗黙的に変換可能ではありません。これは確かに意味がありません。関数が実際に完全に異なるシグネチャを持っていると偽って、意味のある方法で呼び出すことができると期待することはできません。自転車が車のふりをして、ガソリンスタンドで給油しようとしているようなものです。

于 2012-09-07T06:24:35.883 に答える
1
typedef void (*DListVisitNode) (Node*, void*);     

関数へのポインターを、 2つのパラメーターを受け取り、を返すとして定義します。 上記のステートメントを使用すると、タイプとして使用できます。正確なタイプは上記のとおりです。 Node *void *void
DListVisitNode

void* print_content( Node* node, void* ctx) 

を返しますが、void *を返しませんvoid
Cは強く型付けされた言語であり、c標準では、型違反はコンパイラーによって報告される必要があるため、型の不一致があり、コンパイラーはそれを報告します。基本的に、関数が何も返さない場合は、return型を使用しvoidます。または、特定の型を返す場合は、その特定の型を戻り型として使用します。

于 2012-09-07T06:17:54.913 に答える