1

I have two 2D array in C. Let's call them a[row][col] and b[row][col]. I generated random values in array a. Now i want to write a function that will count values, return 2D array and the output would be assigned to array b (it's a convoy game of life).

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

int row, col = 0;

//creates row boundary
int create_row_line()
{
    printf("\n");
    for (int i = 0; i < col; i++)
    {
        printf(" -----");
    }
    printf("\n");
}

//returns the count of alive neighbours
int count_alive_neighbours(int a[row][col], int r, int c)
{
    int i, j, count = 0;
    for (i = r - 1; i <= r + 1; i++)
    {
        for (j = c - 1; j <= c + 1; j++)
        {
            if ((i < 0 || j < 0) || (i >= row || j >= col) || (i == r && j == c))
            {
                continue;
            }
            if (a[i][j] == 1)
            {
                count++;
            }
        }
    }
    return count;
}

int print_state(int x[row][col]){
    create_row_line();
    for (int i = 0; i < row; i++)
    {
        printf(":");
        for (int j = 0; j < col; j++)
        {
            printf("  %d  :", x[i][j]);
        }
        create_row_line();
    }
}

int count_values(int x[row][col]){
    int y[row][col];
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            int neighbours_alive = count_alive_neighbours(x, i, j);
            // printf("%d" ,neighbours_alive);
            if ((x[i][j] == 0) && (neighbours_alive == 3))
            {
                y[i][j] = 1;
            }
            else if ((x[i][j] == 1) && (neighbours_alive == 3 || neighbours_alive == 2))
            {
                y[i][j] = 1;
            }
            else
            {
                y[i][j] = 0;
            }
        }
    }
    return y;
}

int main()
{
    //change row and column value to set the canvas size
    printf("Enter number of rows: ");
    scanf("%d", &row);
    printf("Enter number of cols: ");
    scanf("%d", &col);

    int a[row][col], b[row][col];
    int i, j, neighbours_alive;

    // generate matrix
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            a[i][j] = rand() % 2;
        }
    }

    // print initial state
    printf("Initial state:");
    print_state(a);

    char quit;
    do {
        printf("Press enter to print next generation. To quit, enter 'q'.");
        scanf("%c", &quit);
        int** b = count_values(a);
        print_state(b);
        int** a = b;
    } while(quit != 'q');

    return 0;
} 

But there's an error. I'm a poor C programmer and don't know what should be done to get desirable effect. After assigning the output from function to array b, I want to assign a=b and make a loop from this to make the next generations.

So the question is how to assign values from one 2D array in C to another and how to return 2D array from a function and assign the result to existing 2D array. Thank you for any help

dunlop
  • 61
  • 3
  • You wrote "*But there's an error.*". Please [edit] your question and copy&paste the error message. Assigning a whole array is not possible in the sense of copying all values. (Except if the array is a member of a structure.) – Bodo Dec 15 '21 at 14:43
  • A better approach is to keep two 2d arrays and just swap which one is the current one. – Fredrik Dec 15 '21 at 15:03
  • _"How to return 2D array in C with a function?"_.. One option is to pass pointer to array as function argument, along with its dimensions. The function can modify the contents of the array, and when its pointer is returned, the updated contents are accessible to the caller. The other option is of course to prototype the function to a type that will support returning a pointer to the array. The array object itself cannot be passed, _or_ returned using C, only a pointer the object. – ryyker Dec 15 '21 at 16:42

4 Answers4

1

Arrays are "non-modifiable l-values" thus the cannot be a left argument of the assignment operator. To copy the array just use memcpy(). For example:

int a[row][col], b[row][col];

memcpy(a, b, sizeof(int[row][col]));

Returning an array is a tricky topic, especially when variable-length arrays are used as in your case. It is discussed in thread.

You must return a pointer to the array either as void* or in a case of 2D arrays one can return a pointer to incomplete type of an array of undefined size int(*)[].

Moreover, the array must be allocated dynamically to extend its lifetime beyond the end of the allocation function. Remember to free the memory when it is no longer used.

void* count_values(int x[row][col]){
    int (*y)[col] = malloc(sizeof(int[row][col]));
    ...
    return y;
}

int main() {
  ...
  int (*b)[col] = count_values(a);
  ...
  free(b);
}
tstanisl
  • 13,520
  • 2
  • 25
  • 40
1

The errors you mention should include some non-fatal run-time errors, occurring because functions are defined to return a value, but have no return statement...

int create_row_line()
{
    printf("\n");
    for (int i = 0; i < col; i++)
    {
        printf(" -----");
    }
    printf("\n");
    
    return 0;//added
}  

Either make the function prototype void create_row_line(), or add a return statement at the bottom. Other than these observed behaviors, and without further explanation from you in the post, other errors were not evident, so I will generically address the title question...

"How to return 2D array in C with a function"

Method 1: This method will illustrate creating and returning a 2D array comprised of a single contiguous block of allocated memory from a function.

