2009-11-18 2 views
4
typedef struct dict_pair { 
    void *key; 
    void *value; 
    struct dict_pair *head; 
    struct dict_pair *tail; 
} dict; 

dict* NewDictionary(void) { 
    dict *dictionary = malloc(sizeof(dict_pair)); 
    dictionary->head = null; 
    dictionary->tail = null; 
} 

int main(void) { 
    dict *dictionary = NewDictionary(); 
} 

Первоначально я планировал установить структуры как нулевые, но компилятор не разрешил это. Как проверить, назначена ли структура или нет?Как проверить, инициализированы ли структуры или нет?

Кроме того, можно ли рекурсивно объявить одну и ту же структуру внутри структуры?

+8

Используйте 'NULL' вместо' null'. 'null' не является частью C. –

+0

напишите как ответ, я хочу дать вам реквизиты для него. – nubela

+0

Подождите ... так что проблема в том, что «null» не был распознан? то да, вместо ответа вместо этого используется «NULL». Или 0, если NULL не будет определено в вашей среде разработчиков, что редко бывает в наши дни, но все же возможно. – Randolpho

ответ

8

C не имеет null, он имеет NULL. Так что попробуйте это:

dict* NewDictionary(void) { 
    return calloc(sizeof(dict)); 
} 

Это исправляет несколько проблем:

  1. Вы уезжали value и key неинициализированный, чтобы они могли провести случайный мусор. Использование calloc() будет инициализировать все до 0, что в контексте указателя NULL. Это даже не займет столько времени обработки.
  2. Вы ничего не возвращали. Это неопределенное поведение. Если вы заканчиваете работу без инструкции return, то только благодаря удачи все будет возвращено.
  3. Вы использовали dict_pair вместо struct dict_pair. В C++ имена struct находятся в обычном пространстве имен типов, то есть t x = { 0 }; действителен C++, но в C вам необходимо указать struct t x = { 0 };.
  4. Вы не проверяли возвращаемое значение malloc() (сейчас calloc(), но применяются те же правила). Если памяти недостаточно, calloc() возвращает NULL. Мне не хотелось бы разыгрывать указатель NULL на случай аварии. Нам не нужно проверять возвращаемое значение здесь, потому что я покончил со всеми промежуточными шагами - для нас достаточно calloc().

Отметьте, что calloc() немного менее портативен. Несмотря на то, что стандарт требует, чтобы void *p = 0 устанавливает указатель на нулевой указатель, он не требует, чтобы нулевой указатель был «все биты установлены на ноль», что технически делает calloc().Если вы не хотите использовать calloc() по этой причине, вот версия, которая делает то же самое с malloc():

dict* NewDictionary(void) { 
    dict *dictionary = malloc(sizeof(dict)); 
    if(dictionary) { 
    dictionary->head = NULL; 
    dictionary->tail = NULL; 
    dictionary->value = NULL; 
    dictionary->key = NULL; 
    } 
    return dictionary; 
} 

Или:

dict* NewDictionary(void) { 
    dict *dictionary = malloc(sizeof(dict)); 
    if(dictionary == NULL) return NULL; 
    dictionary->head = NULL; 
    dictionary->tail = NULL; 
    dictionary->value = NULL; 
    dictionary->key = NULL; 
    return dictionary; 
} 

Посмотрите, как гораздо лучше, версия calloc() это?

Что касается Вашего второго вопроса:

Кроме того, я могу рекурсивно объявить обратитесь же структура внутри структуры?

Нет, вы не можете сделать это:

struct t { 
    struct t x; 
} 

Но вы можете сделать это (то, что вы делаете, и то, что вы хотите):

struct t { 
    struct t *x; 
} 

У вас может быть указатель на struct внутри самого struct, но у вас не может быть фактического struct внутри struct себя. То, что вы делаете, совершенно законно, потому что вы используете указатели.

+1

Версия 'calloc' может быть приятнее, но технически не переносима. Он устанавливает основное представление указателей на все биты-ноль, но это * не * обязательно то же самое, что и «NULL» (хотя значение «0» при преобразовании в указатель сравнивается с нулевым указателем). (Не то, чтобы я знал о любом месте, где указатели с нулевыми значениями не равны «NULL», но что-то, о чем нужно помнить). – caf

+0

@caf - Я был просто осведомлен об этом. Я проверил и, хотя я думал, что формулировка - это просто «набор наборов вызовов до нуля», он указывает «все биты нуль», что означает, что да, я считаю, что этот супер-край-регистр действителен. –

+1

Да, это сформулировано именно так, потому что не разумно для реализации знать, как вы собираетесь обрабатывать возвращенную память. То же самое относится к использованию 'calloc' для распределения значений с плавающей запятой - все-биты-ноль не обязательно' 0.0f' – caf

0

0 Вы можете задать себе вопрос NULL, но не такой null. C чувствителен к регистру, а константа NULL - это все колпачки.

И, чтобы ответить на ваш второй вопрос, да, struct определения могут быть рекурсивными в некотором смысле. Внутренняя ссылка должна быть указателем на struct вместо прямого определения struct. Если бы последний был разрешен, вы получили бы бесконечно рекурсивное определение struct, что было бы плохо. См. Ответ Криса Лутца для более подробной информации.

1

Возможно, вы захотите рассмотреть calloc, а не malloc.

calloc заполняет память, которую он выделяет с помощью 0s, поэтому у вас будет голова и хвост как NULL без явного назначения.

+0

ничего себе, интересно, СПАСИБО! – nubela

+0

+1 для Джона Уэйна здесь. –

+0

Это неправильно; не делай это. –

1

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

dict* NewDictionary(void) { 
    static dict null_dict; // no initializer, so zero by default 
    dict *dictionary = malloc(sizeof *dictionary); 
    *dictionary = null_dict; 
    return dictionary; 
} 

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

+0

+1 для экстремальной ловкости. –

0

Я использую трюк, который работает для меня.

struct hello{ 
    ....; 
    ....; 
    ....; 
}; 

struct hello *t; 

t=malloc(sizeof(struct hello)); 
free(t); 
t=NULL; 

Теперь и можно легко проверить, если t инициализируется или нет. И утечки памяти вообще нет.

Смежные вопросы