Ben said:
int array_2d[10][20] = { ... };
int elem = array_2d[5][6];
Figuring out how array_2d[5][6] resolves to an element of the 2d
array given the rules I've described is an interesting exercise.
I will try. Please correct me if im wrong. Here in the assignment
statement array_2d doesnt come under one of the 3 rules you gave so
it is converted to a pointer to its first element then the offset 5
is added to it and then the offset 6 is also added to derive the
value. Like:
int elem= *((array_2d + 5) + 6);
Not quite. You clearly get the that fact that:
array_2d[5][6] is (array_2d[5])[6]
i.e. array_2d first has [5] "applied" and then the result of that is
sub-scripted again (with 6). Applying the definition of [] in terms
if adding and de-referencing, we get:
array_2d[5] is *(array_2d + 5)
(array_2d[5])[6] is *(*(array_2d + 5) + 6)
So, what is array_2d converted to here? It is converted to pointer to
its first element. That is an array of 20 ints
Im sorry if i don't understand but do you mean 200 ints when you say 20
above?
No. In C, 2d arrays are arrays of 1d arrays. array_2d is a
five-element array. Each of these (including the first) is a
20-element int array.
Exactly. So a pointer to the first element must be to one of these
20-element arrays. Yes, it is followed by another 9 of them (in total
taking up space for 200 ints) but each element pointer points to just
one 20-element array.
When you write 'int stats[10]; stats;' the name of the array is
converted to a pointer to an int. A single int. You can use
arithmetic to get new pointers to the other ints in the array and
similarly, you can (and do) use arithmetic on array_2d's pointer value
to get at the other 9 elements of that array.
okay, so in declaration:
int a[2][3];
the elements are so formatted in memory in ascending address from left
to right in page??
+===================+===================+
|+-----+-----+-----+|+-----+-----+-----+|
|| 0,0 | 0,1 | 0,2 ||| 1,0 | 1,1 | 1,2 ||
|+-----+-----+-----+|+-----+-----+-----+|
+=======================================+
Perfect. Yes.
Writing this
type is messy in C and does not help with this exposition[1].
As per your note[1] then *(array_2d + 5) is of type int (*)[20]?
No, my note says that array_2d gets converted to a pointer of that
type. Adding 5 does not alter the type, but the * makes the
expression have type int [20].
This is the key step. Because this expression yields a value of array
type, it too gets converted -- to an int *. There is a problem here
that frustrates all people who teach C. You can't just write a bit of
C ask "what type is this"? You should really ask: "what type is the
object denoted by this?" or "what type does the result of this
expression have"? The two answers will be different if the "bit of C"
in question denotes an array.
*(array_2d + 5)
refers to an array -- the 6th sub-array inside array_2d. That "thing"
is of type int [20] but because this expression has an array type (and
there is no & or sizeof in sight) it is immediately converted to a
pointer to its first element -- an int. Thus the expression has a
value whose type is int *, but the expression also refers to an object
of type int [20].
Then what is type of array_2d + 5?
The same as per my note -- int (*)[20] -- and in this case there is no
ambiguity since the expression does not refer to or denote an object.
Adding a number to a pointer does not change the type. array_2d + i
for i = 0 to 10 are all valid and are all of type int (*)[20].
[Aside: you may be surprised by array_2d + 10, but C allows one to
construct a pointer to the element just past the end of an array. You
can't do much with it, but you can construct the pointer. C treats
single objects like arrays of length 1 for this purpose so given: 'int
i; int *ip = &i;' is it quite legal to calculate ip + 1.]
Now 5 gets added to that pointer. This arithmetic is done is units of
the size of the things being pointer to, in this case, 20-element int
arrays. Thus the result is a pointer that points at the 6th set of 20
ints.
[The standard does not describe it this way (in terms of sizes). It
simply says that adding /i/ to a pointer that points into an array
makes a pointer that points to the /i/th element. I described it my
way, because when that array is an array of arrays (as we have here)
the wording gets messy.]
This new pointer (array_2d + 5) has type "pointer to array of 20 int",
so applying the * to it results in a value whose type is "array of 20
int". Again, because this value is not used in one of the three
special situations, it is converted to a pointer to its first element:
in this case an int.
6 gets added to this int pointer, to give a pointer to the 7th
element in the 6th sub-array of array_2d. Applying the star, gives us
a modifiable lvalue that refers to this element (i.e. array_2d[5][6]
can be used like this: 'array_2d[5][6] = 42;' as well as like this:
'return array_2d[5][6] + 1;').
The tricky bit that array_2d[5] has array type. The value gets
converted to a pointer to array type and the arithmetic on the pointer
moves it about from one 20-element array to the next.
okay then what is the type of *(array_2d + 5) + 6? I think it is
int*?
Yes, it is of type int *. That is why the final * operation produces
an int -- the array element we wanted to get at.
Also what is type of just array_2d? is it int (*)[10][20]?
Not really. What you have written is the type a pointer to the whole
thing in other words that is the type of the expression &array_2d.
array_2d itself is an object of type int [10][20] -- it is not a
pointer at all but an array. Of course, my warning about writing a
bit of C and asking "what type is this" applies here. array_2d is the
name of an array. It is of type int [10][20], but the expression
array_2d, unadorned, has a value of type int (*)[20].
I say "unadorned" because the conversion only happen if the
array-valued expression is not the argument to & or sizeof[1]. That
is why &array_2d has type int (*)[10][20] and sizeof array_2d == 200 *
sizeof (int). In these contexts, array_2d remains unconverted and
refers to the whole array.
[Exactly the same happens with 1d arrays, of course. Given the
declaration 'int stats[20];' then stats is an object of type int [20]
and sizeof stats == 20 * sizeof (int). &stats is an expression whose
value is of type int (*)[20], but stats in other contexts gets
converted, so it is an expression whose value is of type int *.]
Thank you very much Ben. Im not able to easily understand 2D arrays like
1D ones, but i'll keep reading your notes until i figure it.
[1] There is a third exception -- string literals used to initialise a
character array also don't get converted -- but that just muddies the
explanation because it is such a specific case.