2

To pass a 1D array to a function, we do this:

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

void func(int *arr, int n)
{
    // code here

}

int main()
{
    int arr[] = {......};                   // declare array
    int n = sizeof(arr)/sizeof(arr[0]);
    func(arr);                              // calling function func

    return 0;
}

To pass a 2D array to a function, we do this:

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

void func(int arr[][2])
{
    // code here
    }

int main()
{
    int arr[3][2];  // declare 2D array
    func(arr);      // calling function func

    return 0;
}

So while passing a pointer array to a function, can we do something like this?

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

void func(int **arr, int n)
{
    // code here

}

int main()
{
    int *arr[] = {......};                // declare array of pointers
    int n = sizeof(arr)/sizeof(arr[0]);
    func(arr, n);                           // calling function func

    return 0;
}

I am confused with pointers and arrays to be honest. It took me a long time to figure out how to pass a 2D array. I tried searching other similar questions, but to no avail. Please help me with: Passing a pointer array to a function. Also, any links where I can clear the confusion will be really appreciated.

Rohit Bajaj
  • 63
  • 1
  • 12
  • 1
    yes you can, int ** val, is logically equivalent to int val[n][m]; They're not the same, but can be treated as such. It should be noted however, your method of retrieving said size of the two D array seems a bit dodgy in nature because it looks like you only find the size of one of the dimensions, and set one of the 2 dimensions. – Nova Ardent Jul 04 '18 at 08:23
  • 1
    The only thing here is that you can't allocate on the heap directly the `int *`. – Paul Ankman Jul 04 '18 at 08:24

3 Answers3

5

In your last example, you have an array of pointers.

And yes, that would work, check for example this toy example:

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

void func(int **arr, int n) {

    // print equal to arr[0][1] 
    printf("%d %d\n", n, *arr[1]);
}

int main(void) {
    int a = 1, b = 2, c = 3;

    // set the points of a, b, and c, to arr[0][0..2];
    int *arr[] = {&a, &b, &c};
    int n = sizeof(arr)/sizeof(arr[0]);
    printf("%d %d\n", n, *arr[1]);

    func(arr, n);

    return 0;
}

Output:

3 2
3 2
Nova Ardent
  • 161
  • 1
  • 16
gsamaras
  • 71,951
  • 46
  • 188
  • 305
4

I am confused with pointers and arrays to be honest. It took me a long time to figure out how to pass a 2D array.

This confusion is very widespread. It stems from the fact that you cannot pass arrays in C, but the language allows a syntax that makes it look like you could.

So, as you can't pass an array to a function, what you do instead is pass it a pointer to the first array element and, if needed, the size of the array. Inside the function, you can use that pointer for accessing the array, and the syntax looks the same as if it would be an actual array. This also needs some explanation:

  • [], the indexing operator, works by adding the index to a given pointer and dereferencing the resulting pointer. Therefore, writing a[5] is exactly the same as *(a+5).
  • This even works if a is an actual array (and not a pointer), because an array evaluates to a pointer to the first element in most contexts (there are exceptions like the sizeof operator).

To make things more complicated, C allows to declare functions that look as if they were taking arrays, e.g. you can write:

void foo(int bar[]);

There's a rule in the C standard that says function parameter types are subject to "type adjustment": Any parameter of an array type is automatically adjusted to the corresponding pointer type. So, the function above is actually the same as

void foo(int *bar);

Therefore, better forget about passing arrays (I'd even recommend to not use the array syntax in function parameters, but that's subject to debate) -- you always pass pointers.


With this knowledge, you can easily construct correct examples for all your cases:

(1) "Normal" array:

void foo(int bar[], size_t n); // with type adjustment
void foo(int *bar, size_t n);  // what it really means

// call like
int a[5];
foo(a, 5);

(2) 2D array:

A 2D array is an array of arrays, so it's first element is itself an array -> you would pass a pointer to an array

void foo(int bar[][10], int n);  // with type adjustment, 2d array of n x 10
void foo(int (*bar)[10], int n); // what it really means

// call like:
int a[5][10];
foo(a, 5);

(3) Array of pointers:

An array of pointers is often used as an alternative to a 2d array -- as the elements are just pointers, they can point to single values or arrays of different lengths, but the drawback is that you don't have the whole data as a single block in memory, as would be the case with a real 2d array. As this is just an array of pointers, it looks very much like a "normal" array in use:

void foo(int *bar[], int n); // with type adjustment
void foo(int **bar, int n);  // what it really means

// call like:
int *a[5];
foo(a, 5);

Final note: You will often read that arrays "decay as pointers" in C. That's no official wording, but very widespread. If you combine the rule that an array identifier evaluates to a pointer in most contexts (e.g. when passing it to a function) with the rule of type adjustment for function parameters, the result is that you write array syntax everywhere, but get pointers, so that's the meaning of "decaying".

  • I believe defining the function like 'void foo(int bar[10])' has the advantage that debugger knows the number of elements and can display them in the variables window. If you only pass a pointer, debugger only displays the first element. – PauliL Apr 28 '22 at 14:22
1

There is actually no differences among 1-D array, 2-D array, X-D array and pointer array. All of them are just consequent memory area.

1-D array:

int arr[8];
int *addr = arr;

| arr[0] | ...*6... | arr[7] |
^                   ^
|                   |
addr                 addr+7

2-D array:

int arr[4][2]
int *addr = arr[0];

| arr[0][0] | ...*6... | arr[3][1] |
^                      ^
|                      |
addr                   addr+7 

3-D array:

int arr[2][2][2];
int *addr = arr[0][0];

| arr[0][0][0] | ...*6... | arr[1][1][1] |
^                         ^
|                         |
addr                      addr+7

pointer array:

typedef pointer int*;
...

int a0[8] = {0, 1, 2, 3, 4, 5, 6, 7};
int a1[7] = {0, 1, 2, 3, 4, 5, 6};
...
int a7[1] = {0};

pointer arr[8] = {a0, a1, a2, a3, a4, a5, a6, a7};
pointer *addr = arr;

| arr[0] = a1 | ...*6... | arr[7] = a7 |
^                        ^
|                        |
addr                     addr+7

Defining an X-D array can be implemented as following:

typedef data_type int;

typedef struct X-Array
{
    int d1;
    int d2;
    ...
    int dx;
    data_type *data;
} X-Array;

X-Array *create_X_Array(int d1, int d1, ..., int dx)
{
    X-Array *arr;
    if ((arr = malloc(sizeof(X-Array))) == NULL)
        return NULL;

    if ((arr->data = malloc(sizeof(data_type * d1 * d2 * ... * dx))) == NULL)
    {
        free(arr);
        return NULL;
    }

    arr->d1 = d1;
    arr->d2 = d2;
    ...
    arr->dx = dx;

    return arr;
}
Joy Allen
  • 402
  • 3
  • 8
  • 1
    [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) for the correct method. – Lundin Jul 04 '18 at 10:00