Ссылка на указатель на структуру, которая содержит указатель на указатель на структуру

#c #pointers #nested

#c #указатели #вложенный

Вопрос:

Мне трудно ссылаться на память внутри структуры, я не уверен, что мой синтаксис правильный (хотя, учитывая другие сообщения, я думаю, что я в порядке). Во время выполнения происходит сбой кода. Насколько мне известно, я выделил необходимую momory (52 карточки — строка 66), но я не уверен, какое приведение применить. Мне просто нужен этот небольшой толчок и руководство, большое спасибо!

 #include <stdio.h>

#define DECK_SIZE (52)

/* the enum suite definition */
enum suite {
    diamond = 1,
    club,
    heart,
    spade
};

/* the card definition */
struct card {
    int number;
    enum suite type;
};

/* the deck definition */
struct deck {
    struct card ** cards;
    int numCards;
};

/* **
 * Name: addCard(deck *myDeck);
 * Purpose: Add a card to the deck
 ** */
void addCard(struct deck * myDeck)
{
    int number,suiteType;
    printf("Please enter card number: n");
    scanf("%d",amp;number);
    printf("Please enter suite type: n");
    scanf("%d",amp;suiteType);
    /* increase myDeck->numCards by one */
    myDeck->numCards  = 1;
    /* reallocate the block and increase the size by one */
    *(myDeck->cards) = (struct card*) realloc ( *(myDeck->cards), sizeof(struct card) * myDeck->numCards);
    if ( NULL == *(myDeck->cards) ) {
        printf("realloc failed - exiting..n");
        free( *(myDeck->cards));        
        return;
    }
    /* put the data */
    myDeck->cards[myDeck->numCards-1]->number = number;
    myDeck->cards[myDeck->numCards-1]->type = suiteType;
}

/***
 * Name: initializeDeck();
 * Puspose: create a deck memory block and fill it
 ***/
struct deck * initializeDeck()
 {
    struct deck * myDeck;
    int num,suite,i;
    /* allocate memory for a deck */
    myDeck = (struct deck*) malloc ( sizeof(struct deck) );
    if (NULL == myDeck) {
        printf("Failed to allocate a deck, exiting..n");
        return 0;
    }
    /* allocte 52 cards */
    myDeck->numCards = DECK_SIZE;
    myDeck->cards = (struct card**) malloc ( sizeof(struct card) * myDeck->numCards );
    if (NULL == *(myDeck->cards)) {
        printf("Failed to allocate 52 cards, exiting..n");
        free(myDeck);
        return 0;
    }
    /* fill the deck */
    num = 1;
    suite=1;
    for (i = 0; i<DECK_SIZE; i  ) {
        myDeck->cards[i]->number = num;
        myDeck->cards[i]->type = suite;
        num  ;
        if (num > 13) {
            num = 1;
            suite  ;
        }
    }
    return myDeck;
 }

int main()
{
    struct deck * myDeck;
    myDeck = initializeDeck();
    addCard(myDeck);
    return 0;
}
  

Ответ №1:

На первый взгляд я вижу две вещи (которые могут не решить основную причину, но на самом деле могут помочь в написании большего количества кода сохранения ;-))

1.1 Если инициализация завершается неудачно, вы возвращаете значение NULL , но вы не проверяете результат initializeDeck() , а вызываете addCard , даже если myDeck значение равно NULL . Таким образом, в случае ошибки во время инициализации addCard происходит сбой при разыменовании myDeck .

Либо выполните следующие действия в main() :

 [...]
if (myDeck)
  addCard(myDeck);
[...]
  

