3

I'm trying to get more comfortable with pointers and C in general so I've been doing practice problem. I have a struct:

typedef struct Card
{
    enum { hearts, spades, clubs, diamonds } suit;
    int value;
} Card;

and a function used to allocate memory for the deck:

void createDeck(Card ** deck)
{
    deck = malloc(52 * sizeof(Card *)); //allocate space for deck
    if (deck == NULL)
    {
        fprintf(stderr, "malloc failed\n");
        return;
    }

        //allocate memory for each card in deck
    for (size_t i = 0; i < 52; i++)
    {
        *(deck + i) = malloc(sizeof(Card));
    }
}

and I'm trying to use the code like this:

int main()
{
    Card *deck = NULL;

    createDeck(&deck);

    printf("%d", deck[0].suit)
}

This gives a nullptr error which makes me think I'm not allocating memory correctly. I've changed different things but I can't get this to work regardless. How do I access the members of deck after I've done work to it with createDeck?

Carl
  • 90
  • 1
  • 11
  • 1
    everything in C is pass-by-value. A copy of `deck` is made when you call `createDeck`, therefore any modification made to `deck` is persistent only within the function to that copy. In order to see modifications to `deck` outside the function, you must return it or (as in this case, since you've passed a pointer), dereference the pointer and modify the object it points to (which has scope outside of the function). – yano Apr 09 '19 at 04:31

2 Answers2

4

Let me breakdown your problem.

In main(), Card *deck = NULL; will allocate a pointer to Card and the pointer is pointing to NULL.

Then you call createDeck(&deck); Here you are passing the address of the pointer to type Card. So, this address is not NULL as it is pointing to pointer variable which is allocated on stack of type Card*.

It means in createDeck(), the variable deck will not be NULL and it will be having valid address. But *deck which will be pointing to a pointer of type Card* will be NULL. You should allocate memory only for *deck.

void createDeck(Card ** deck)
{
    //deck = malloc(52 * sizeof(Card *)); //deck is a double pointer pointing to memory of type Card* allocated on stack in calling function main().
    if (deck == NULL)
    {
        fprintf(stderr, "Null pointer error\n"); //If calling function passes NULL, it means there is some issue.
        return;
    }
    *deck = NULL; //In more complex functions, if there are other logic before doing malloc(), it it better to reset the variables with default values.

        //allocate memory for each card in deck
    //for (size_t i = 0; i < 52; i++)
    //{
    //    *(deck + i) = malloc(sizeof(Card));
    //}

    *deck = malloc(sizeof(Card)*52); //Allocate for all 52 Cards together
    if(NULL == *deck)
    {
        fprintf(stderr, "malloc failed\n");
        return;
    }

    //In case you want to access a card using deck variable.
    (*deck)[2].suit = hearts;
}
MayurK
  • 1,925
  • 14
  • 27
  • If I want to allocate the deck of 52 cards, I only need to use the one malloc()? – Carl Apr 09 '19 at 04:36
  • @Dumbassahedratron: Yes, this is *conceptually* equivalent to a `Card[52]` buffer, but allocated on the heap. – Mark Benningfield Apr 09 '19 at 04:39
  • Yep! You're allocating space for 52 structs. The logic is the same as for allocating 10 elements for an integer array(`int *arr = malloc(sizeof(int) * 10)`) if that analogy helps. – Pratik Sampat Apr 09 '19 at 04:40
  • 2
    Is `if(NULL != *deck)` correct? If I'm understanding correctly, we're doing a `malloc()` which will assign a block of memory for us, meaning it's not going to be NULL which fits the condition and will always fire that `fprintf` and `return`? – Carl Apr 09 '19 at 04:43
  • @Dumbassahedratron My mistake. I corrected it. :) – MayurK Apr 09 '19 at 04:47
0

Life will always be more simpler without double pointers, As far as I can see you need an arry of cards ( 52 ) ??

why not this ?

Card* createDeck( )
{
    Card *deck = malloc( 52 * sizeof(Card) );
    {
         printf("OOPS");
    }

    return deck;
}

and the caller

Card *deck = createDeck();
if( !deck )
{
   printf("Alloc Failure");
   exit(0);
}

Or better get rid of the createDeck and call malloc directly

Card *deck = malloc( 52 *  sizeof(Card));

Its a completely different proposition if you want to seethings differently for instance a deck is a collection of cards

/* That's how a card looks like */
struct Card{
    int value;
    /* waharever else */
};

/* That's a deck */
struct deck {
   struct Card *cards;
};

Now Initialize a Deck

struct deck *createDeck( )
{
     struct deck *deck = malloc( struct deck );
     if( deck )
     {
         deck->cards = malloc( 52 * sizeof(card));
         if(deck->cards)
         {
            /* Do some thing or leavel it as is */
         }
     }

     return deck;
}

and the caller ..

struct *deck = createDeck();
if( !deck )
{
    printf("OOps memory");
    exit(0);
}
asio_guy
  • 3,667
  • 2
  • 19
  • 35