10

C で qsort を使用して、比較関数を渡します。

int cmp(const void*, const void*);

qsort のプロトタイプは a を期待するint (* )(const void* , const void*)ので、次のように呼び出します。

qsort(..., cmp);

ただし、次の呼び出しも同様に有効です。

qsort(..., &cmp);

これは、C++ で静的メンバー関数を渡した場合に行う必要があることです。Kernighan & Ritchie (第 2 版、5.11「関数へのポインター」p119) は、「[cmp] は関数であることがわかっているため、& 演算子は、配列名の前に必要ないのと同様に、必要ありません。 "

他の誰かがこれに少し不快に感じますか (特にタイプセーフに関して)?

4

6 に答える 6

6

不快に感じるかどうかに関係なく、C が型安全な言語と見なされないという事実は変わりません。適例:

int main()
{
   int integer = 0xFFFFFF; 
   void (*functionPointer)() = (void(*)())integer; 

   functionPointer(); 

   return 0; 
}

これはコンパイル時に完全に有効ですが、明らかに安全ではありません。

于 2008-11-03T11:58:21.757 に答える
6

答えは、関数を値で渡すと関数ポインターが生成されるということです。配列を値で渡すと、最初の要素へのポインターが生成されるのと同じです。配列と関数は「崩壊」すると言われています。その減衰が起こらない場合はごくわずかです。たとえば、sizeof(array) は、最初の要素ポインターのサイズではなく、配列のサイズを返します。sizeof(function) は無効です (関数はオブジェクトではありません)。sizeof(&function) を実行する必要があります。その他の場合は、参照に拘束されます。

void baz();

void foo(void (&bar)()) {
    bar();
}

// doesnt work, since a reference to a function is requested. 
// you have to pass 'bar' itself, without taking its address 
// explicitely.
foo(&baz);  

これは、ところで、すべてがあなたができる理由です

template<typename T, int N>
void ByRef(T (&foo)[N]) { 
    ...
}

参照パラメーターを考慮すると、配列はまだ減衰していないためです。

于 2008-11-05T01:42:13.497 に答える
5

C で許可されている「金属に近い」コーディングに慣れていないため、不快に感じるだけだと思います。C が関数でアドレス取得演算子を必要としない理由は、関数を呼び出すか渡す以外に関数で実際にできることは何もないからです。

つまり、関数の「値」を操作するための適切な定義はありません。

int では、値に加算または値から減算できますが、これは関数には意味がありません。

いずれにせよ、C は C++ と独自の標準化の両方よりも前から存在します。元の K&R C にはプロトタイプさえありませんでした。ANSI は、標準化によって既存のコードができるだけ壊れないようにする必要がありました。

于 2008-11-03T12:12:03.117 に答える
3

構文の詳細です。& を使用して関数ポインターを示したくない場合は、常に使用してください。余分なキープレスを節約し、コーディングで明示的にしたくない場合は、それを利用してください。

タイプセーフに関しては、それがどのように違いを生むのかわかりません。コンパイラには引数として関数が与えられ、名前から完全に関数シグネチャが何であるかを判断できます。() 構文を使用して呼び出していないという事実は、この場所に関数ポインターが必要であることをコンパイラーが必要とするすべての指示を提供します。& は、何らかの記述のポインタを見ていることを人間に示すための単なる構文上の細やかさです。

C++ を使用している場合は、とにかく関数を渡すためのより良い方法として、ブースト ファンクターを検討することをお勧めします。これらの素敵なエンティティにより、C++ のすべての関数に対して統一されたかなり明確な構文が可能になります:)

于 2008-11-03T12:10:30.000 に答える
1

同じことが「逆参照」(つまり、呼び出し) 関数ポインターにも当てはまることに注意してください。あなたはする必要はありません

(*funcptr)(arg1, arg2);

なので

funcptr(arg1, arg2);

十分であろう。

于 2008-11-03T22:20:56.163 に答える
1

これは不快感というよりは、有効でありながら競合する構文オプションに対する嫌悪感です。いずれかcmpがポインタであり、そのように一貫して処理する必要があります。または、他のタイプです。これは、IMHO が構文的に誤解を招く可能性があります。

もう少し先に進むと、ポインターを逆参照する (ポインターであるという事実を強調する) か、しない (関数名ポインターであるため) かのいずれかを呼び出す必要がありますが、両方を許可することはできません。

関数ポインターは非常に強力ですが、初心者と経験豊富なプログラマーの両方にとって混乱の原因のようです。難しさの一部は、使用法を曖昧にするのではなく、明確にする単一の意味のある構文を要求することで軽減できます。

于 2008-11-03T12:11:04.053 に答える