1

I have the following 3 structs for a card game program

// Linked list of cards, used for draw & discard pile, players' hands
typedef struct cardStack {
    struct cardStack *next;
    Card *card;
} CardStack;

// A player and their hand
typedef struct player {
    int playerNumber;
    CardStack *hand;
} Player;

typedef struct _game {
    CardStack *discardPile;
    CardStack *drawPile;    
    Player *players[4];
    int currentPlayer;
    int currentTurn;
} *Game;

and this function to initialise the game struct

Game newGame(int deckSize, value values[], color colors[], suit suits[]) {

    Game game = (Game)malloc(sizeof(Game));

    game->players[0] = (Player*)malloc(sizeof(Player));
    game->players[0] = &(Player){0, NULL};
    game->players[1] = (Player*)malloc(sizeof(Player));
    game->players[1] = &(Player){1, NULL};
    game->players[2] = (Player*)malloc(sizeof(Player));
    game->players[2] = &(Player){2, NULL};
    game->players[3] = (Player*)malloc(sizeof(Player));
    game->players[3] = &(Player){3, NULL};

    for (int i = 1; i <= 7; i++) {
        for (int j = 1; i <= NUM_PLAYERS; i++) {
            Card card = newCard(values[i * j - 1], colors[i * j - 1], suits[i * j - 1]);
            addToStack(card, game->players[j-1]->hand);
        }
    }

    CardStack *discardPile = (CardStack*)malloc(sizeof(CardStack));
    Card firstDiscard = newCard(values[28], colors[28], suits[28]);
    addToStack(firstDiscard, discardPile);
    game->discardPile = discardPile;

    CardStack *drawPile = (CardStack*)malloc(sizeof(CardStack));
    for (int i = 29; i < deckSize; i++) {
        Card card = newCard(values[i], colors[i], suits[i]);
        addToStack(card, drawPile);
    }
    game->drawPile = drawPile;

    game->currentPlayer = 0;
    game->currentTurn = 1;

    return game;
}

It compiles fine, but when I try to run it, this line

game->players[0] = (Player*)malloc(sizeof(Player));

and similar, give an error "illegal array, pointer or other operation" I'm not sure whats wrong as I am just setting one pointer (in the array of pointers in the struct) to another

EDIT: unfortunately this is an assignment where the header file was given so I have no choice but to use the pointer typedef

5
  • You are getting confused with malloc when you use *Game If Game is really a pointer than sizeof(Game) isn't correct allocation! Commented Oct 10, 2018 at 2:03
  • 2
    ok... this time I bet I have it... Game is a pointer. So, this line doesn't allocate enough space: Game game = (Game)malloc(sizeof(Game)); Commented Oct 10, 2018 at 2:03
  • 1
    Probably worth noting that the line game->players[0] = &(Player){0, NULL}; not only causes a memory leak, but also takes the address of an object allocated on the stack. That object will cease to exist when the function returns. §6.5.2.5/5 Commented Oct 10, 2018 at 2:26
  • Note that you should not create function, variable, tag or macro names that start with an underscore, in general. C11 §7.1.3 Reserved identifiers says (in part): — All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces. See also What does double underscore (__const) mean in C? Commented Oct 10, 2018 at 5:50
  • 1
    Bad luck on being given a header to work with that illustrates several ways in which you should not write C code. Presumably, you were also given a structure for the type Card Commented Oct 10, 2018 at 6:00

3 Answers 3

3
typedef struct _game {
    ...
} *Game;

Game is defined as an alias for struct _game *, a pointer.

Game game = (Game)malloc(sizeof(Game));

That means that sizeof(Game) is the size of a pointer and not the entire struct. A pointer is smaller than the entire struct, so it's not enough memory. Writing to ->players accesses memory outside of the malloc'ed area which causes the illegal operation error.

A correct allocation would be:

Game game = malloc(sizeof *game);

Lesson learned: use p = malloc(sizeof *p) rather than p = malloc(sizeof(Type)) to avoid this kind of mistake. The compiler won't catch a size mismatch. sizeof *p will always be the right size, even if p changes type.

And if possible, get rid of the * in the definition of Game! It's quite out of place.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, that fixed it. Unfortunately I have to follow a given header file with that definition. I understand the malloc issue, but could you explain why the error was an illegal operation?
sizeof(Game) does not allocate enough memory. Writing to ->players accesses memory outside of the malloc'ed area.
2

Quite apart from the memory allocation issue identified by John Kugelman, you have at least one more major problem…

Another problem

Note that the lines like these two:

game->players[0] = (Player*)malloc(sizeof(Player));
game->players[0] = &(Player){0, NULL};

carefully leak the allocated memory. You replace the just allocated pointer with a pointer to the compound literal, which leaves you no way to free the allocated memory. It is perfectly legitimate to modify the compound literal as long as it doesn't go out of scope — but it does go out of scope when the function returns, so you not only leak memory but you also modify data you no longer own if you ever change the player information.

You probably want this instead, which copies the compound literal to the allocated memory:

game->players[0] = (Player*)malloc(sizeof(Player));
*game->players[0] = (Player){0, NULL};

I wouldn't be surprised to find there are other issues lurking, but the code is not an MCVE (Minimal, Complete, Verifiable Example) so it is hard to be sure.

Comments

0

Don't use pointers at the end of struct definitions it's ugly.

Change it to:

typedef struct _game {
   CardStack *discardPile;
   CardStack *drawPile;    
   Player *players[4];
   int currentPlayer;
   int currentTurn;
} Game;

And in your malloc replace that statement with the following:

Game* games = (Game*)malloc(sizeof(Game));

Bonn-appetite!

2 Comments

Curious — I don't think I've ever seen the advice "don't use pointers at the end of struct definitions; it's ugly" before. Would you care to elaborate on why it is ugly, and can you perhaps provide a URL where the reasoning is explained?
Oh — do you mean "Don't typedef pointers"? If so, then the linked question is the canonical reference on the topic. Note that when you say "don't use pointers at the end of struct definitions", people are likely to assume that you mean "don't use struct Whatever { …; SomeType *ptr; } because that is at the end of the structure definition. In the question, there is typedef struct _game { … } *Game; which is defining the name Game as a pointer to struct _game, but it's not part of the structure definition per se. English is weird sometimes!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.