0

First of all, I have gone through these links to better help me understand multidimensional arrays and how to pass them in functions
https://stackoverflow.com/a/17569578/10452758
http://c-faq.com/aryptr/pass2dary.html
https://jameshfisher.com/2016/12/08/c-array-decaying/

I still have some unclarity in my mind related to these things, which I would like to ask

Suppose we have a 2d array like

int mat[][COLS] = {{...}}

i) We know that we can pass the mat in a function if its size is fixed i.e. known at the compile time as int (*mat)[COLS] to a function. Here, mat is referred to as a single pointer to an integer array i.e. it points to a whole integer array, then will mat in the main function like defined above will be referred to as same i.e. single pointer to an integer array or is it not a pointer at all??? But printing mat into console gives out an address so basically, it is a pointer.
As in the case of int arr[] = {}, we say that arr is the address of the first element, what can be said about mat.

I understand the difference b/w mat and mat[0] but how come a pointer pointing to the whole array and a pointer pointing to the first element of an array has the same value. How does in case of dereferencing the pointer, one knows that it is a pointer to the first element or the whole array???
I know the question sounds a little vague but simply I want to know why mat and mat[0] points to the same address of the memory because if they mean different thing won't they have different values???

If someone can share what they know about what mat refers to as a pointer, it will be very helpful. I have read other SO questions about these topics but these things still hadn't clicked with me.

ii) We know that arrays in C are not basically a pointer but when we access the value of an array, it decays to a value of a pointer type. So basically I wanted to ask is whether in int arr[] = {...}, arr is not a pointer and I want to back it with sizeof(arr)!=size_occupied_by_pointer, is that the case also when we define mat and when we print them in console there decayed pointer values are referred to. Is this the answer to the first question???

iii) Other doubt I have is passing a matrix as a pointer to a pointer. This is the code I am tending to follow but results in segmentation error. If someone can point out the mistake -

void process_pointer_to_pointer(int **arr, size_t rows, size_t cols){
    for (size_t i = 0; i < rows; ++i){
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << arr[i][j] << '\t';
        std::cout << std::endl;
    }
}

