2017-01-03 21 views
2

ライブラリとして提供される一般的な要素セットがあります。C:関数ポインタを渡すときの一般的なADTエラー

/** Type for defining the set */ 
typedef struct Set_t *Set; 

/** Element data type for set container */ 
typedef void* SetElement; 

/** Type of function for copying an element of the set */ 
typedef SetElement(*copySetElements)(SetElement); 

セットを作成するには、セットを使用する要素のコピーを処理する関数へのポインタを指定する必要があります。

Set setCreate(copySetElements copyElement); 

私は以下のタイプやコピー機能を書いた:

typedef struct location_t { 
    char *name; 
} *Location; 

Location locationCopy(Location location){ 

    Location new_location = locationCreate(location->name); 

    return new_location; 
} 

*もちろん、私は議論の焦点を合わせるためにすべてを簡素化。

は私が呼ぶとき:

Set locations = setCreate(locationCopy); 

私はコンパイルエラーを取得:

警告:互換性のないポインタ タイプから 'setCreate' の引数1を渡し

期待 'copySetElements' は引数の型は 'struct location_t *(*)(struct location_t *)'です。

+2

おそらく関数ポインタを除いて、 'typedef'の後ろにポインタ性質を隠さないでください。あなたを含む誰もが混乱します。 –

答えて

3

あなたの例では、関数へのポインタC standard

typedef void * (*VF)(void *); 
typedef int * (*IF)(int *); 

IF a = 0; 
VF b = a; //Warning 

に煮詰めできるオブジェクトへのポインタ未満汎用性があります。
それ

voidへのポインタがかもしれに対して明示的に可能にする標準で句があるので、あなたは、任意のキャスト(と警告)なしvoid(バック)へのポインタにオブジェクトへのポインタに変換することができます任意のオブジェクト型へのポインタからまたはそのポインタに変換されます。
へのポインタは、すべてのオブジェクトタイプをvoidへのポインタに変換して戻すことができます。
結果は、 が元のポインタと等しいと比較されます。

ここで、関数はオブジェクトではありません。関数へのポインタについて

標準保証があることを唯一のもの:

一種の関数へのポインタが再び別の型の関数へのポインタに変換することができます。結果は元のポインタと等しいとみなされます。変換されたポインタは、型が参照される型と互換性がない機能を呼び出すために使用されている場合
は、動作は未定義である

その後標準は2つの機能が互換性を持つため、それがどういう意味を明確に:

互換性のある2つの関数型の場合、両者は互換性のある戻り値の型を指定します。
さらに、パラメータタイプリストは、両方が存在する場合は、パラメータの数と省略記号ターミネータの使用で一致します。対応するパラメータは互換性のある型を持つものとする。

今、あなたはすべての後に、あなたがお互いを割り当てることができ、void*struct location_t*は互換性のある型であることを考えることがあります。
しかし、標準は、クリスタルクリアです:自分のタイプが同じであれば

二つのタイプが互換性のあるタイプがあります。

これは、より複雑な型と修飾された型のために、この関係を拡張することで後に続きます。

驚くかもしれませんが、int*void*は互換性がありません。
floatを指すことができ、または異なる整列要件を持つオブジェクトの後に割り当てることはできますが、互換性はありません。

異なるタイプのポインタについて言えば、標準では主にそれらを前後に割り当てることが関係しています。
互換性のないポインタ型を変換することは禁じられていませんが、それらを使用することは未定義の動作です。


より良いアプローチは、関数as suggested in this answerの内部でキャストを実行することです。

関数へのポインタの間にキャストが必要ないように、すべての関数には符号void* (void*)が必要です。 SetElementポインタが互換性のないポインタにキャストしている場合

SetElement locationCopy(SetElement element) 
{ 
    Location location = (Location)element; 

    Location new_location = locationCreate(location->name); 

    return new_location; 
} 

関数内キャストが再び未定義の動作の対象となっているが、あなたは本当に期待どおりに(のみLocation sのポインタをlocationCopyと呼ぶことにしますならば、それは起こるべきではありませんにする)。サイドノートとして


、いくつかのプログラマは、ポインタタイプを隠すためにtypedefを使用時に顔をしかめてもよいです。

+0

標準への引用の非常に良い使用。注記は、依存している特定の基準への参照を含めることだけです。 (例えば、C89/90、C99、C11)。私はそれがこのケースで違いをもたらすとは思わないが、それらの間には微妙で重要な変化がある。 (あなたがC11ドラフトへのリンクを提供したのが分かります - それで十分です) –

0

は、あなたはまた、

copySetElements => Set 

としてsetCreateを定義

POINTER (SetElement => SetElement) 

としてcopySetElementsを定義し、あなたはパラメータでsetCreateを呼び出して警告がこれから生成され

POINTER(Location => Location) 

タイプのlocationCopyイムコ宣言されたパラメータ型と実際の引数の型の互換性。

関連する問題