void *の意味と使用方法は?


147

今日私が他の人のコードを読んでいたとき、私はのようなものを見ましたvoid *func(void* i);、これはvoid*それぞれ関数名と変数タイプに対してここで何を意味しますか?

さらに、この種のポインターをいつ使用する必要がありますか?


2
どんなCブックを使っていますか?あなたは章全体のより良い部分を求めています。
cnicutar 2012




mallocとからキューを取得しcallocます。manページにはさらに、「...割り当てられたメモリへのポインタが返されます。これは、組み込みデータ型に合わせて適切に配置されています。」
オートマトン

回答:


175

へのポインタvoidは、「一般的な」ポインタ型です。void *明示的なキャストなしで、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);

配列式はiArrdArr、および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はその引数がintsを指すことを期待していますが、実際にはdoublesを使用しています。コンパイル時にこの問題を見つける方法はありません。あなたはちょうどmisorted配列で終わります。


5
void*が関数ポインタにキャストできることは実際には保証されていません。しかし、データポインタについては、あなたが言ったことはそのままです。
Vatine

voidポインタが利用可能になる前は、代わりに「char *」が使用されていました。ただし、実際には何かを直接変更するために使用することはできないため、voidの方が適しています。
user50619

22

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));

これは、他の言語のジェネリックとほとんど同じですが、型チェックがありません。
ウィンガーセンドン

3
似ていると思いますが、型チェックがないため、ミスをすると非常に奇妙な結果が生じたり、プログラムが完全にクラッシュする可能性があります。
TheSteve 2016

7

Cはこの点で注目に値します。何もかもがすべてでvoidはないvoid*(すべてになることができる)と言うことができます。

*違いを生み出しているのは、この小さなことだけです。

ルネはそれを指摘しました。A void *はある場所へのポインターです。「解釈」の方法はユーザーに任されています。

これは、Cで不透明な型を持つ唯一の方法です。非常に有名な例は、glibや一般的なデータ構造ライブラリなどにあります。「Cインターフェイスと実装」で非常に詳細に扱われています。

完全な章を読み、「理解する」ための指針の概念を理解することをお勧めします。


5
void*

「どのタイプがそこに格納されているかを想定しないメモリへのポインタ」です。たとえば、関数に引数を渡したい場合に使用できます。この引数は複数のタイプであり、関数では各タイプを処理します。


3

ポインタに関するこの記事http://www.cplusplus.com/doc/tutorial/pointers/をご覧になり、次の章をお読みください。voidポインタ

これはC言語でも機能します。

void型のポインターは、特殊な型のポインターです。C ++では、voidは型の不在を表します。したがって、voidポインターは、型のない値(つまり、長さが未確定で逆参照のプロパティも未確定)を指すポインターです。

これにより、voidポインターが整数値または浮動小数点から文字列までの任意のデータ型を指すことができます。しかし、引き換えにそれらには大きな制限があります:それらによってポイントされたデータは直接逆参照することができません(逆参照するタイプがないため、これは論理的です)。そのため、常にvoidポインターのアドレスをキャストする必要があります。逆参照する前に具象データ型を指す他のポインタ型。


3

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);
}

メインの最後ではpthread_exit(NULL);なく、なぜ呼び出すのreturn 0;ですか?
Seabass77 2018年

1
@ Seabass77参照してくださいstackoverflow.com/questions/3559463/...
Jeyaram

1

a void*はポインターですが、それが指すものの型は指定されていません。関数にvoidポインターを渡す場合、後でその関数を使用するために、関数内で正しい型にキャストバックするために、その型が何であるかを知る必要があります。あなたはの例が表示されますpthreadsスレッド関数として使用されているあなたの例では、正確にプロトタイプとその利用機能を。その後void*、選択した汎用データ型へのポインターとして引数を使用し、それをその型にキャストして、スレッド関数内で使用できます。voidポインターを使用するときは注意が必要ですが、その真の型のポインターに戻らないと、あらゆる種類の問題が発生する可能性があります。


1

C11標準(n1570)§6.2.2.3al1 p55は言う:

へのvoidポインタは、任意のオブジェクト型へのポインタとの間で変換できます。オブジェクト型へのポインタは、voidへのポインタに変換され、再び元に戻されます。結果は、元のポインタと同じになります。

このジェネリックポインターを使用して、任意のオブジェクト型へのポインターを格納できますが、通常の算術演算を使用することはできず、参照することもできません。


0

この関数は、任意の型へのポインタを受け取り、そのような1つを返します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.