First, based on your declaration, argstr
is laid out in memory as follows:
+---+
argstr: |'a'| argstr[0][0]
+---+
|'b'| argstr[0][1]
+---+
|'c'| argstr[0][2]
+---+
...
+---+
|'f'| argstr[0][5]
+---+
| 0 | argstr[0][6]
+---+
...
+---+
| ? | argstr[0][9]
+---+
|'D'| argstr[1][0]
+---+
|'r'| argstr[1][1]
+---+
...
+---+
| ? | argstr[1][9]
+---+
|'s'| argstr[2][0]
+---+
...
+---+
| ? | argstr[4][9]
+---+
All 50 array elements are laid out sequentially in row-major order (that is, all of the elements of the first row, followed by all elements of the second row, etc.). Note that no storage has been set aside for any pointers anywhere. This means that the address of the first element of the first subarray (&argstr[0][0]
) is the same as the address of the first element of the array (&argstr[0]
) which is the same as the address of the array (&argstr
).
Except when it is the operand of the sizeof
or unary &
operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T
" will be converted ("decay") to an expression of type "pointer to T
", and the value of the expression will be the address of the first element of the array.
In your printf
call, the expression argstr
has type "5-element array of 10-element array of TCHAR
". Since it's not the operand of the sizeof
or unary &
operators, it is converted to an expression of type "pointer to 10-element array of TCHAR
", or TCHAR (*)[10]
, and the value of the expression is the address of the first element (&argstr[0]
).
In the same call, the expression argstr[0]
has type "10-element array of TCHAR
". Since it's not the operand of the sizeof
or unary &
operators, it is converted to an expression of type "pointer to TCHAR
", or TCHAR *
, and the value of the expression is the address of the first element (&argstr[0][0]
).
What the hell? Why does the type of an array expression change into a pointer?
The array subscript operator []
is defined in terms of pointer arithmetic: the expression a[i]
is defined as *(a + i)
. Given an address a
, offset i
elements (not bytes) from that address and dereference the result. This is a carryover from the B programming language from which C was derived. In B, a separate pointer was created as part of an array definition; that pointer would be bound to the array name and point to the first element of the array, so in B &argstr
and &argstr[0]
would indeed be different.
Ritchie got rid of that array pointer when he designed C, but he wanted to keep B's array semantics, so he created the array conversion rule above. In an expression like argstr[i][j]
, the subexpression argstr
is first converted to a pointer value, which is subscripted with [i]
; that gives us another array expression, which is converted to a new pointer expression, which is subscripted with [j]
.
Summing up, given the declaration TCHAR argstr[5][10]
, all of the following are true:
Expression Type Decays to Value
---------- ---- --------- -----
argstr TCHAR [5][10] TCHAR (*)[10] Address of argstr[0]
&argstr TCHAR (*)[5][10] n/a Address of argstr
*argstr TCHAR [10] TCHAR * Value of argstr[0]
argstr[i] TCHAR [10] TCHAR * Address of argstr[i][0]
&argstr[i] TCHAR (*)[10] n/a Address of argstr[i]
*argstr[i] TCHAR n/a Value of argstr[i][0]
argstr[i][j] TCHAR n/a Value of argstr[i][j]
&argstr[i][j] TCHAR * n/a Address of argstr[i][j]
argstr
, &argstr
, *argstr
, argstr[0]
, &argstr[0]
, and &argstr[0][0]
all yield the same value (the address of the first element of the array), but the types of the expressions are different.