1

Practicing some simple arrays and pointers basics, I met something I can't understand:

in all written sources I could find, they say that a name of a 2D array is actually a 2D pointer, meaning that if I write:

int a[3][4];

and also declare a pointer:

int **d2;

both of them are of the same type and I can safely assign:

d2 = a;

making the pointer point to the beginning of the first line.

But most surprisingly, that doesn't work, and my question is-why?

I would copy for you the code and the warnings I am getting:

#include <stdio.h>

int main() {
    int **d2;
    int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };

    d2 = a;
    while (d2 < a + 2) {
        printf("%d", **d2);
        d2++;
    }
    return 0;
} 

I get this diagnostic:

warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
  d=a;
   ^

ptr.c:11:9: warning: comparison of distinct pointer types lacks a cast
  while(d<a+2)
chqrlie
  • 131,814
  • 10
  • 121
  • 189
davidku
  • 39
  • 1
  • 7

4 Answers4

5

They have totally different inner structure, so they are not interchangeable.

2D array

2D array is like this:

p[3][3]
|
v 
+---------+---------+---------+---------+---------+---------+---------+
| p[0][0] | p[0][1] | p[0][2] | p[1][0] | p[1][1] | p[1][2] |   ...   |
+---------+---------+---------+---------+---------+---------+---------+

A single continuous space.

Pointer

pointer is like this:

**p
|
v 
+------+   +---------+---------+---------+---------+---------+
| p[0] |-->| p[0][0] | p[0][1] | p[0][2] | p[0][3] |   ...   |
+------+   +---------+---------+---------+---------+---------+
| p[1] |-->| p[1][0] | p[1][1] | p[1][2] | p[1][3] |   ...   |
+------+   +---------+---------+---------+---------+---------+
| p[2] |-->| p[2][0] | p[2][1] | p[2][2] | p[2][3] |   ...   |
+------+   +---------+---------+---------+---------+---------+
| .... |
+------+

Where every p[i] may pointing to difference space with different size.

Zang MingJie
  • 5,164
  • 1
  • 14
  • 27
2

int **d2 defines a pointer to a pointer to an int. You can also think of it as a pointer to an array, and each array element contains a pointer to an int. Zang MinJie's answer shows this nicely.

An array int a[3][4]; is a contiguous block of memory, so equivalent to int a[3*4];. As you see, it is not an array of pointers to the rows.

Note that the compiler knows when you work with a contiguous array and with an array of pointers, and uses the correct indexing method. For example:

void f(int **d2)
{
    printf("%d", d2[3][4]);
}
void g(void)
{
    int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };

    printf("%d", a[3][1]);
}

As you see, the way to write the index expressions is the same, even though the underlying representations are completely different. I can understand that is confusing. Just remember that "the compiler knows".

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • Maybe it could be useful to show how OP could access elements in 2d array using a pointer? – andresantacruz Aug 18 '19 at 10:07
  • i am trying to understand what all of you are saying... but yet, if "a" is a 2d array, &(a+1) would give the address of the begining of the second row. it makes it look like there is some kind of an array of pointers in a 2d array... – davidku Aug 18 '19 at 10:10
  • As others had noted there is no "second row". A 2d array is a sequential linear block of data - there is just "1 row". Elements in a 2d array is layed out in memory the exact same way as a 1d array the difference is just how you access those elements. – andresantacruz Aug 18 '19 at 10:19
  • @dedecos, that is partially true. Declaring as 2d allows easier indexing expessions. (Actually as _n_ d). – Paul Ogilvie Aug 18 '19 at 10:21
  • @PaulOgilvie by the generated assembly you mean? – andresantacruz Aug 18 '19 at 10:22
  • @dedecos, no, in writing: `a[2][1];` vs `b[2*3+1];` – Paul Ogilvie Aug 18 '19 at 10:29
  • @PaulOgilvie yeah of course but I said that the difference between a 1d array and a 2d array is how you access the elements. How is this partially true? '-' – andresantacruz Aug 18 '19 at 10:31
  • hmm... a small question: based on the said above- if i want a function to get a matrix as a parameter- how should i write it? function( a[][x])? or is there an option to write it in pointer-style? – davidku Aug 19 '19 at 10:25
  • 1
    @davidku, you could write it as `void f(int *a, int rowsize, int colsize)` and index it as `a[i*rowsize+j]` with i the row you want to work on and j the col to work on. Newer versions of C allow `void f(int rowsize, int colsize, int a[rowsize][colsize]` and index as `a[i][j]`. – Paul Ogilvie Aug 19 '19 at 11:40
