Запрос простого примера кода на C, который показывает, как можно использовать универсальный или нетипизированный (через void *) массив - PullRequest
3 голосов
/ 10 марта 2009

Я пытаюсь создать универсальный (или нетипизированный) массив в C (я знаю, что C ++ делает это проще). В двух словах я хочу выделить массив для хранения массива определенного известного типа (во время выполнения). В реальной реализации это зависит от ввода пользователя.

Я пытался использовать сценарий enum / struct, следуя совету, найденному в нескольких хитах Google, но боюсь, что моя неопытность с пустыми указателями и отсутствие конкретного примера мешают мне получить работающий кусок кода , (Обычно я бы просто купил книгу, но я нахожусь в стране, где я не говорю на языке, а книг по английскому языку не существует.)

Проблема сводится к следующему упрощению: у меня есть изображение (только 1D), где значения пикселей могут быть int, float или double. У меня есть функция, которая скажет мне тип. Все, что я хочу сделать, это сохранить пиксели в массиве соответствующего типа. (На практике эти изображения очень большие, и я мотивирую их экономить память и предотвращать написание блоков кода для каждого типа.)

Я пробовал что-то вроде следующего, но, может быть, это не лучший вариант (фрагмент кода для возможной структуры данных):

enum type {
    typeint, typefloat, typedouble
};

struct genericarray {
    enum type type;
    void *storage;
};

Каким-то образом я хочу сохранить эти пиксели в экземплярах generciarray. Все мои попытки до сих пор превратились в "предупреждение: разыменование" пустых указателей "сторон, которые, я признаю, я не понимаю.

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

Ответы [ 5 ]

4 голосов
/ 10 марта 2009

Я бы описал все это следующим образом:

#include "stdlib.h"
#include "stdio.h"

// structure
enum type { typeint, typefloat, typedouble };

struct genericarray
{
    enum type type;
    void ** storage;
};

typedef struct genericarray genericarray;

// allocate
void allocate(long numItems, enum type varType, genericarray * array)
{
    (*array).type = varType;
    switch (varType)
    {
        case typeint:
            (*array).storage = malloc(numItems*sizeof(int));
            break;
        case typefloat:
            (*array).storage = malloc(numItems*sizeof(float));
            break;
        case typedouble:
            (*array).storage = malloc(numItems*sizeof(double));
    }
}

// release
void release(genericarray array)
{
    free(array.storage);
}

// usage
int main(int argCount, char ** argList)
{
    genericarray image_1;
    genericarray image_2;

    int iv;
    float fv;

    allocate(10, typeint, &image_1);
    allocate(10, typefloat, &image_2);

    ((int *)(image_1.storage))[5] = 42;
    iv = ((int *)(image_1.storage))[5];
    printf("image_1[5] = %d\n", iv);

    ((float *)(image_2.storage))[5] = 3.14159;
    fv = ((float *)(image_2.storage))[5];
    printf("image_2[5] = %f\n", fv);

    release(image_2);
    release(image_1);

    return 0;
}
2 голосов
/ 10 марта 2009

На самом деле вы не указываете строки, которые выдают ошибку, но я думаю, что они могут выглядеть немного как

struct genericarray ga;
float fValue;

fValue = ga.storage[idx];

Что вы можете сделать, это добавить актерский состав

fValue = ((float*)ga.storage)[idx];

Я бы рекомендовал создать несколько макросов или функций для установки и получения значений

#define GET_STORAGE(_type, _src, _idx) \
  ((_type*)_src.storage)[_idx]
#define SET_STORAGE(_type, dst, _idx, _src) \
  ((_type*)_dst.storage)[_idx] = _src 

fValue = GET_STORAGE(float, ga, 3);

SET_STORAGE( float, ga, 3, sin(ang) );

Я вижу, как «Крис Янг» использует операторы switch в своих ответах, и это хорошо для этих функций доступа.

2 голосов
/ 10 марта 2009

Это потому, что вы не можете разыменовать пустой указатель, не приведя его к чему-то другому. Это потому, что реализация C должна знать, к какому типу объектов относится разыменованный указатель.

В дополнение к уже предложенному объединению вы можете использовать метод, на который вы ссылаетесь, но вам придется условно преобразовать (неявно или явно с помощью приведения), если вы хотите разыменовать хранилище:

int *iptr;
double *dptr; 

switch (x.type)
{
    case typedouble:
        dptr = x.storage; // implcit conversion example
        // reference your "image" as dptr[i] now
        break;
    case typeint:
        iptr = (int *)x.storage; // explicit conversion, actually unnecessary
        // reference your "image" as iptr[i] now
        break;
 }
2 голосов
/ 10 марта 2009
enum type {
    typeint, typefloat, typedouble
}

struct genericarray {
    enum type type;
    union { int i; float f; double d; } storage;
};

это то, что вам нужно (imho.)

EDIT: чтобы использовать void * в качестве контейнера для int / float / double, вы должны выделить его: (массив - это шаблон вашего старого типа - с void * storage)

array.storage = malloc(sizeof(int));

и разыменование как:

(*((int*)array.storage))

заменить float для типа == typefloat и т. Д.

и вы также должны освободить его

free(array.storage);
0 голосов
/ 10 марта 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...