Как проверить, инициализированы структуры или нет - PullRequest
4 голосов
/ 19 ноября 2009
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();
}

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

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

Ответы [ 5 ]

8 голосов
/ 19 ноября 2009

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() 1031 *

Обратите внимание, что 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 голос
/ 19 ноября 2009

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

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

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

1 голос
/ 19 ноября 2009

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

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

0 голосов
/ 15 марта 2014

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

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

struct hello *t;

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

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

0 голосов
/ 19 ноября 2009

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

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

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