или и даже лучше, и выполните следующие действия в addCard :

 void addCard(struct deck * myDeck)
{
  if (!myDeck) {
    printf("invalid inputn");
    return;
  }
  [...]
  

1.2 malloc() возвращает NULL при сбое, поэтому проверьте результат и не разыменовывайте его:

 [...]
myDeck->cards = (struct card**) malloc ( sizeof(struct card) * myDeck->numCards );
if (NULL == myDeck->cards) {
    printf("Failed to allocate 52 cards, exiting..n");
[...]
  

При ближайшем рассмотрении становится понятно, что вы, очевидно, не уверены, как упорядочить свои данные … 😉

Эта строка

 myDeck->cards = (struct card**) malloc ( sizeof(struct card) * myDeck->numCards );
  

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

Итак, есть две ошибки:

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

2.2 Вам не хватает, чтобы назначить память для самих карт.

Чтобы исправить 2.1, измените строку выше на:

  myDeck->cards = (struct card**) malloc ( sizeof(struct card *) * myDeck->numCards );
  

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

   [...]
  for (i = 0; i<DECK_SIZE; i  ) {
    myDeck->cards[i] = malloc(sizeof(struct card));
    /* adding error checking here is left as an exercise ... */
    myDeck->cards[i]->number = num;
    [...]
  

Добавление этих двух исправлений продвинет вас дальше … 😉

Подсказка: те же две ошибки, которые вы допустили при распределении колоды (2.1 и 2.2), вы делаете в коде добавления карты ( addCard() ).

Кстати: приведение результата malloc() мне кажется ненужным, поскольку malloc() return void * совместим с любым указателем.

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

Комментарии:

1. большое вам спасибо за подробное объяснение! мне очень помогло!

Ответ №2:

Вы выделяете массив, содержащий указатели на карточки, но не сами карточки.

Ответ №3:

Этот вызов выделяет один блок myDeck->numCards карточек:

 malloc ( sizeof(struct card) * myDeck->numCards );
  

…но a struct card ** не является подходящей переменной для хранения указателя на такой блок. Вы должны использовать только a struct card * для этого элемента, а затем использовать . , а не -> для доступа к каждому элементу этого массива:

 myDeck->cards[i].number = num;
myDeck->cards[i].type = suite;
  

Правило, которое нужно использовать, заключается в том, что a type * указывает на (блок) type s . Таким образом, a struct card * указывает на (блок) struct card s, а a struct card ** указывает на (блок) struct card * s .

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

 myDeck->cards = malloc (sizeof(struct card *) * myDeck->numCards);
if (NULL == myDeck->cards) {
    fprintf(stderr, "Failed to allocate %d card pointers, exiting..n", myDeck->numCards);
  

Теперь вы можете распределять сами карты, помещая указатели на карты в массив указателей, выделенных ранее. Самый простой способ сделать это — одно выделение на карту:

 for (i = 0; i < myDeck->numCards; i  )
    myDeck->cards[i] = malloc(sizeof(struct card));
  

Затем ваше перераспределение должно выглядеть так:

 struct card **new_block;

myDeck->numCards  = 1;
/* reallocate the block of pointers and increase the size by one */
new_block = realloc (myDeck->cards, sizeof(struct card *) * myDeck->numCards);
if (NULL == new_block) {
    fprintf(stderr, "realloc failed - exiting..n");
    return;
}
myDeck->cards = new_block;
/* Allocate the new card */
myDeck->cards[myDeck->numCards - 1] = malloc(sizeof(struct card));
if (myDeck->cards[myDeck->numCards - 1] == NULL) {
    fprintf(stderr, "failed to allocate cardn");
    myDeck->numCards--;
    return;
}
/* put the data */
myDeck->cards[myDeck->numCards - 1]->number = number;
myDeck->cards[myDeck->numCards - 1]->type = suiteType;
  

Комментарии:

1. хорошо, но допустим, что я «вынужден» использовать ‘struct card **’ (следовательно, указатель на указатель). Вы бы реализовали это иначе, чем то, что я реализовал выше?

2. @shluvme: Да. Если вы используете a struct card ** , то у вас фактически есть массив указателей на карточки, а не массив карточек. Это означает, что вы должны выделить блок указателей и выделить сами карточки — я обновил свой ответ, чтобы показать это.

3. большое вам спасибо! действительно, это то, что я в итоге сделал, и оно выполнило свою работу 🙂