int main(int argc, char const *argv[]){
    const int m=4, n=4;

    int mat[][n]= {
        {1, 3, 1, 5},
        {2, 2, 4, 1},
        {5, 0, 2, 3},
        {0, 6, 1, 2}
    };
        int *ip = &mat[0][0];
    process_pointer_to_pointer(&ip, m, n);

I know the simpler way of doing the same thing by using an array of pointers and filling those with mat[i], but I have followed the limitation in following this method and tried this approach from here
http://c-faq.com/aryptr/pass2dary.html
but didn't get the desired result...

PS: The question may be completely all over the place so if not able to understand something specific, pls ask me. I am more than happy to go in a debate mode. Also, I am new to coding, so pls cut me some slack. :p

  • https://www.geeksforgeeks.org/pointer-array-array-pointer. This has helped me a lot but still don't get why mat and mat[0] have same address value?? – Akash Chandra Jun 18 '19 at 02:44
  • `int **arr` is incorrect when passing a 2D array. An array does not decay to a 2D pointer. My solution is to not use raw 2D arrays. Are you permitted to use `std::array`? Another alternative is to provide a 1D array and a function that converts 2D indexing to a 1D index (`row*numberColumns + column`). – user4581301 Jun 18 '19 at 02:45
  • @user4581301 I know its incorrect to pass pointer to pointer, but u can refer the 4th point of https://stackoverflow.com/a/17569578/10452758 – Akash Chandra Jun 18 '19 at 02:46
  • ``mat`` is the address of the beginning of the array. ``mat[0]`` is also the address of the beginning of the array, since the first elements starts at the beginning of the array. – kmoser Jun 18 '19 at 02:47
  • Note the note left by the answerer of the question where it is pointed out that `int **` abuses undefined behaviour and may fail horribly. – user4581301 Jun 18 '19 at 02:53
  • 1
    The addresses of `mat`, `mat[0]` and `mat[0][0]` are identical. They are the addresses of the first element in the matrix. However, the types of each differ. – doug Jun 18 '19 at 02:53
  • `int arr[] = {}` is an empty array. A C++ compiler should be quite upset by this line. A C compiler should also reject it. – user4581301 Jun 18 '19 at 03:04
  • Pick a language. C is not C++. – n. m. could be an AI Jun 18 '19 at 04:19

2 Answers2

0

You have a couple choices. Actually create an array of pointers that point to each row, or calculate an offset into the matrix and treat it as a one dimensional array of ints. The former is the way this was done 30+ years ago when multiplication was slower than an extra memory access. This must be done if you declare arr as int **arr

#include <iostream>
using std::cout;

void process_pointer_to_pointer(int** arr, size_t rows, size_t cols) {
    for (size_t i = 0; i < rows; ++i) {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << arr[i][j] << '\t';
        std::cout << std::endl;
    }
}

int main(int argc, char const* argv[]) {
    const int m = 4, n = 4;

    int mat[][n] = {
        {1, 3, 1, 5},
        {2, 2, 4, 1},
        {5, 0, 2, 3},
        {0, 6, 1, 2}
    };

    int* pRows[m];
    for (int i = 0; i < m; i++)
        pRows[i] = mat[i];

    // pRows decays to a pointer to a pointer to an int
    process_pointer_to_pointer(pRows, m, n);
}

What is now normally done with arrays of variable sizes is calculate an offset into a linear array, typically instantiated as a vector but here we use your base.

#include <iostream>
using std::cout;

void process_pointer_to_pointer(int* arr, size_t rows, size_t cols) {
    for (size_t i = 0; i < rows; ++i) {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << arr[i*cols+j] << '\t';
        std::cout << std::endl;
    }
}

int main(int argc, char const* argv[]) {
    const int m = 4, n = 4;

    int mat[][n] = {
        {1, 3, 1, 5},
        {2, 2, 4, 1},
        {5, 0, 2, 3},
        {0, 6, 1, 2}
    };
    // mat[0] decays to a pointer to int and points to the first int in mat
    process_pointer_to_pointer(mat[0], m, n);
}

For 2D arrays that have constant rows/columns, the preferred approach is using std::array. For instance:

std::array<std::array<int,4>,4> mat;
mat[2][3]=42;

This allows access much like traditional C type arrays and is just as efficient.

See also this example of wrapping a 2D array in a class that still allows conventional access using brackets.

Statically declared 2-D array C++ as data member of a class

doug
  • 3,840
  • 1
  • 14
  • 18
0

Lets experiment with some little programs:

#include <stdio.h>
#include <stdint.h>
int a1[3];
int a2[3][3];
int a3[3][3][3];

void showptr(void *a, void *b) {
    printf("%p, %p, %zd\n", a, b, (intptr_t)b - (intptr_t)a);
}

int main() {
        showptr(a1, a1+1);
        showptr(a2, a2+1);
        showptr(a2[0], a2[0]+1);
        showptr(a3, a3+1);
        showptr(a3[0], a3[0]+1);
        showptr(a3[0][0], a3[0][0]+1);
        return 0;
}

The above defines three arrays if increasing number of dimensions, and prints out some values about them. The first, (a1, a1+1), shows that adding one to ‘a1’ increments the address by 4, which is the size of an int. The next two do the same for a2 and a2[0]. A2, it turns out increments by 12 (3 ints) which is a row of the array. A2[0], increments by 4 (1 int). When we get to a3, it increments by 36 (3 rows), a3[0] increments by 12 (3 ints, a row) and a3[0][0] increments by 4.

Now, lets add a few more near the end of main:

... int *t; t = a1; showptr(t, t+1); t = a2; showptr(t, t+1); t = a2[0]; showptr(t, t+1); t = a3; showptr(t, t+1); t = a3[0]; showptr(t, t+1); t = a3[0][0]; showptr(t, t+1); ...

What happens here is interesting, the compiler complains about:

a.c:20:4: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]

and this is repeated for lines 22 and 23:

t = a2;
t = a3;
t = a3[0];

The expressions on the right hand side are all considered incompatible with the pointer type. C (and to a degree c++) permit address expressions, where you can perform limited arithmetic operations on address types (pointers). The merits of this aside, this is why some array expressions and pointer expressions overlap, they are both working on the same fundamental data type -- the address. Because address expressions are often unforgiving, you should examine the compiler warnings carefully; they are there to help you, the code gets generated anyways.

mevets
  • 10,070
  • 1
  • 21
  • 33