今日、他の人のコードを読んでいたときに、関数名と変数の型それぞれについて、void *func(void* i);
これはどういう意味ですか? のようなものを見ました。void*
さらに、いつこの種のポインターを使用する必要があり、どのように使用するのでしょうか?
今日、他の人のコードを読んでいたときに、関数名と変数の型それぞれについて、void *func(void* i);
これはどういう意味ですか? のようなものを見ました。void*
さらに、いつこの種のポインターを使用する必要があり、どのように使用するのでしょうか?
へのポインターvoid
は、「汎用」ポインター型です。Avoid *
は、明示的なキャストなしで他のポインター型に変換できます。a を逆参照しvoid *
たり、ポインター演算を実行したりすることはできません。最初に完全なデータ型へのポインターに変換する必要があります。
void *
同じコード内で異なるポインター型を操作できるようにする必要がある場所でよく使用されます。よく引用される例の 1 つは、ライブラリ関数qsort
です。
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
base
は配列のアドレス、 は配列nmemb
内の要素の数、size
は各要素のサイズ、 はcompar
配列の 2 つの要素を比較する関数へのポインタです。次のように呼び出されます。
int iArr[10];
double dArr[30];
long lArr[50];
...
qsort(iArr, sizeof iArr/sizeof iArr[0], sizeof iArr[0], compareInt);
qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareDouble);
qsort(lArr, sizeof lArr/sizeof lArr[0], sizeof lArr[0], compareLong);
配列式iArr
、dArr
、およびlArr
は、関数呼び出しで配列型からポインター型に暗黙的に変換され、それぞれが " int
/ double
/long
へのポインター" から "ポインターへのポインター" に暗黙的に変換されますvoid
。
比較関数は次のようになります。
int compareInt(const void *lhs, const void *rhs)
{
const int *x = lhs; // convert void * to int * by assignment
const int *y = rhs;
if (*x > *y) return 1;
if (*x == *y) return 0;
return -1;
}
を受け入れることvoid *
で、qsort
任意のタイプの配列を操作できます。
使用の欠点は、void *
型の安全性を窓から追い出し、対向車線に放り込むことです。間違った比較ルーチンの使用から保護するものは何もありません:
qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareInt);
compareInt
は引数が s を指していることを期待していますがint
、実際にはdouble
s を操作しています。コンパイル時にこの問題をキャッチする方法はありません。並べ替えられていない配列になってしまうだけです。
void * を使用することは、関数が特定の型である必要のないポインターを受け取ることができることを意味します。たとえば、ソケット関数では、
send(void * pData, int nLength)
これは、たとえば、さまざまな方法で呼び出すことができることを意味します
char * data = "blah";
send(data, strlen(data));
POINT p;
p.x = 1;
p.y = 2;
send(&p, sizeof(POINT));
この点で C は注目に値します。void
無void*
がすべてであると言えます(すべてである可能性があります)。
*
違いを生むのは、この小さなことだけです。
レネはそれを指摘した。Avoid *
は、ある場所へのポインタです。何を「解釈」するかはユーザーに委ねられています。
これは、C で不透明な型を持つ唯一の方法です。非常に顕著な例は、glib や一般的なデータ構造ライブラリなどで見つけることができます。「C インターフェイスと実装」で非常に詳細に扱われています。
この章全体を読んで、「get it」へのポインターの概念を理解しようとすることをお勧めします。
void*
「格納されている型を仮定しないメモリへのポインタ」です。たとえば、関数に引数を渡したい場合に使用できます。この引数は複数の型にすることができ、関数では各型を処理します。
void ポインターは、ジェネリック ポインターと呼ばれます。サンプルの pthread シナリオで説明したいと思います。
スレッド関数のプロトタイプは次のようになります。
void *(*start_routine)(void*)
pthread API の設計者は、スレッド関数の引数と戻り値を考慮しました。それらを汎用化すると、引数として送信しながら void* に型キャストできます。同様に、戻り値は void* から取得できます (ただし、スレッド関数からの戻り値を使用したことはありません)。
void *PrintHello(void *threadid)
{
long tid;
// ***Arg sent in main is retrieved ***
tid = (long)threadid;
printf("Hello World! It's me, thread #%ld!\n", tid);
pthread_exit(NULL);
}
int main (int argc, char *argv[])
{
pthread_t threads[NUM_THREADS];
int rc;
long t;
for(t=0; t<NUM_THREADS; t++){
//*** t will be type cast to void* and send as argument.
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
/* Last thing that main() should do */
pthread_exit(NULL);
}
ポインターに関するこの記事http://www.cplusplus.com/doc/tutorial/pointers/を見て、次の章を読むことができます: void ポインター。
これは C 言語でも機能します。
void 型のポインターは、特殊な型のポインターです。C++ では、void は型の不在を表すため、void ポインターは、型を持たない値を指すポインターです (したがって、不定の長さと未定の逆参照プロパティも)。
これにより、void ポインターは、整数値または float から文字列まで、任意のデータ型を指すことができます。しかし、引き換えに、それらには大きな制限があります。それらが指すデータを直接逆参照することはできません (逆参照する型がないため、これは論理的です)。そのため、void ポインター内のアドレスを常にキャストする必要があります。逆参照する前に具体的なデータ型を指す他のポインター型。
C11 標準 (n1570) §6.2.2.3 al1 p55 は次のように述べています。
へのポインター
void
は、任意のオブジェクト型へのポインターとの間で変換できます。任意のオブジェクト型へのポインターは、void へのポインターに変換され、再び元に戻される可能性があります。結果は元のポインタと等しくなります。
この汎用ポインターを使用して、任意のオブジェクト型へのポインターを格納できますが、通常の算術演算を使用することはできず、参照することもできません。
avoid*
はポインターですが、それが指すものの型は指定されていません。関数に void ポインターを渡す場合、後で関数内で正しい型にキャストして使用するために、その型が何であったかを知る必要があります。pthreads
スレッド関数として使用される例のプロトタイプとまったく同じ関数を使用する例が表示されます。次に、void*
引数を選択した汎用データ型へのポインタとして使用し、それをその型にキャストしてスレッド関数内で使用できます。void ポインターを使用する場合は注意が必要ですが、真の型のポインターに戻らない限り、あらゆる種類の問題が発生する可能性があります。
この関数は、任意の型へのポインターを受け取り、そのような型を返します。
これは、このリンクを使用してポインターに関する詳細情報を取得できるポインターを意味します http://www.cprogramming.com/tutorial/c/lesson6.html