1

int **d2; is not a 2D pointer, it is a pointer to a pointer to int, sometimes called a double pointer.

int a[3][4]; defines an array of 3 arrays of 4 int, also called a 2D array.

These 2 objects have incompatible types: there is no int pointer variable here, the address of which could be stored into d2.

Let's take an analogy:

  • an int is like a house with 32 rooms;
  • a street is an array of houses;
  • a block is a group of contiguous streets;
  • a pointer is an entry in an address book with the address of a house;
  • a double pointer is a piece of paper where you wrote the location where you stored your address book.

The rows in a 2D array are consecutive in memory, so you can enumerate the entries in the array this way:

#include <stdio.h>

int main() {
    int *p;
    int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };

    printf("iterating as a 2D array:\n");
    p = &a[0][0];
    for (int row = 0; row < 5; row++) {
        for (int col = 0; col < 2; col++) {
            printf("%d ", *p);
            p++;
        }
        printf("\n");
    }
    printf("\n");

    printf("iterating as a single array:\n");
    p = &a[0][0];
    while (p < &a[5][0]) {
        printf("%d ", *p);
        p++;
    }
    printf("\n");
    return 0;
}

Output:

iterating as a 2D array:
1 2
3 4
5 7
7 8
9 11

iterating as a single array:
1 2 3 4 5 7 7 8 9 11
chqrlie
  • 131,814
  • 10
  • 121
  • 189
1

This is because int **d2 is a pointer to pointer. There is no such thing as 2d pointer.

For example, lets consider 1d array:

int a[2] = {1, 2};

Now, an integer pointer will point to the first element of the array if assigned as follows:

int *d = a;

Talking about pointer to pointers, a pointer to pointer points to a memory location which holds address of other pointer!

For example,

// This will declare an integer pointer
int *a;

The above pointer will store address of some other variables.

For example, you can assign values to it as follows:

int x = 5;
int *a = &x;

Now, here is what a pointer to pointer does:

A pointer to pointer holds memory address of other pointers.

This means that, in the above example we can declare a pointer to pointer that holds address of pointer a:

int x = 5;
int *a = &x;

// A pointer to pointer
int **b = &a;

// To print value of x using b
printf("%d", **b);

The above code can be understood as follows:

  1. a = &x; so *a = *(&x) = 5
  2. b = &a; so *b = *(address in b) = *(address of a) = address of x
  3. But **b = *(*b) = *(address of x) = 5

Hope this clears your confusion about pointer to pointers.

Now, coming towards your use case, if you want a pointer to point to your 2d integer array, you can just use a pointer and assign it the address of first element of the array. This can be done as follows:

int a[2][2] = {{1, 2}, {3, 4}};

// Declare a pointer that points to first element of array
int *b = &a[0][0];

Now if you want to access the element a[i][j] of this array using pointer, you can access it as b + 2*i + j.

In general, if the dimensions of array is p*q, the element a[i][j] can be accessed using b + q * i + j.

For example, to print the array using 2 for loops,

int rows = 2;
int cols = 3;
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *b = &a[0][0];

for(int i=0; i<rows; i++) {
    for(int j=0; j<cols; j++) {
        // Print a[i][j] using pointer b
        printf("%d ", *(b + cols * i + j));
    }
}

// 1 2 3 4 5 6

You can also use single loop,

int rows = 2;
int cols = 3;
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *b = &a[0][0];

for(int i=0; i<rows*cols; i++) {
    printf("%d ", *(b + i));
}

// 1 2 3 4 5 6
sanketd617
  • 809
  • 5
  • 16
  • thanks! that clears it out... to make sure i get it: in your very last example, what would mean the meantioning of the veriable "a", the name of the matrix? is it a pointer that points to the very first element? – davidku Aug 18 '19 at 10:36
  • @davidku Please check the explanation of **b. It was a typo. I corrected it. – sanketd617 Aug 18 '19 at 10:44
  • @davidku It will be great if you mark the answer worked the best for you as accepted so that it will help other guys facing the same problem. :) – sanketd617 Aug 19 '19 at 21:06