web-dev-qa-db-ja.com

void **ポインターを正しく使用する方法?

私は二重のvoidポインターを使用しようとしていますが、その使用法について少し混乱しています。 _void **_配列を含むstructがあります。

_struct Thing{
    void ** array;
};

struct Thing * c = malloc (sizeof(struct Thing));
c->array = malloc( 10 * sizeof(void *) );
_

したがって、各ポインターに異なるオブジェクトを割り当てて、値を取得しようとする場合

_ // Option 1
 *(c->array + index) = (void *) some_object_ptr;

 // Option 2
 c->array[index] = (void *) some_object_ptr;
_

次に、_(void *) item_ではなく、各セルを指す_some_object_ptr_を提供する別の関数があります。

_some_object_ptr_が指す値を取得したい場合は、
すべきか

_ function return type is 'void *' and takes argument 'void *'
 // Option 3 
 return (void**) item

 // Option 4
 return *((void**)item)?
_

奇妙なことに、配列の添え字方式で配列を使用した場合、オプション4は使用できず、オプション3しか使用できません。 *(c->array + index)を使用したときは、opt.4しか使用できませんでした。 opt.3ではありません。 ..

誰か教えていただけますか?無効な仮定をしている場合は、修正していただけませんか。

22
in His Steps

void **は、型が指定されていないメモリへのポインタへの単なるポインタです。逆参照は1回しかできません(void *を逆参照することはできないため)。ただし、それ以外は基本的に他のポインター型と同じです。役立つ場合は、int *の場合と同じように考えてください。

したがって、特定の状況では、次のようになります。

void** array;
int arrayLen = 10;
array = (void**)malloc(arrayLen * sizeof(void*));

some_type_t* some_object_ptr;    
// The following two assignment are equivalent since in C,
// array[index] <=> *(array + index)
array[index] = (void*)some_object_ptr;
*(array + index) = (void*)some_object_ptr;

その場合、arrayは配列全体へのポインタですが、*arrayarray[0]と同等であるため、最初の要素へのポインタです。

28

ポインターに関する1つの簡単なヒント:キャストしている場合、おそらく何かが間違っています。

あなたの質問も。あなたの問題のitemが何であるかわかりません。最初の部分では、配列のメンバーにアクセスする方法をすでに発見しました。あなたは単にそれを使うことができます:

_void *get_item(Thing *c, int index)
{
    return *(c->array + index); // or return c->array[index];
}
_

インデックスにあるポインタのアドレスが必要な場合:

_void **get_item_cell(Thing *c, int index)
{
    return c->array + index; // or return &c->array[index];
}
_

2番目の部分では、ポインターを逆参照しない(+オプションの場合)か、配列結果のアドレスを取得します。これは、ポインターを自動的に逆参照するためです。


編集:私は今あなたが何を望んでいるかを知っていると思います。上記の2番目の機能と同様の機能がありますが、次のようになります。

_void *get_item_cell(Thing *c, int index)
{
    return (void *)(c->array + index);
}
_

この関数から返された値を逆参照して、オブジェクトにアクセスしたいとします。その場合、安全に使用できるのはオプション4のみです。インデックスがないため、他のセルに移動することはできません(配列の最後にあるのか、それとも先頭にあるのかわからないため、加算や減算はありません)。上記の関数の間違いのみを修正できます:_void **_にキャストし、それを逆参照します:*(void **)item。これにより、_void *_が得られます。このセルからポイントされているオブジェクトにアクセスする場合は、それも正しいタイプにキャストする必要があります:some_object_ptr *obj = *(void**)item

2
vhallac

あなたが使用しているという事実void*およびvoid**は関係ありません。ポインタ演算は引き続き正常に機能するため、記述した両方のオプションが正しいです。

次に例を示します。

struct Thing
{
    void ** array;
};

struct Object
{
    int i;
    char c;
};

int main ()
{

    struct Thing * c = malloc (sizeof(struct Thing));
    c->array = malloc(10 * sizeof(void*));

    struct Object * o = malloc (sizeof(struct Object));
    o->i = 2; o->c = 'a';

    *(c->array + 2) = o;

    printf("Object: i = %d, c = %c\n", ((Object*)c->array[2])->i, ((Object*)c->array[2])->c);

    free(o);
    free(c->array);
    free(c);
    return 0;
}

void*そこにポインタを置くことができます。使用する前に、元の型にキャストすることを忘れないでください;)

1
LihO