Lostsoul
Lostsoul

Reputation: 26067

Passing a unknown size matrix reference to a c function

I'm new to C(but starting to like it after a bit of the growing pains) but I'm having a bit of trouble sending a matrix. I started building everything in my main function and it worked. I created the list like this: int list[sizeOfBuckets][30]; then added data and when I wanted to read it I did something like this:

    int x;
    int xx;
    for (x=0;x<sizeof(current_list) / sizeof(current_list[0]); x++) {
        printf("we are in the list \n");
        for (xx=0;xx<sizeof(current_list[x]) / sizeof(current_list[x][0]); xx++) {
            printf("item: %i \n", current_list[x][xx]);
        } 
    }

It worked fine, it would give me the contents of the matrix. But now I want to send it to a function but I'm having problems. I did some reading and learned that matrix's are not the same as sending arrays as C needs to know the dimensions beforehand but since I will have this dynamically changing I created two variables that monitor the sizes of both and I send them to an array(in my test I verified they are correct, list is list[2][30]). Here's my function:

void process_list(int **current_list, int num_of_rows, int num_items_in_row) {
    printf("hello from function \n");
    //test the list
    int x;
    int xx;
    for (x=0;x<num_of_rows; x++) {
        printf("we are in the list \n");
        for (xx=0;xx<num_items_in_row; xx++) {
            printf("item: %i \n", current_list[x][xx]);
        } 
    }
}

It doesn't work for me. It simply prints out we are in the list and not the actual items(from the for loop below) as I would expect. When I compile with GCC I get the following warning:

./learningC.c: In function ‘main’:
./learningC.c:169: warning: passing argument 1 of ‘process_list’ from incompatible pointer type

I'm wondering what am I doing wrong? If it helps, I don't need to modify the array, I just need to contents sent to the function and then my function will output a different result.

Update: Here's the matrix I'm sending:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 4],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 4],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 4],

Here's the expected result:

hello from function 
we are in the list 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 2 
item: 2 
item: 2 
item: 4 
we are in the list 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 1 
item: 1 
item: 2 
item: 2 
item: 4 
we are in the list 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 0 
item: 1 
item: 2 
item: 1 
item: 2 
item: 4 

Upvotes: 1

Views: 1743

Answers (5)

vmsnomad
vmsnomad

Reputation: 339

I'm all for treating static arrays as objects vs. pointers. However in the cases when it's about remedies to an existing code, some C casting trickery could be applied [carefully].

In your original example to pass a static array to process_list function, one may do:

int **arrptr;
int static_arr[3][4] = {...};  /* init static array */
...
*arrptr = (int *)&static_arr;    /* put the static_arr address into arrptr */

process_list(arrptr, num_rows,num_cols);

This effectively treats the static array as if it were malloc'ed, that is the pointer arrptr contains reference to memory address of static_arr, instead of the actual address, which is assigned at compile-time.

NOTE:I'm not advocating such technique, and if applied in code, some comments about the cast intent should be supplied. Otherwise someone may unknowingly try to "free" such pointer, also scoping issues etc.

Upvotes: 0

Jens Gustedt
Jens Gustedt

Reputation: 79003

If you have a modern C compiler, starting with C99 would do, you can pass a matrix with variable lengths (technical term is "VLA") just like this

void process_list(size_t num_of_rows, size_t num_items_in_row, int current_list[num_of_rows][num_items_in_row]) {
... access the elements as current_list[i][j] here ...
}

Just watch that you have the sizes before the array, such that they are known in the declaration of the array parameter.

BTW, your attempt to do it with ** is not correct at all. A 2D matrix and an array of pointers are really different things.

Upvotes: 4

An n dimensional array is still an array of 1 dimensional memory space. So use only a pointer, not a pointer to pointer.

