adsf
adsf

Reputation: 59

Difference between int* a and int (*a)[N] in functions?

I can pass int arrays to both of these functions (1d and 2d arrays). What is the difference between them? With the second function you need to specify the size of the array. Why?

void foo(int *a, int cols) 
void bar(int (*a)[N])

I have a program where I want to pass 2d int arrays to functions. Which is the better one to use and does it matter?

Upvotes: 2

Views: 269

Answers (3)

anastaciu
anastaciu

Reputation: 23802

Difference between int* a and int (*a)[N] in functions?

While int* a is a pointer to int, int (*b)[N] is a pointer to an array of N ints. These are different types, and are not compatible.

b can only point to an array of N ints while a can point to an int wether it's in an array or not. When you pass an array as argument you are really passing a pointer to its first element, it's commonly said that it decays to a pointer. Another difference is that if you increment b it will point to the next block of N ints whereas if you increment a it will just point to the next int.

I have a program where I want to pass 2d int arrays to functions. Which is the better one to use and does it matter?

It matters, for foo, it hints to a flat array with cols width, of course with some arithmetic you can treat it as a 2D array, bar is a more natural use for a 2D array.

The memory layout will probably be the similar, and using a flat array to store elements in such a way that you can use it as a 2D array is perfectly fine. I personally find it less messy and more clear to use a pointer to array, instead of pointer to int, when I need a 2D array.

Example:

#include <stdio.h>
#include <stdlib.h>
#define N 5
#define M 5
void bar(int (*a)[N]) {
    
    // prints 20, 4 bytes * 5 (can be different deppending on the size of int)
    printf("Size of 'a' is %zu\n\n", sizeof *a);

    // populate the array
    int c = 1;
    for (int i = 0; i < N; i++)
        for (int j = 0; j < M; j++) 
            a[i][j] = c++;

    for(int i = 0; i < N; i++){ 
        printf("a[%d][0] = %2d\n", i, **a);  
        // incrementing 'a' will make it point to the next array line 
        a++;
    } 
    putchar('\n');     
}

Output:

Size of a is 20

a[0][0] =  1
a[1][0] =  6
a[2][0] = 11
a[3][0] = 16
a[4][0] = 21
int main() {

    int(*a)[N] = malloc(sizeof *a * M);

    bar(a);

    // prints the complete array
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            printf("%2d ", a[i][j]);
        }
        putchar('\n');
    }
    free(a);
}

In this code a is a 2D array N x M which is passed to bar to be populated. I added some handy prints and comments for clarification. See it live: https://godbolt.org/z/1EfEE5ed7

Output:

 1  2  3  4  5 
 6  7  8  9 10 
11 12 13 14 15 
16 17 18 19 20 
21 22 23 24 25 

Upvotes: 2

Andreas Wenzel
Andreas Wenzel

Reputation: 24736

With the second function you need to specify the size of the array. Why?

You don't have to specify the entire size of the entire 2D array, but you do have to specify the number of columns, i.e. the size of the sub-array.

The reason why the compiler must know the size of the sub-array is that this information is required for offset calculations.

For example, if you define a 2D array like this

int arr[2][4];

then the array elements will be stored in memory in the following order:

arr[0][0]
arr[0][1]
arr[0][2]
arr[0][3]
arr[1][0]
arr[1][1]
arr[1][2]
arr[1][3]

If the compiler wants to for example access arr[1][2], it will need to know how many columns there are per row, i.e. how big each sub-array is. If the compiler does not know that there are 4 columns per row, then the compiler has no way of knowing where to find arr[1][2]. However, if the compiler knows that there are 4 columns per row, it will know that the 5th element of the 2D array is the start of the second row, so it will know that arr[1][2] is the 7th element of the 2D array.

I can pass int arrays to both of these functions (1d and 2d arrays).

Although accessing a 2D array as a 1D array may work on most compilers, it is undefined behavior according to the ISO C standard. See this question for further information:

One-dimensional access to a multidimensional array: is it well-defined behaviour?

Which is the better one to use and does it matter?

The first one is better in the sense that it allows you to specify the number of columns at run-time, whereas with the second one, the number of columns must be set at compile-time, so you are less flexible.

However, as stated above, depending on your compiler, it may not be safe to access a 2D array as a 1D array. Therefore, it would probably be best to pass 1D arrays to the first function and 2D arrays to the second function.

However, it is not clear how the second function is supposed to know how many rows the 2D array contains, since this information is not passed to the function as an argument. Maybe the function is assuming a fixed number of rows, or maybe it is not intended to be used for 2D arrays at all. The information that you have provided in the question is insufficient to answer this.

Upvotes: 1

chqrlie
chqrlie

Reputation: 144715

There are significant differences difference between these methods for 2D matrices

  • in the first method you pass a pointer to int, which is not the proper type for the matrix, even after implicit decaying.
  • cols is an int argument in the first prototype, whereas it is a constant N in the second one. In both cases, the number of rows is not specified, so it must be implicit, either because it is constant or because the matrix is square.
  • You will need to write the index computations explicitly in the first case and you can use a[row][col] in the second, which will be simpler, more readable and compile to more efficient code.

Note that since C99, there is a 3rd possibility allowing you to use the array syntax even for a variable number of columns. Here is an example:

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

void init_matrix(int rows, int cols, int (*a)[cols]) {
    int x = 0;
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) {
            a[r][c] = x++;
        }
    }
}

void print_matrix(int rows, int cols, const int (*a)[cols]) {
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) {
            printf("%3d%c", a[r][c], " \n"[c == cols - 1]);
        }
    }
}

int main() {
    int cols = 4, rows = 3;
    int (*a)[cols] = malloc(sizeof(*a) * rows);
    init_matrix(rows, cols, a);
    print_matrix(rows, cols, a);
    return 0;
}

Upvotes: 2

Related Questions