//prtotype;
void * arr_2d_alloc (size_t x, size_t y);//implemented
void * arr_fill_2 (size_t x, size_t y, int array[x][y]);//implemented
void arr_print_2 (size_t x, size_t y, int array[x][y]);//left to do

int main(void)
{
    int x=3, y=4;

    //Create 2D array, return from function
    int (*arr)[x] = arr_2d_alloc (x, y);
    if(arr)
    {    //pass 2D array in, return populated array out
         (*aptr_2)[y] = *(int *)arr_fill_2(x,y,aptr_2);//filling array
         //write routine output a 2D array, (see prototype above)
    }
    free(aptr_2); 
    
    return 0;
}

void * arr_2d_alloc (size_t x, size_t y)
{
    int (*aptr_2)[x] = malloc( sizeof **aptr_2 ); // allocate a true 3D array
    if(aptr_2)
    {
        memset(aptr_2, 0, sizeof **aptr_2);//zero array
    }
    return aptr_2;
}

void * arr_fill_2 (size_t x, size_t y, int array[x][y])
{
    for(size_t i=0; i<x; i++)
    {
        for(size_t j=0; j<y; j++)
        {
            array[i][j] = 1+i*j;//some generic content
        }
    }
    return array;
}
  

Method 2:
Another way is to pass VLA array as argument using VLA arrangement. This will allow changing the values in the function, then return the updated array. The following is a generic example to illustrate.

Note the position of the array sizes before the array itself:

void populate2D(int x, int y, int (*arr)[x][y]);

int main(void)
{
    int x=3, y=4;
    int arr[x][y];
    memset(arr, 0, sizeof arr);//initialize array
    populate2D(x, y, &arr);//passing array sizes, and pointer to array
    
    return 0;
}

void populate2D(int x, int y, int (*arr)[x][y])
{
    for(int i=0;i<x;i++)
    {
        for(int j = 0;j<y;j++)
        {
            (*arr)[i][j] = i*j;
        }
    }
}

If array dimensions are same for two arrays, such as in your example, a similar prototype can be created to accommodate your specific need:

void update_2D_arrays(int x, int y, int (*arr1)[x][y], int (*arr2)[x][y])
{
     // Do work using arr1 and arr2 
}
ryyker
  • 22,849
  • 3
  • 43
  • 87
1

Arrays objects cannot be assigned to, returned from functions, or passed as an argument of a function call. In the latter case, the function call argument is converted to a pointer to the first element, and function parameters declared as an array of type T are "adjusted" to pointer to type T (so void foo(int a[6]) is adjusted to void foo(int *a), for example).

Since main already has two arrays a and b you could pass both of them to count_values, changing count_values to accept two arrays as follows:

void count_values(const int x[row][col], int y[row][col])
{
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            /* put next generation in y[i][j] here */
        }
    }
}

And call it from main as follows:

        count_values(a, b);
        print_state(b);
        memcpy(b, a, sizeof(a));

(Note: memcpy is declared by #include <string.h>.)

Rather than copying a to b each time, you could have a 3-D array int a[2][row][col]; and flip the usage of a[0] and a[1] for each generation:

    int flip = 0;
    int a[2][row][col];
    int i, j, neighbours_alive;

    // generate matrix
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            a[flip][i][j] = rand() % 2;
        }
    }

    // print initial state
    printf("Initial state:");
    print_state(a[flip]);

    char quit;
    do {
        printf("Press enter to print next generation. To quit, enter 'q'.");
        scanf("%c", &quit);
        // a[flip] contains the current generation,
        // a[1-flip] will contain the next generation.
        b = count_values(a[flip], a[1-flip]);
        // flip the usage of a[0] and a[1]
        flip = 1 - flip;
        // a[1-flip] now contains the previous generation
        // a[flip] now contains the new current generation
        print_state(a[flip]);
    } while(quit != 'q');

A possible improvement is to pass the numbers of rows and columns as function arguments instead of using global variables:

void create_row_line(int col)
{
    /* print stuff */
}

int count_alive_neighbours(int row, int col, const int a[row][col], int r, int c)
{
    int count = 0;
    /* count neighbours */
    return count;
}

void print_state(int row, int col, const int x[row][col])
{
    /* print state */
}

void count_values(int row, int col, const int x[row][col], int y[row][col])
{
    /* put next generation in y */
}

Corresponding function calls:

    create_row_line(col);
    int neighbours_alive = count_alive_neighbours(row, col, x, i, j);
    print_state(row, col, a[flip]);
    count_values(row, col, a[flip], a[1-flip]);
Ian Abbott
  • 15,083
  • 19
  • 33
0

A simple but not efficient way is you can define a struct to hold 2d array. Then you can simply return struct object from function and assign to each other.

typedef struct {
    int array[row][col];
} S2dArray;

S2dArray count_values(S2dArray x){
   S2dArray y;
   ....
   return y;
}

int main() {
    S2dArray a, b;
    ....
    b = count_values(a);
    print_state(b);
    a = b;
    ....
}
idris
  • 488
  • 3
  • 6