0

For my project I had to save my data in a dynamic data structure (dynamic array, dynamic list etc..) which size and capacity would be set during execution time with malloc() as I suppose. I completely forgot about it and I was working with that data without allocating any memory.

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

typedef struct Bet {
    char* bets[3][2];
} Bet;

int main() {
     Bet betTypes = { .bets={{"Red", "Black"}, {"Even", "Odd"}, {"1 to 18", "19 to 36"}}};
}

This is what I am working with. I am confused how should I do this. Do I do

typedef struct Bet {
    int x;
    int y;
    char* bets[x][y];
} Bet;

Then in main I create that Bet betTypes; do betTypes.x = 3; and betTypes.y = 2 and call malloc() using those x and y? But then how do I create that exact same list of strings, because I didn't find any another ways aside Bet betTypes = { .bets={{"Red", "Black"}, {"Even", "Odd"}, {"1 to 18", "19 to 36"}}};, if I declared Bet betTypes; before that, then the Bet betTypes = { .bets={{"Red", "Black"}, {"Even", "Odd"}, {"1 to 18", "19 to 36"}}}; part would not work. Or I suppose I am just too unexperienced with memory allocation and I don't know how this should look like (neither I don't know how to allocate memory for this array)

Aldomond
  • 171
  • 6
  • 1
    Structs must have fixed sizes . You will need to read up on allocating arrays via `malloc` in order to haven runtime-sized arrays – M.M Jan 26 '21 at 20:39
  • note that it will be simpler to allocate one array of length `x*y` rather than try to have nested dynamic layers – M.M Jan 26 '21 at 20:40
  • @M.M Easier to allocate/free, but harder to access because you have to do the index calculation yourself. – Barmar Jan 26 '21 at 20:41
  • 1
    If you're using a hard-coded initialization list, why do you need the array sizes to be dynamic? – Barmar Jan 26 '21 at 20:43
  • @Barmar doing a multiplication is a whole lot easier (and faster) than allocating a pile of different memory chunks – M.M Jan 26 '21 at 20:44
  • I wouldn't bother doing all of this memory allocation, but at the end I must make a presentation and one of the points says "Show, how you work with your dynamic memory" Haha – Aldomond Jan 26 '21 at 20:47
  • @M.M `array[x][y]` is easier to read and write than `array[y*rowsize+x]` – Barmar Jan 26 '21 at 20:48
  • @Barmar `array[y*max_x+x]` – Jay-Pi Jan 26 '21 at 20:51
  • @Jay-Pi Isn't that the same as what I just wrote? It's not nearly as obvious that that's a 2-dimensional array access. – Barmar Jan 26 '21 at 20:53
  • @Norbertas You need a 3D array and either use static maximum lengths for each word or you need a nested pointer structure (this would mean 2D array of pointers `char*`). What do you want? – Jay-Pi Jan 26 '21 at 20:54
  • 1
    @Jay-Pi Well I guess static maximum lengths sound easier – Aldomond Jan 26 '21 at 20:57
  • Oh alright then, I will do that, thanks – Aldomond Jan 26 '21 at 21:00
  • I would argue a more object-oriented approach would be to have a `Bet` that is two strings. Then you could have an array-of-Bets, a list-of-Bets, or some other structure. – Neil Jan 26 '21 at 21:12
  • @M.M *note that it will be simpler to allocate one array of length x*y rather than try to have nested dynamic layers* Use a VLA - only one allocation needed. And you can do a double-pointer "2-dimensional array" pretty easily with only two memory allocation calls. You can even do such an "array" with only one call, but the code to calculate the size of the allocation and then to fill in all the required pointers is a pain and usually not worth the trouble. – Andrew Henle Jan 26 '21 at 23:21
  • @AndrewHenle VLAs cannot be struct members – M.M Jan 26 '21 at 23:22
  • @M.M They can be saved as a `void *`. Or if you want to write more complex code again, they can be a flexible array member at the end of the `struct` that gets reinterpreted as a VLA to dereference it as a 2D array. But that's probably not worth the trouble. – Andrew Henle Jan 26 '21 at 23:24
  • @AndrewHenle I don't think dynamically allocated space can be described as "a VLA" even if you choose to access it via VLA-type pointers . – M.M Jan 26 '21 at 23:27
  • @M.M [Sure it can](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). `malloc()` *et al* return memory that "is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement". The syntax is more than a bit obtuse, but it works well once you understand it. Heck, the syntax is easier to understand IMO than function pointers... – Andrew Henle Jan 26 '21 at 23:50
  • @AndrewHenle I'm saying I don't think "a VLA" is correct terminology for this technique . Space allocated by malloc has no type initially, and can never have *effective type* of any array type (VLA or not) since it is not possible to write through an lvalue of array type – M.M Jan 27 '21 at 00:02

2 Answers2

1
typedef struct
{
    size_t x,y;
    int data[];
}mydata_t;


mydata_t *allocate(size_t x, size_t y)
{
    mydata_t *mydata = malloc(sizeof(*mydata) + x * y * sizeof(mydata -> data[0]));

    if(mydata) 
    {
        mydata -> x = x;
        mydata -> y = y;
    }
    return mydata;
}

int getval(mydata_t *sptr, size_t x, size_t y)
{
    return sptr -> data[x + sptr -> x * y];
}
0___________
  • 60,014
  • 4
  • 34
  • 74
  • Names that end in `_t` are [reserved by POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html). And on systems with GNU `libc`, [by GNU](http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html). – Andrew Henle Jan 26 '21 at 22:55
1

You can use a VLA on C99-compliant compilers (or on C11 and later compilers that implement them):

#include <stdint.h>
#include <stdlib.h>

struct Bet
{
    uint32_t x;
    uint32_t y;
    void *bets;
};

struct Bet *allocateBet( uint32_t x, uint32_t y )
{
    struct Bet *b = malloc( sizeof( *b ) );
    if ( NULL == b )
    {
        return( NULL );
    }

    b->x = x;
    b->y = y;

    char *( *array )[x][y] = calloc( 1, sizeof( *array ) );

    b->bets = array;

    return( b );
}

And to easily get and set the values:

char *getBet( struct Bet *b, uint32_t x, uint32_t y )
{
    char *( *array )[b->x][b->y] = b->bets;
    return( ( *array )[x][y] );
}

void setBet( struct Bet *b, uint32_t x, uint32_t y, char *bet )
{
    char *( *array )[b->x][b->y] = b->bets;

    // might want to use strdup() here:
    // if ( ( *array )[x][y] )
    // {
    //     free( ( *array )[x][y] );
    // }
    // ( *array )[x][y] = strdup( bet );

    ( *array )[x][y] = bet;
}

To free the structure:

void freeBet( struct Bet *b )
{
    // if strdup() is used in setBet:
    // char *( *array )[b->x][b->y] = b->bets;
    // for ( uint32_t ii = 0; ii < b->x; ii++ )
    // {
    //     for ( uint32_t jj = 0; jj < b->y; jj++ )
    //     {
    //         free( ( *array )[ii][jj] );
    //     }
    // }

    free( bet->bets );
    free( bet );
}
Andrew Henle
  • 32,625
  • 3
  • 24
  • 56