void process_list(int *current_list, int num_of_rows, int num_items_in_row) {
    printf("hello from function \n");
    //test the list
    int x;
    int xx;
    for (x=0;x<num_of_rows; x++) {
        printf("we are in the list \n");
        for (xx=0;xx<num_items_in_row; xx++) {
            printf("item: %i \n", current_list[x*num_items_in_row + xx]); % previously it was x*num_of_rows + xx
        } 
    }
}

I also changed the way to access elements. Now you must make the function call like that:

process_list(*current_list,3,3);

Upvotes: 2

Mike Kwan
Mike Kwan

Reputation: 24477

There are quite a few ways of passing 2D arrays. I shall first go over a few examples of these then point out the fundamental error in your code:

/*
 * Array with empty first dimension.
 * Note the y value passed is redundant in this case.
 */
void process_matrix(int matrix[][4], int x, int y)
{
    for(int i = 0; i < x; i++) {
        for(int j = 0; j < y; j++) {
            printf("[%d][%d] = %d\n", i, j, matrix[i][j]);
        }

        puts("");
    }
}

This function takes in an array with an empty first dimension. This allows the unknown size that you state to be passed in. The y is redundant in this case because we can get it back out using something a macro such as ARRAY_SIZE.

/*
 * Pointer to array with explicitly specified second dimension.
 * Again the y value passed is redundant in this case.
 */
void process_matrix2(int (* matrix)[4], int x, int y)
{
    for(int i = 0; i < x; i++) {
        for(int j = 0; j < y; j++) {
            printf("[%d][%d] = %d\n", i, j, matrix[i][j]);
        }

        puts("");
    }
} 

This is a pointer to an array where the second dimension is explicitly specified. Note the bracketing which is very important in this case. The same code without brackets is still valid C but means something entirely different. By a similar argument as above, the y parameter is redundant.

/*
 * Both dimensions known.
 * Note the x and y value passed are both redundant in this case.
 */
void process_matrix3(int matrix[2][4], int x, int y)
{
    for(int i = 0; i < x; i++) {
        for(int j = 0; j < y; j++) {
            printf("[%d][%d] = %d\n", i, j, matrix[i][j]);
        }

        puts("");
    }
}

This case might not be applicable for you but is here for completeness. If both dimensions are known, they can be specified as part of the function prototype. By similar arguments as above, x and y are redundant here.

The above functions can be tested with this code (make sure to compile with -std=c99):

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

int main(void)
{
    int matrix[2][4] = {{1,2,3,4}, {8,7,6,5}};

    process_matrix(matrix, 2, 4);
    process_matrix2(matrix, 2, 4);
    process_matrix3(matrix, 2, 4);
    process_matrix4(matrix, 2, 4);
    return EXIT_SUCCESS;
}

Back to your original code, the problem you have is that a double pointer can't be used as a 2D array. To quote from this article, which you should certainly read:

...it is wrong to declare: "int **mat" and then use "mat" as a 2D array. These are two very different data-types and using them you access different locations in memory. On a good machine (e.g. VAX/VMS) this mistake aborts the program with a "memory access violation" error.

This mistake is common because it is easy to forget that the decay convention mustn't be applied recursively (more than once) to the same array, so a 2D array is NOT equivalent to a double pointer. A "pointer to pointer of T" can't serve as a "2D array of T". The 2D array is "equivalent" to a "pointer to row of T", and this is very different from "pointer to pointer of T".

The article shows a few more methods of passing a 2D array which I don't show such as doing it by flattening the array, which is what Seçkin Savaşçı is trying to show.

Upvotes: 1

Greg E.
Greg E.

Reputation: 2742

There are some subtle distinctions between statically allocated multi-dimensional arrays in C and dynamically allocated ones, and I'm not sure if I remember them accurately. That said, I think the correct type of your matrix is actually a pointer to a one-dimensional 30-element integer array. Try altering your function declaration to read: void process_list(int current_list[][30], int num_of_rows, int num_items_in_row), or perhaps void process_list(int *current_list[30], int num_of_rows, int num_items_in_row).

Upvotes: 1

Related Questions