Hádēs
Hádēs

Reputation: 160

How to dynamically allocate memory without affecting other data allocated dynamically?

Im implementing a card game using C, here's my code:

deck.h:

#ifndef DECK_H
    #define DECK_H
    #define S_NUM 4
    #define V_NUM 12
    #define S_MAXLEN 9
    #define V_MAXLEN 6
    #define OFLEN 5

    typedef char deck_t
        [S_NUM * V_NUM]
            [S_MAXLEN + V_MAXLEN + OFLEN];
    
    typedef struct {
        deck_t *hand;
        deck_t *remaining_cards;
    } dealed_deck_t;
    
    deck_t *new_deck(void);
    void print_deck(const deck_t *);
    dealed_deck_t deal(const deck_t *, int);
    void free_deck(const deck_t *);
    
    static void deckcpy(const deck_t *const, deck_t *, int);
    
#endif

deck.c:

#include "deck.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

deck_t *new_deck() {
    int i, j, k;
    deck_t *cards = calloc(1, sizeof(deck_t));
    char suits[S_NUM][S_MAXLEN] = {
        "Spades", "Diamonds",
        "Hearts", "Clubs"
    };
    char values[V_NUM][V_MAXLEN] = {
        "Ace", "Two", "Three",
        "Four", "Five", "Six",
        "Seven", "Eight", "Nine",
        "Jack", "Queen", "King"
    };
    for (i = 0; i < S_NUM * V_NUM; ++i) {
        memset(*cards[i], 0, strlen(*cards[i]));
    }
    for (i = 0, j = 0; i < S_NUM; ++i) {
        for (k = 0; k < V_NUM; ++k, ++j) {
            strcat(*cards[j], values[k]);
            strcat(*cards[j], " of ");
            strcat(*cards[j], suits[i]);
        }
    }
    return cards;
}

void print_deck(const deck_t *deck) {
    for (int i = 0; i < S_NUM * V_NUM && strlen(*deck[i]); ++i) {
        printf("%s\n", *deck[i]);
    }
}

dealed_deck_t deal(const deck_t *deck, int handsize) {
    int i, j;
    dealed_deck_t dealed_deck;
    dealed_deck.hand = calloc(1, sizeof(deck_t));
    dealed_deck.remaining_cards = calloc(1, sizeof(deck_t));
    
    // print_deck(deck);
    
    return dealed_deck;
}

void free_deck(const deck_t *deck) {
    free((void *)deck);
}

static void deckcpy(const deck_t *const deck, deck_t *dest, int handsize) {
    for (int i = 0; i < handsize; ++i) {
        char temp[strlen(*deck[i])];
        for (int j = 0; j < strlen(*deck[i]); ++j) {
            temp[j] = (*deck[i])[j];
            temp[j] = '\0';
        }
        for (int j = 0; j < strlen(temp); ++j) {
            (*dest[i])[j] = temp[j];
            (*dest[i])[j] = '\0';
        }
    }
}

main.c:

#include "deck.h"

int main() {
    deck_t *cards = new_deck();
    deck_t *hand = deal(cards, 6).hand;
    // print_deck(cards);
    free_deck(cards);
    return 0;
}

the problem is deck.c in the deal function. when I allocate memory for dealed_deck.hand and dealed_deck.remaining_cards the data pointed to by the parameter deck is affected and changed, so when I comment the two lines: dealed_deck.hand = calloc(1, sizeof(deck_t)); and dealed_deck.remaining_cards = calloc(1, sizeof(deck_t));, the data is the same, when I uncomment the data change a bit.

I'm using calloc here because when I used malloc the data pointed to by dealed_deck.hand and dealed_deck.remaining_cards was the same as the data pointed to by deck, but when I used calloc they were empty as I want.

I need a way to allocate memory without messing the memory and data I allocated before, how to do that?

I'm compiling using a MacOS and gcc. thanks.

Upvotes: 0

Views: 63

Answers (1)

Eric Postpischil
Eric Postpischil

Reputation: 224311

The line memset(*cards[i], 0, strlen(*cards[i])); is wrong. First, cards was allocated with calloc, so it is filled with zero bytes, which act as empty strings. So, if *cards[i] pointed to something in cards, strlen returns zero, and memset sets zero bytes to zero.

Unfortunately, *cards[i] is valid only when i is zero. Since cards is a pointer to a deck_t, cards[0] is the first deck_t, which is an array of array of char. As an array, it is automatically converted to a pointer to its first, element, an array of char. Then *cards[0] is that array of char. As an array, it is automatically converted to a pointer to its first element, which is passed to strlen. However, when i is one (or greater), cards[i] would be the second (or greater) deck_t. But space for only one deck_t was allocated. So *cards[i] is invalid; it attempts to access space that was not allocated.

Since this line is not needed (due to calloc), remove it and the loop it is in.

In strcat(*cards[j], the same problem exists—*cards[j] is valid only when j is zero. A correct expression is (*cards)[j].

In (*cards)[j], cards is a pointer to a deck_t, so (*cards) is a deck_t, which is an array of array of char. Then (*cards)[j] is element j of that array, so it is an array of char.

Similarly, in print_deck, change *deck[i] to (*deck)[i].

(The code can be changed so that deck[i] can be used instead of (*deck)[i] by changing the type of deck to char (*MyType)[S_MAXLEN + V_MAXLEN + OFLEN]. However, you may wish to make the above changes first and understand them before changing types.)

Upvotes: 2

Related Questions