Доступ к массивам в указателе на структуру

У меня есть простая структура:

typedef struct {
    void *things;
    int sizeOfThings;
} Demo;

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

Demo * Create(int value) {
    Demo *d = malloc(sizeof(Demo));
    if (d != NULL) {
        d->sizeOfThings = value;
        d->things = malloc(20 * value); // We'll have a max of 20 things
    }
}

Например, value - это sizeof(int) для массива целых чисел.

Если в другой функции я хочу что-то вставить в d->things (предполагая, по крайней мере, что я просто добавляю это в первый слот, управление позициями выполняется в другом месте):

char * thing = "Me!";
strncpy(d->things[0], &thing, d->sizeOfThings);

Я обхожу область strncpy

test.c:10: warning: pointer of type ‘void *’ used in arithmetic
test.c:10: warning: dereferencing ‘void *’ pointer
test.c:10: error: invalid use of void expression

Я просто пытаюсь понять использование void* как способ обобщить мои функции. Я подозреваю, что что-то не так с d->things[0].


person Rio    schedule 22.04.2011    source источник


Ответы (4)


Согласно стандарту C, void не имеет размера -- sizeof(void) не определен. (Некоторые реализации делают его sizeof(int), но это не соответствует требованиям.)

Когда у вас есть массив типа foo, это выражение:

array[3]

Добавляет 3*sizeof(foo) к адресу, хранящемуся в массиве, а затем уважает его. Это потому, что все значения упакованы вместе в памяти. Поскольку sizeof(void) не определен, вы не можете сделать это для массивов void (на самом деле вы даже не можете иметь массивы void, только указатели void.)

Вы должны привести любой указатель void к другому типу указателя, прежде чем рассматривать его как массив:

d->things = malloc(20 * sizeof(int));
(int *)(d->things)[0] = 12;

Однако имейте в виду, что вам даже не нужно делать это, чтобы использовать на нем strncpy. Strncpy может нормально принимать указатель void. Но вы неправильно использовали strncpy. Ваш вызов strncpy должен выглядеть так:

strncpy(d->things, thing, d->sizeOfThings);

Что бы ваша версия сделала, так это попыталась бы обработать первый член массива d->things как указатель, когда это не так, и обработала бы &thing, который является char **, как если бы это был просто char *.

person Max E.    schedule 22.04.2011
comment
Это полезно. Буду ли я использовать арифметику указателя для будущих элементов strncpy? - person Rio; 22.04.2011
comment
Ты прав. Но в данном случае это было sizeof(void *), а не sizeof(void). - person shinkou; 22.04.2011
comment
Я не уверен, что ты имеешь в виду. Strncpy скопирует строку (которая представляет собой просто массив символов). Если вы хотите объединить несколько строк в один большой буфер, вы можете использовать strncpy для указателя смещения, но гораздо проще использовать strncat. - person Max E.; 22.04.2011
comment
Ну в идеале это массив void *? - person Rio; 22.04.2011
comment
В этом случае вы должны изменить объявление в своей структуре на void **things. И тогда да, вы используете d-›things[0] (в том числе при присвоении результата malloc!) - person Max E.; 22.04.2011

Попробуйте посмотреть, решит ли это вашу проблему:

char *thing = "Me!";
strncpy(&d->things[0], thing, d->sizeOfThings);

Затем используйте указатели, чтобы избавиться от предупреждений, но вы должны быть уверены, что собираетесь делать.

char *thing = "Me!";
strncpy((char *) &d->things[0], (const char *) thing, d->sizeOfThings);
person shinkou    schedule 22.04.2011

Demo *d = malloc(sizeof(Demo));
if (d != NULL) {
    d->things = malloc(20 * sizeOfThings); // We'll have a max of 20 things
}

Чем инициализируется sizeOfThings? Вероятно, это может быть мусор и вызывает ошибку. Даже если он инициализирован значением 0 по умолчанию, malloc возвращает NULL( malloc( 20 * 0 ) ; ). А так, я подозреваю -

strncpy(d->things[0], &thing, d->sizeOfThings);
      // ^^^^^^^^^^ causing the error.
person Mahesh    schedule 22.04.2011
comment
@Rio - В предупреждении ясно сказано - test.c:10: warning: dereferencing ‘void *’ pointer - person Mahesh; 22.04.2011
comment
Для sizeOfThings установлено значение sizeof(char *) для строк и sizeof(int) для целых чисел. - person Rio; 22.04.2011
comment
@Rio. Вы запрашиваете память размером 20*sizeOfThings. Но в момент выполнения оператора malloc каково значение sizeOfThings? Также, как предложил @shinkou, второй параметр должен быть thing. - person Mahesh; 22.04.2011
comment
Сейчас я работаю с массивом целых чисел. gdb говорит мне, что sizeOfThings равен 4. - person Rio; 22.04.2011
comment
@Rio - Где вы инициализируете sizeOfThings до 4, которые у вас не указаны в части вопроса, который вы разместили? Я подозреваю, что вы видите sizeof(sizeOfThings), что в любом случае составляет 4 байта. - person Mahesh; 22.04.2011

Две вещи:

Во-первых, определенно что-то не так с использованием d->things[0]. d->things на самом деле является указателем, и соглашение состоит в том, что указатели и массивы в основном взаимозаменяемы (за некоторыми исключениями), и имя массива всегда будет указывать на первый элемент массива.

Во-вторых, функциональной сигнатурой strncpy является char* strncpy(char* назначение, const char* источник, size_t num);. Итак, чтобы это работало, мы должны преобразовать d->thing из void* в char* и убедиться, что мы передаем вещь как char* (просто вещь) вместо char** (то есть вещь&).

поэтому вместо этого мы хотим это утверждение:

strncpy((char*)d->вещи, вещи, d->sizeOfThings);

Как только изменения внесены, остальная часть кода компилируется и запускается, как и ожидалось.

person Marc    schedule 22.04.2011