2

I'm a novice in C and I need a structure to pass constant two-dimensional arrays to function as one parameter. I want to make this

const int a_size_x = 20;
const int a_size_y = 30;
const int a_output_array[size_x][size_y] = {{..., ...}, ..., {..., ...}};

const int b_size_x = 20;
const int b_size_y = 30;
const int b_output_array[size_x][size_y] = {{..., ...}, ..., {..., ...}};

void function(const int array[], int arr_size_x, int arr_size_y){
    for (int i = 0; i < arr_size_x; i++) 
    {
        for (int j = 0; j < arr_size_y; j++)
        {
            printf("%i ", array[i][j];
        }
        printf("\n");
    }

function(a_output_array, a_size_x, a_size_y);
function(b_output_array, b_size_x, b_size_y);

easier to be able to call function(a) like this:

const struct OUTPUT
{
    const int size_x;
    const int size_y;
    const int array[size_x][size_y];
};

struct OUTPUT a = {.size_x = 20, .size_y = 30, .array = {{...}, ..., {...}};
....
struct OUTPUT z = {.size_x = 30, .size_y = 20, .array = {{...}, ..., {...}};


function(const struct OUTPUT out){
    for (int i = 0; i < out.size_x; i++) 
    {
        for (int j = 0; j < out.size_y; j++)
        {
            printf("%i ", out.array[i][j];
        }
        printf("\n");
    }

function(a);
function(b);

but of course compiler says that size_x and size_y is undeclared in struct declaration.
I've read about flexible array members, but there's dynamic memory allocation needed, and in AVR Harvard architecture malloc can't work in program memory, where i put all this data.

Is there some way to do it in C? May be, in C++?

UPDATE Answer that worked for me - create a one-dimensional array of lenght 2 + width*height where first two members are true width and height and use a pointer to work with this. Here's an example function to print out this array:

char arr [11] = 
{
   3 // width
   3 // height
   1, 2, 3,
   4, 5, 6,
   7, 8, 9
}

void print_array(char *ptr)
{
   char width = *ptr++;
   char height= *ptr++;
   for (int i = 0; i < height; i++)
   {
      for (int j = 0; j < width; j++)
      {
         print("%c\t", *ptr++);
      }
      print("\n");
   }
}

print_array(arr);
  • This `void function(const int array[], ...` defines array as a pointer to `int`. So this `printf("%i ", array[i][j];` will not compile. Passing a 2D array to it invokes undefined behaviour, to not say is plain wrong. – alk Apr 03 '18 at 14:50
  • What is `const struct OUTPUT`? You need a list of one or more tags – lost_in_the_source Apr 03 '18 at 15:11
  • Yes, there must be some mistakes in snippets. Actually when i wrote my function i used `(int *array, int size_x, int size_y)` parameters. But that's not a question. My probllem is to get a structure, that makes easier defining of many objects with varying dimensions in it and function that needs only one parameter(this structure) to process these arrays, because with many of these 'A_array', 'B_array' and then calling it many times in functions with all three parameters makes a complete mess of program. – Paul the Hedgehog Apr 04 '18 at 08:22

3 Answers3

2

When declaring an array with an initializer, the bounds of the array must be constants. A variable with a const qualifier does not qualify as a constant. You can however use a macro which does a text substitution:

#define A_SIZE_X 2
#define A_SIZE_Y 3
const int a_output_array[A_SIZE_X][A_SIZE_Y] = {{3,4,5},{6,7,8}};

#define B_SIZE_X 2
#define B_SIZE_Y 3
const int b_output_array[B_SIZE_X][B_SIZE_Y] = {{1,2,3},{4,5,6}};

When passing a 2D array to a function, the definition must say that it expects a 2D array. Your is expecting const int array[] which is a 1D array.

You can have a function accept arrays with different bounds if the bounds are specified first in the definition:

void function(int arr_size_x, int arr_size_y, const int array[arr_size_x][arr_size_y]) {

You can then call it like this:

function(A_SIZE_X, A_SIZE_Y, a_output_array);
function(B_SIZE_X, B_SIZE_Y, b_output_array);
dbush
  • 205,898
  • 23
  • 218
  • 273
2

For most compilers, 2D arrays can be refered to as 1D as such: matrix[3][3]=[1,2,3 4,5,6 7,8,9] Index in 1D is calculated by row size*row number. For example: matrix[5]=6.

This means you can pass only 1 parameter, the row length, and by calculating the length of the whole vector you can deduce the 2nd parameter (number of rows).

You can add the row length parameter to the end of your array, and by so passing the array only, if that helps.

Prostagma
  • 1,756
  • 9
  • 21
  • 3
    "*In C, 2D arrays can be refered to as 1D*" That's not correct, it's [a grey area at best](https://stackoverflow.com/questions/6290956/one-dimensional-access-to-a-multidimensional-array-is-it-well-defined-behaviour). –  Apr 03 '18 at 14:59
  • edited, thanks. Isn't it true for most compilers at least? – Prostagma Apr 03 '18 at 15:06
  • It's one of the things where you will probably have a hard time to find a real-world implementation of C **not** doing what you expect, still it's not correct according to the language standard :) –  Apr 03 '18 at 15:07
  • Of course, this means you might pick that approach for your embedded project, being sure your compiler does what you expect. I always prefer to find a compliant solution though, just to be sure. In this case, I'd probably opt to directly use flat arrays. –  Apr 03 '18 at 15:09
  • 1
    Note that `matrix[5]` in the first paragraph is a row of the array, except that it is out of bounds of the array. `matrix[2]` would be a pointer to the row containing 7, 8, 9 — it would become a pointer to the element holding 7 in many contexts. Under no circumstance does `matrix[5]` refer to the element with value 6. Your initializer would elicit warnings from compilers set fussy — they'd prefer `int matrix[3][3] = { { 1, 2, 3, }, { 4, 5, 6, }, { 7, 8, 9, }, };` (where up to four of the trailing commas could be omitted if you prefer). – Jonathan Leffler Apr 03 '18 at 16:35
  • That's idea! I'll pass a pointer to ... something like `size_x = *pointer++` points to first dimension, `size_y = *pointer++` to second one and then in two `for` cycles `printf(*pointer++)` iterates other members. If then a legal way to make it? May i be sure if write `int name = 20, name_y = 30; int name_array[20][30] = {{1, ... 20}, {21...40}, ...{581...600}};` it'll be in right sequence? Or declare it like `name[20*30 + 2] = {20, 30, 1, 2,...,600};`? – Paul the Hedgehog Apr 04 '18 at 09:09
  • Jonathan Leffler, `matrix[5]` won't do, and `*(matrix + 5)` don't, so is there some way to get straight N number of multi-dimensional array without creating pointer `int *ptr = &matrix; ptr += 5; int n_member = *ptr;`? – Paul the Hedgehog Apr 04 '18 at 09:22
  • @RadioJava -- "I meant it in a practical way": It is undefined behavior to access a 2d array as a 1d array. The Standard even gives an explicit example of this in [Annex J.2](http://port70.net/~nsz/c/c11/n1570.html#J.2): "An array subscript is out of range, even if an object is apparently accessible with the given subscript (as in the lvalue expression a[1][7] given the declaration int a[4][5]) (6.5.6)." – ad absurdum Sep 28 '18 at 13:04
1

Side note first, the first snippet has a wrong signature and your compiler should warn you:

void function(const int array[], int arr_size_x, int arr_size_y){

here, array is a pointer to int (in a function signature, an array automatically gets adjusted to a pointer), but for passing a 2d array, you would need a pointer to array of int. Did you test that snippet? I assume it doesn't do what you want.

With C99 and above (assuming the compiler supports VLA, variable length arrays), something like this would be correct:

void function( int arr_size_x, int arr_size_y, const int (*array)[arr_size_y]){

As for your idea with a struct, you could only do it when you keep the second dimension fixed. A C array is contiguous in memory, so to do the indexing correctly, the compiler must know all dimensions except for the first one at compile time. VLAs are an exception to that rule, but you can't declare a VLA statically.

What you can do however is using a flat array and do the 2d indexing yourself, like in this tiny example:

struct outputdata
{
    size_t rows;
    size_t cols;
    int *data;
};

const int a_data[] = {1, 2, 3, 4, 5, 6};
const struct outputdata a = {
    .rows = 2,
    .cols = 3,
    .data = a_data
};

// [...]

void function(const struct outputdata x)
{
    for (size_t r = 0; r < x.rows; ++r)
    {
        for (size_t c = 0; c < x.cols; ++c)
        {
            printf("%d ", x.data[r*x.cols + c]);
        }
    }
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278