Разыменование указателя на массив void - PullRequest
2 голосов
/ 04 августа 2011

Я пытаюсь узнать больше о C и его тайных скрытых способностях, и я попытался создать образец структуры, содержащий указатель на пустоту, предназначенный для использования в качестве массива.РЕДАКТИРОВАТЬ: Важное примечание: это для необработанного кода C.

Допустим, у меня есть эта структура.

    typedef struct mystruct {
        unsigned char foo;
        unsigned int max;
        enum data_t type;
        void* data;

    } mystruct;

Я хочу, чтобы данные содержали макс любых неподписанных символов, unsigned short int и unsigned long int, перечисление data_t содержит значения для этих трех случаев.

    enum Grid_t {gi8, gi16, gi32}; //For 8, 16 and 32 bit uints.

Затем у меня есть эта функция, которая инициализирует и выделяет одну из этих структур, и должна возвращать указательв новую структуру.

    mystruct* new(unsigned char foo, unsigned int bar, long value) {
        mystruct* new;
        new = malloc(sizeof(mystruct)); //Allocate space for the struct.
        assert(new != NULL);
        new->foo = foo;
        new->max = bar;
        int i;
        switch(type){
            case gi8: default:
                new->data = (unsigned char *)calloc(new->max, sizeof(unsigned char));
                assert(new->data != NULL);
                for(i = 0; i < new->max; i++){
                    *((unsigned char*)new->data + i) = (unsigned char)value;
                    //Can I do anything with the format new->data[n]? I can't seem
                    //to use the [] shortcut to point to members in this case!
                }
            break;
        }
        return new;
    }

Компилятор не возвращает предупреждений, но я не слишком уверен в этом методе.Это законный способ использования указателей?

Есть ли лучший способ ©?

Я пропустил это.как mystruct * P;P = новый (0,50,1024);

Союзы интересны, но не то, что я хотел.Так как мне все равно придется подходить к каждому конкретному случаю индивидуально, кастинг выглядит так же хорошо, как объединение.Я специально хотел иметь гораздо большие 8-битные массивы, чем 32-битные, поэтому объединение, похоже, не помогает.Для этого я бы сделал массив длинных: P

Ответы [ 4 ]

2 голосов
/ 04 августа 2011

Нет, вы не можете разыменовать указатель void*, это запрещено стандартом языка C.Вы должны привести его к конкретному типу указателя, прежде чем сделать это.

В качестве альтернативы, в зависимости от ваших потребностей, вы также можете использовать union в своей структуре вместо void*:

typedef struct mystruct {
    unsigned char foo;
    unsigned int max;
    enum data_t type;
    union {
        unsigned char *uc;
        unsigned short *us;
        unsigned int *ui;
    } data;
} mystruct;

В любой момент времени допустим только один из data.uc, data.us или data.ui, поскольку все они занимают одно и то же место в памяти.Затем вы можете использовать соответствующий член для доступа к массиву данных без необходимости приведения из void*.

1 голос
/ 04 августа 2011

А как же

typedef struct mystruct 
{
    unsigned char foo;
    unsigned int max;
    enum data_t type;
    union
    {
        unsigned char *chars;
        unsigned short *shortints;
        unsigned long *longints; 
    };
} mystruct;

Таким образом, нет необходимости вообще разыгрывать. Просто используйте data_t, чтобы определить, к какому из указателей вы хотите получить доступ.

0 голосов
/ 04 августа 2011

Указатель - это просто адрес в области памяти. Вы можете интерпретировать это как хотите. Просмотрите union для получения дополнительной информации о том, как вы можете интерпретировать одно и то же место в памяти несколькими способами.

приведение между типами указателей распространено в C и C ++, и использование void * подразумевает, что вы не хотите, чтобы пользователи случайно разыменовывали (разыменование void * приведет к ошибке, но разыменование того же указателя при приведении к int * приведет к нет)

0 голосов
/ 04 августа 2011

Должен ли type быть аргументом функции? (Не называйте эту функцию или любую переменную new, или любой программист C ++, который попытается использовать ее, выследит вас)

Если вы хотите использовать индексы массива, вы можете использовать временный указатель, подобный этому:

unsigned char *cdata = (unsigned char *)new->data;
cdata[i] = value;

Я не вижу проблемы с вашим подходом. Если вы ожидаете определенного размера (который, как мне кажется, вы дали имя gi8 и т. Д.), Я бы предложил включить stdint.h и использовать typedefs uint8_t, uint16_t и uint32_t.

...