1

My teacher told me that int **array is not a 2D array, it is just a pointer to a pointer to an integer. Now, in one of my projects I have to dynamically allocate a 2D array of structs, and this is how it is done:

struct cell **array2d = (struct x **)calloc(rows, sizeof(struct x *));
    
for (int i = 0; i < columns; i++) {
    array2d[i] = (struct x *)calloc(j, sizeof(struct x));
}

But here we return a pointer to a pointer to the struct, so how is this a 2D array?

Before using dynamic allocation, I had a statically allocated array of the form: array2d[][]

Now that I replaced it by dynamic allocation, I also replaced array2d[][] by **array2d.
Every function that takes array2d[i][j] als argument now returns an error saying the types don't match.

Could someone explain me what is happening here? What is the difference between **array and array[m][n] and why is the compiler complaining?

10
  • First of all, C doesn't have "2D" arrays. It does have nested arrays, arrays of arrays, which unfortunately are frequently talked about as multi-dimensional arrays. And secondly, an array of arrays is not the same as a pointer to a pointer (also sometimes known as a jagged array). See e.g. this old answer of mine for a somewhat visual representation of the differences. Commented Dec 8, 2021 at 7:21
  • And to help you understand things a little better, there are two things you need to know: 1) All arrays (proper arrays) can decay to a pointer to its first element. So if we have e.g. int array[X]; then using plain array is the same as &array[0], with the type int *; And 2) For any array or pointer p and index i, the expression p[i] is exactly equal to *(p + i), which means that all "array" indexing is really pointer arithmetic. These two things is what sometimes can make arrays and pointers seem similar. Commented Dec 8, 2021 at 7:23
  • 1
    Both or neither, depending on whom you ask. A 2D array is an abstraction. It only exist in people's heads (and details vary between people's heads). It is not a language construct. Not in C at any rate. Both int **array and int array[m][n] may or may not implement your preferred abstraction to some degree or another. But they are different, incompatible things as far as C is concerned. Commented Dec 8, 2021 at 7:31
  • for (int i = 0; i < i; i++) { - When do you expect i to be less than i? Will this loop ever run? Given that i appears before the loop, it looks like the i locally scoped to your list is shadowing the i in the outer scope. Commented Dec 8, 2021 at 7:39
  • 1
    @Chris I fixed it to make the example clearer. Commented Dec 8, 2021 at 7:42

1 Answer 1

3

They're thoroughly different things.

An array is a sequence of values of the same type stored one after another in memory.

In C, an array is more or less interchangeable with a pointer to its first elementa[0] is *a,
a[1] is *(a+1), etc. — at least when we're talking about one-dimensional arrays.

But now consider:

int a[3][3];

in this case, a contains nine elements, contiguous in memory. a[0][0] through a[0][2], then a[1][0] immediately after, up until a[2][2].

If you pass a to a function, it would fit into a parameter type of int * or int[3][3] or int [][3] (knowing the "stride" of the second dimension is absolutely necessary to doing the math to look up a given element).

On the other hand:

int *b[3];
b[0] = malloc(...);
b[1] = malloc(...);
b[2] = malloc(...);

in this case, b is an array of 3 elements, each of which is a pointer to an array of 3 elements. You still access it like b[0]0] or b[1][2], but something completely different is happening under the hood. The elements aren't all stored contiguously in memory, and *b isn't any of them, it's a pointer. If we were to pass b to a function, we would receive it with a parameter of type int ** or int *[]. Knowing the length of each row in advance isn't necessary, and in fact each row could have a different length from the others. Some of the rows could even be null pointers, with no storage behind them for integers.

Sign up to request clarification or add additional context in comments.

1 Comment

For your example *a is the same as a[0] -- you need **a to be the same as a[0][0]. The expression a[1] is perfectly meaningful; it is a pointer to the second subarray of a and will decay to &a[1][0] (a pointer to the first element of that subarray) in most cases.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.