0

What is the difference between

#include <stdio.h>

int a[9]; 

int
main()
{
    printf("%d\n", a[1]);
}

and

#include <stdio.h>

int a[3][3]; 

int
main()
{
  printf("%d\n", a[1]);
}

I think both result in placing same 36 byte memory buffer in .bss section, what is the difference? Or is a[3][3] syntactic sugar over a[9] - a[3*3]?

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
Bulat M.
  • 680
  • 9
  • 25
  • 1
    Try declaring as `a[9]` and printing `a[0][0]`. – Riley Sep 16 '16 at 17:36
  • Looks to me that one is an array a[9] and the other one is a matrix a[3][3]. First is an array of 9 elements and the other is and array of 3 arrays of 3 elements – Sergiu Sep 16 '16 at 17:36
  • I'm voting to close this question as off-topic because it shows no research at all. – gsamaras Sep 16 '16 at 17:37
  • Some good info here, possibly: http://stackoverflow.com/a/2565048/6273251 – Random Davis Sep 16 '16 at 17:38
  • 2
    Possible duplicate of [How are multi-dimensional arrays formatted in memory?](http://stackoverflow.com/questions/2565039/how-are-multi-dimensional-arrays-formatted-in-memory) – kfsone Sep 16 '16 at 17:41
  • We cannot just look at the byte allocation and assume it is same. One is a 1D array another is a 2D array. If we go by byte allocation, then int x=100000 and int x=900000 will use same number of bytes but you see they do differ – Suvarna Pattayil Sep 16 '16 at 17:41
  • If you'd *run* the two pieces of code you posted, you would have seen they give different results. Downvoting. – kfsone Sep 16 '16 at 17:42
  • @kfsone and the output will be based on UB. You can never _rely_ on the output unless you _know_ your code. Just sayin'. :) – Sourav Ghosh Sep 16 '16 at 17:43
  • @SouravGhosh Are you referring to the '%d'? – kfsone Sep 16 '16 at 17:45
  • @kfsone certainly, I am. – Sourav Ghosh Sep 16 '16 at 17:45
  • @SouravGhosh Well, if he'd tried compiling and running his code the compiler would have annotated that :) http://stackoverflow.com/questions/2565039/how-are-multi-dimensional-arrays-formatted-in-memory – kfsone Sep 16 '16 at 17:49
  • @KeithThompson thanks, this comment will self-destroy in a minute. :) – Sourav Ghosh Sep 16 '16 at 18:08
  • "this comment will self-destroy in a minute. :) – Sourav Ghosh **12 mins ago**" 8-)} – Keith Thompson Sep 16 '16 at 18:21

3 Answers3

6

Nope, they are not same, they represent different types. In reference to your code,

  • In the first case, a is an one dimensional array. Hence, a[1] is of type int.

    §) To print the value, %d is fine.

  • In the second case, however, a is a two-dimensional array. Hence, a[1] is of type int [3].

    §) When passed as a function argument, it decays to a pointer to the first element, basically a int *. You'll be needing %p to print that (and cast the pointer to void *, as required by the %p format specifier).

However, if you're bothered about the memory layout for both the variables, you can check the other answer by AnT or another one which details about the memory layout of multi-dimensional arrays.

Community
  • 1
  • 1
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
3

int a[3][3] is a semantic sugar of sorts over int a[9]. The raw memory layout it the same (i.e. it is a contiguous block of 9 ints), but the language-level access syntax is different. When accessing a[3][3] as a[i][j], the apparent 2D-indexing is converted by the compiler to 1D indexing using i * 3 + j formula. The latter index translation scheme is easily extendable to any number of dimensions.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    Does not it mean that multi-dimensional static arrays are redundant because we can always convert them to one-dimensional? I mean, is multidimensional arrays are only there to ease programming and not to bother with index/offsets counting? – Bulat M. Sep 16 '16 at 17:58
  • 1
    @Bulat M.: Yes, exactly. In that sense they are redundant. You can always replace a built-in multi-dimensional array with a single-dimentional one, in which case you will have to implement the above index translation scheme manually. – AnT stands with Russia Sep 16 '16 at 18:00
  • But why cannot we pass int[9] array to function that accepts as a parameter two-dimensional array? – Bulat M. Sep 16 '16 at 18:04
  • 2
    @BulatM.: Because they're different types. (For that matter, we can't pass arrays to functions at all; C has no parameters of array type.) Just because `int[9]` and `int[3][3]` have the same memory layout, that doesn't mean you can perform the same operations on them. – Keith Thompson Sep 16 '16 at 18:09
  • @Bulat M.: Multi-dimentsional arrays are not qualitiatively different from single-dimensional ones. From the language point of view `int[3][3]` array is actually a single dimensional array `T[3]`, in which `T` just happens to be an `int[3]`. I.e. it is a single-dimensional array of single-dimensional arrays. So, from the language point of view allowing to pass `int[3][3]` array as `int[9]` array is like allowing to pass some `T[3]` as `int[9]`. These types are not really related, if you look at them that way. They just happen to have the same size. – AnT stands with Russia Sep 16 '16 at 18:35
  • 1
    But you said that `int a[3][3]` is semantic sugar over `int a[9]`. Now you say they're not really related. – Keith Thompson Sep 16 '16 at 18:36
  • @Keith Thompson: What I meant is that the differences exist only at the language level. The raw memory blocks are organized indenticaly and whatever differences exist, they exist only as language concepts (be that multi-dimensional access or C type system). This is why I called it *semantic* sugar instead of *syntactic* sugar. – AnT stands with Russia Sep 16 '16 at 18:44
  • Does `int a[3][3]` vs. `int b[9]`. impose different memory constants concerning "one past"? I do not _think_ so - so maybe this concern does not apply. `&b[9]` must be a valid pointer value, as does `&a[3]` and `&a[2][3]`. Seems all are the same address. Hmmm. – chux - Reinstate Monica Sep 16 '16 at 19:17
3

What is the difference between

int a1[9];

and

int a2[3][3];

(I've changed the names so I can talk about the declarations more easily.)

The difference is that they're of different types. They both have the same underlying memory layout,each consisting of a contiguous region of memory 9 times the size of an int. But a1 is an array of 9 int objects, while a2 is an array of 3 objects each of which is an array of 3 int objects. It's a multidimensional array, which in C is precisely an array of array, nothing more, nothing less.

The difference is not just syntactic sugar. You might get the same generated code for certain operations, for example a1[1] and a2[0][1]. But, for example, a1[3] refers to the 4th element of the array a1, while a2[0][3], though you might think it refers to the same thing, actually has undefined behavior. (Compilers are permitted, but not required, to perform run-time array bound checking, and are permitted to assume that array references do not go past the end of the indexed array object.)

printf("%d\n", a2[1]);

As others have mentioned, this has undefined behavior. a2[1] is an array object of type int[3]. An expression of array type is, in most contexts, converted to an expression of pointer type, so a2[1] ends up being of type int*, and yields a pointer to the initial element of the second row of a2. To print a pointer value, use %p -- which requires an argument of type void*, so you need to cast it:

printf("%p\n", a2[1]);

Recommended reading: Section 6 of the comp.lang.c FAQ.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631