Инициализация массивов в C с помощью void * - PullRequest
2 голосов
/ 06 января 2012

Скажем, у меня есть группа структур, для которых я хочу управлять массивами с помощью блоков указателей. Я хотел бы инициализировать эти блоки в общем виде, и вот что я думаю:

enum my_type {STRUCT_1, STRUCT_2, STRUCT_3} ;

struct my_struct_1 {...} ;
struct my_struct_2 {...} ;
struct my_struct_3 {...} ;

void * my_array_init(my_type t) {
    void * array = NULL ;
    switch(t) {
    case STRUCT_whatever :
        (my_struct_whatever **) array ; /* does this cast or is assignment needed? */
    default :
    }
    array = malloc(sizeof(*array) * BLOCK_SIZE) ;
    if(array) {
        for(i=0 ; i<BLOCK_SIZE ; ++i)
            array[i] = NULL ;
    }

    return array ;
}

Тогда я думал, что смогу использовать это как:

/* Initially set all pointers to NULL */
my_struct_1 ** s1 = NULL ;
my_struct_2 ** s2 = NULL ;
my_struct_3 ** s3 = NULL ;

s1 = my_array_init(STRUCT_1) ;
s2 = my_array_init(STRUCT_2) ;
s3 = my_array_init(STRUCT_3) ;

Я на правильном пути, у меня неправильный уровень косвенности или я должен просто упаковать его и написать отдельные функции 'init'? Что касается 'void *', так как он может указывать на все, что я думаю, это может быть дескриптор (указатель на указатель), и я видел противоречивые аргументы о 'void **' Тем не менее, 'void **' появляется по крайней мере в одном из примеров в K & R 2nd ed. - в «5.11 Указатели на функции» на стр. 119. Для чего бы это ни стоило, все структуры содержат только указатели и основные типы данных.

Ответы [ 4 ]

2 голосов
/ 06 января 2012

(my_struct_whatever **)array не будет постоянно изменять тип переменной. Вам нужно назначение, а в случае указателя void вам даже не понадобится приведение:

void *vp;
foobar_t *fp;

fp = vp;  // the cast is implicit; leaving it out is considered good style
0 голосов
/ 06 января 2012

Принимая во внимание мой предыдущий ответ и замечания Аарона МакДейда, получается, что вам даже не нужна функция my_array_init. Код вдоль этих линий будет делать:

#include <stdlib.h>

struct my_struct_1 {char a;} ;
struct my_struct_2 {short b;} ;
struct my_struct_3 {int c;} ;

#define BLOCK_SIZE 10

int main() {
    struct my_struct_1 ** s1 = calloc(BLOCK_SIZE, sizeof(void*));
    struct my_struct_2 ** s2 = calloc(BLOCK_SIZE, sizeof(void*));
    struct my_struct_3 ** s3 = calloc(BLOCK_SIZE, sizeof(void*));
    ...
}

Однако, если вы параноик, вы можете взглянуть на эти два обсуждения:

В чем разница между NULL, '\ 0' и 0

Все ли указатели данных имеют одинаковый размер на одной платформе для всех типов данных?

0 голосов
/ 06 января 2012

В этом конкретном примере функции my_init_array не нужно знать тип структуры. Это потому, что он создает массив указателей (все NULL), а не создает массив структуры.

(Предупреждение: технически возможно, что ваша платформа - это та, где sizeof(void*) отличается от sizeof(struct my_struct_1 *). См. этот ответ . Хорошая новость заключается в том, что указатели на все типы структур будут того же размера. Если вы параноик, вам следует соответствующим образом отредактировать этот код. В этом ответе я предположил, что sizeof(void*)==sizeof(struct my_struct_WHATEVER *). Возможно, вам лучше всего просто скопировать и вставить всю функцию три раза и написать одну функцию для каждого из три типа структуры. Здесь может помочь макрос.)

#include<stdlib.h>

enum my_type {STRUCT_1, STRUCT_2, STRUCT_3} ;

struct my_struct_1 { int x; };
struct my_struct_2 { double x; };
struct my_struct_3 { char * x; };

#define BLOCK_SIZE 10

void * my_array_init(void) {
    void ** array = NULL ;
    array = malloc(sizeof(*array) * BLOCK_SIZE) ;
    if(array) {
        int i;
        for(i=0 ; i<BLOCK_SIZE ; ++i)
            array[i] = NULL ;
    }
    return array ;
}

int main() {
        struct my_struct_1 ** s1 = my_array_init();
        struct my_struct_2 ** s2 = my_array_init();
        struct my_struct_3 ** s3 = my_array_init();
}

Я внес много изменений в код исходного вопроса. Я думаю, что интересная строка:

array = malloc(sizeof(*array) * BLOCK_SIZE) ;

sizeof(*array) не зависит от типа используемой структуры. array является указателем на указатель и, следовательно, *array является просто указателем. И размер указателя всегда одинаков. Поэтому эта строка никоим образом не должна зависеть от типа структуры. И array может быть типа void**.

Единственная другая интересная строка -

array[i] = NULL ;

Опять же, array[i] это просто указатель, и мы устанавливаем его в NULL. И, следовательно, этот код не зависит от типа структуры. Именно эта строка требует, чтобы массив был void**, а не void*

0 голосов
/ 06 января 2012

Функция calloc инициализирует выделенную память нулями, что вам и нужно, поскольку вы инициализируете все значения нулями (NULL). Итак, все, что вам нужно сделать, это что-то вроде:

#include <stdlib.h>

void * my_array_init(my_type t) {
    switch(t) {
    case STRUCT_1:
        return calloc(BLOCK_SIZE, sizeof(struct my_struct_1 *));
    case STRUCT_2:
        return calloc(BLOCK_SIZE, sizeof(struct my_struct_2 *));
    case STRUCT_3:
        return calloc(BLOCK_SIZE, sizeof(struct my_struct_3 *));
    default:
        return NULL;
    }
}
...