1

For finite field math I store the corresponding addition and multiplication tables as statically typed integer arrays, e.g., for GF(4/8), I have

static const uint8_t GF4ADD[4][4] = {...};

static const uint8_t GF8ADD[8][8] = {...};

Now, at runtime, the program reads from a config file which field size is desired and should assign to a struct pointer the corresponding table:

struct obj data {
...
  uint8_t** table_add;
  uint8_t** table_mult;
...
};

switch(order) {
case 4:
  data.table_add = GF4ADD;
  data.table_mult = GF4MULT;
  break;
case 8:
  data.table_add = GF8ADD;
  data.table_mult = GF8MULT;
  break;
}

Of course, the above doesn't work, but it should give you the idea of what I am trying to accomplish. The main problem is that I don't know of which type I should declare the struct members, as the size of the tables is only known at runtime. Besides, I do not want to resort to a one-dimensional indexing of the tables only.

Thanks, Tom.

tomseidel1
  • 85
  • 8
  • Sounds like you need to study *dynamic allocation*. – klutt Nov 20 '17 at 12:47
  • If you want to have either `uint8_t *[4] table_add;` and `uint8_t *[8] table_add;`, would putting them in a union type work? – Pawel Batko Nov 20 '17 at 12:57
  • Well, yes, sure. But the struct (and therefore its members) are declared in a header and instantiated only afterwards. So the question is, how do I have to declare it in the header, as the size is not known at type of declaration. – tomseidel1 Nov 20 '17 at 12:58
  • The C language is problematic here, since it does not allow you to declare variable length arrays (VLA) at file scope. So if you need a struct with variable-length arrays to be accessible from multiple files, you will have to do some manner of work-around. The most common way would be to "mangle" the data into a single dimension and calculate the index is run-time. That's an acceptable solution, if not "pretty". (Efficient C code is rarely pretty.) – Lundin Nov 20 '17 at 14:31

1 Answers1

0

You need to declare it as you did, with pointers. After that, when you will get the order, you will be able to allocate dynamically the memory you will need.


Simple example 1 (using pointer to pointer):

struct{
    int ** table_add;
}data;


int main()
{
    unsigned int order;
    scanf("%u", &order);
    // create "order" pointers 
    data.table_add = malloc(order * sizeof(int *));
    // where each pointer points to "order" elements
    for(unsigned int i = 0; i < order; i++)
        data.table_add[i] = malloc(order * sizeof(int));

    // fill with numbers
    int counter = 0;
    for(unsigned int i = 0; i < order; i++)
        for(unsigned int j = 0; j < order; j++)
            data.table_add[i][j] = counter++;

    // read result
    for(unsigned int i = 0; i < order; i++){
        for(unsigned int j = 0; j < order; j++)
            printf("%d ",data.table_add[i][j]);
        printf("\n");
    }
    return 0;
}

Simple example 2 (allocating 2D array):

replace the start of the 1st example with:

    ...
    unsigned int order;
    scanf("%u", &order);
    // allocate memory for a 2D array
    data.table_add = malloc(sizeof(int[order][order]);
    // fill with numbers 
    ...

As @Lundin mentioned, this way is more efficient. The reasons for this are 3 as I see it:

  1. Size - no need to allocate memory for pointers (saves order * sizeof(int*) )
  2. Time - one call to malloc instead of order + 1
  3. Simpler code (less mallocs --> less frees --> less errors)

In your case, you will need to fill the elements in a loop (looping over each element and assigning them, or looping over order elements and using memcpy)

CIsForCookies
  • 12,097
  • 11
  • 59
  • 124
  • Please note that this is not a 2D array and also very inefficient code, see [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). – Lundin Nov 20 '17 at 13:52
  • Of course. But I guess this is what OP had in mind. I'll add the option to malloc 2D array, but I think this the basic simple answer (and not surprisingly, inefficient). – CIsForCookies Nov 20 '17 at 14:00
  • 1
    4. Data cache friendly (the main performance reason) 5. Heap fragmentation-friendly. – Lundin Nov 20 '17 at 14:36
  • 1
    Please note however that you cannot use a pointer-to-pointer to point at a 2D array. `data.table_add = malloc(sizeof(int[order][order]);` will not work unless `table_add` is of type `int(*)[order]` or `int(*)[order][order]`. – Lundin Nov 20 '17 at 14:37
  • Thank you for all your comments. This was very insightful as I wasnt aware of this and it seems that is not that straightforward. Assuming I forget about my restriction with using the previously declared struct, but simply want to create a 2D array with memory that is consecutively allocated. In this case I can define int(*a)[order]; a = malloc(sizeof(int[order][order])) However, VLA is then allocated on the stack, isn't it? As it is usually the case with any array. Assuming that order is really large, how would I do it then? – tomseidel1 Nov 20 '17 at 17:31