dunlop
dunlop

Reputation: 71

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

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

Upvotes: 1

Views: 933

Answers (4)

ryyker
ryyker

Reputation: 23208

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 
}

Upvotes: 1

Ian Abbott
Ian Abbott

Reputation: 17403

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]);

Upvotes: 1

tstanisl
tstanisl

Reputation: 14107

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);
}

Upvotes: 1

idris
idris

Reputation: 588

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;
    ....
}

Upvotes: 0

Related Questions