Home > Software engineering >  Initialize the arrays inside an array of char* using a loop
Initialize the arrays inside an array of char* using a loop

Time:10-20

I need to create an array of strings, each representing a card of the Spanish deck:

#define __USE_MINGW_ANSI_STDIO 1
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    char *type[4]= {"copas", "basto", "espada", "oro"};
    char *number[10]= {"Uno", "Dos", "Tres", "Cuatro", "Cinco", "Seis", "Siete", "Diez", "Once", "Doce"};
    char *deck[40];
    int deckIndex= 0;
    for (int i = 0; i < 4; i  )
    {
        for (int j = 0; j < 10; j  ) {
          char card[100] = "";
          strcat(card, number[j]);
          strcat(card, " de ");
          strcat(card, type[i]);
          strcat(card, "\n");
          deck[deckIndex]= card;
          deckIndex  ;
        }
    }
    for (int i = 0; i < 40; i  )
    {
        printf("%s\n", deck[i]);
    }
    return 0;
}

However, all entries of deck[] point to the same string. As a result, "Doce de oro" is printed 40 times. I don't understand why this happens, but I've theorized it's because card[] is being reinitialized in the same memory direction, and overrides what was already written there in the previous iteration. If I'm right, I would have to declare every array separately, but I have no idea how to do that without writing 40 different arrays.

Tldr: ¿Why do all entries of deck[] point to the same location? ¿How do I fix it?

(Btw suggestions for a better title are appreciated)

CodePudding user response:

In C, memory on the stack is allocated in terms of Scopes. So yes, your theory is right. You are rewriting on the same location.

To fix your program, there are two possible solutions I can think of.

  • You can use Multidimensional Arrays.
  • Or you can allocate memory in heap using malloc (but make sure to free it once you are done with it)

CodePudding user response:

As pointed out in the comments, in the deck[deckIndex]= card; line, you are assigning the same pointer1 to each of your deck elements – and, worse, a pointer to a variable (the card array) that is no longer valid when the initial nested for loop goes out of scope.

To fix this, you can make copies of the card string, using the strdup function, and assign the addresses of those copies to the deck elements. Further, as also mentioned in the comments, you can simplify the construction of the card string using a single call to sprintf, rather than using multiple strcat calls.

Here's how you might do that:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    char* type[4] = { "copas", "basto", "espada", "oro" };
    char* number[10] = { "Uno", "Dos", "Tres", "Cuatro", "Cinco", "Seis", "Siete", "Diez", "Once", "Doce" };
    char* deck[40];
    int deckIndex = 0;
    for (int i = 0; i < 4; i  ) {
        for (int j = 0; j < 10; j  ) {
            char card[100] = "";
            sprintf(card, "%s de %s", number[j], type[i]);
            deck[deckIndex] = strdup(card);
            deckIndex  ;
        }
    }
    for (int i = 0; i < 40; i  ) {
        printf("%s\n", deck[i]);
    }

    // When you're done, be sure to free the allocated memory:
    for (int i = 0; i < 40; i  ) {
        free(deck[i]);
    }
    return 0;
}

If your compiler does not support the strdup function (most do, and it is part of the ISO C Standard from C23), writing your own is very simple:

char* strdup(const char *src)
{
    char* result = malloc(strlen(src)   1); // Add one to make room for the nul terminator
    if (result) strcpy(result, src);
    return result;
}

1 Well, formally, a new card array is born on each iteration of the inner for loop, but it would be a very inefficient compiler that chose to do that, rather than simply re-using the same memory – which is clearly what is happening in your case.

  •  Tags:  
  • c
  • Related