struct key new_node = (struct key) calloc(1, sizeof(struct key));
calloc
возвращает значение указателя (void *
), которое вы пытаетесь преобразовать и назначить агрегатному (IOW, нескалярному) типу (struct key
). Чтобы это исправить, измените тип new_node
на struct key *
и переписайте свое распределение следующим образом:
struct key *new_node = calloc(1, sizeof *new_node);
Две вещи на заметку. Прежде всего, угробите литое выражение. malloc
, calloc
и realloc
все возвращают void *
, которые могут быть присвоены любому типу указателя объекта без необходимости приведения 1 . Фактически, присутствие приведения может потенциально замаскировать ошибку, если вы забудете включить stdlib.h
или у вас нет объявления для malloc
в области действия 2 .
Во-вторых, обратите внимание, что я использую выражение *new_node
в качестве аргумента для sizeof
, а не (struct key)
. sizeof
не оценивает его оператор (если только это не переменный тип массива, а это не так); он просто вычисляет тип выражения. Поскольку тип выражения *new_node
равен struct key
, sizeof
вернет правильное количество байтов для хранения этого объекта. Это может сэкономить некоторые трудности при обслуживании, если ваш код структурирован как
T *foo;
... // more than a few lines of code
foo = malloc(sizeof (T))
и вы измените тип foo
в объявлении, но не забудьте обновить вызов malloc
.
Кроме того, неясно, чего вы пытаетесь достичь с помощью определений типов и структур. Код
typedef struct value_t value;
struct value{
void* x;
int y;
value* next;
value* prev;
};
не делает то, что вы думаете. Вы создаете имя typedef value
, которое является синонимом для пока неопределенного типа struct value_t
. Этот тип value
отличается от типа struct value
, который вы создадите позже (имена typedef и теги struct находятся в разных пространствах имен). Перепишите свои структуры, чтобы следовать этой модели:
struct value_t {
void *x;
int y;
struct value_t *next;
struct value_t *prev;
};
typedef struct value_t value;
Кроме того, жизнь будет проще, если вы напишите декларации так, чтобы *
ассоциировался с декларатором, а не спецификатором типа 3 . Декларация типа T* p
анализируется, как если бы она была написана T (*p)
. Это избавит вас от смущения написания int* a, b;
и ожидания того, что и a
, и b
будут указателями (b
- это просто обычный int
).
1 - это одна из областей, где C и C ++ различаются; C ++
не допускает неявные преобразования между
void *
и другими типами указателей объектов, поэтому, если вы скомпилируете это как код C ++, вы получите ошибку во время компиляции. Кроме того, до принятия стандарта 1989 года функции
*alloc
возвращали
char *
, поэтому в те дни приведение
требовало , если вы назначали другой тип указателя. Это должно быть проблемой, только если вы работаете на
очень старой системе.
2 - Вплоть до стандарта 1999 года, если компилятор видел вызов функции без предшествующего объявления, он предполагал, что функция вернула int
(именно поэтому вы все еще время от времени видите примеры, подобные
main()
{
...
}
в некоторых уроках; main
это неявно набрано, чтобы вернуть int
. Начиная с C99, это больше не разрешено). Поэтому, если вы забудете включить stdlib.h
и вызвать calloc
(а вы не компилируете как C99), компилятор примет функцию, возвращающую int
, и сгенерирует машинный код соответствующим образом. Если вы оставите преобразование выключенным, компилятор выдаст диагностическое сообщение о том, что вы пытаетесь присвоить указателю значение int
, что недопустимо. Если вы оставите приведение, код скомпилируется, но значение указателя может быть изменено во время выполнения (преобразование указателей в int
и обратно в указатели снова не гарантируется).
3 - Есть несколько редких случаев, ограниченных C ++, где стиль T* p
может сделать код немного более понятным, но в целом вам лучше следовать стилю T *p
. Да, это личное мнение, но оно подкреплено нетривиальным количеством опыта.