Will the prototype of a[1][2] be this: int **a?
a[1][2]
is expanded by compiler like this: *( *(a+1) +2 )
. So if a
has such a prototype:int **a
,
The foregoing expression should be explained like this:
Get the address of
a
from symbol table. Note it isa
pointer to a pointerNow we add it by
1
, then it point to the somewhere next to wherea
point to.Then we dereference it开发者_运维百科. I think here is a undefined behavior, for we don't know if
a+1
is valid and we arbitraryly access it.Ok, if we are lucky enough that we successfully get the value
*(a+1)
. We add this by2
.Upon this step, we dereference
(*(a+1) +2 )
. Will we be lucky now?
I read this in Expert C Programming in Chapter 10. Is this correct?
New answer, after edited question:
For a[1][2]
to be valid, given that a
has is defined as int **a;
, both of these must be true:
a
must point at the first of two sequentialint *
objects;- The second of those
int *
objects must point at the first of three sequentialint
objects.
The simplest way to arrange this is:
int x[3];
int *y[2] = { 0, x };
int **a = y;
Original answer:
If the expression a[1][2]
is valid, then there are many distinct possibilities for the type of a
(even neglecting qualifiers like const
):
type **a;
(pointer to pointer to type)type *a[n];
(array of n pointers to type)type (*a)[n];
(pointer to array of n type)type a[m][n];
(array of m arrays of n type)
Precisely how the expression is evaluated depends on which of these types a
actually has.
First a + 1
is calculated. If a
is itself a pointer (either case 1 or case 3), then the value of a
is directly loaded. If a
is an array (case 2 or case 4), then the address of the first element of a
is loaded (which is identical to the address of a
itself).
This pointer is now offset by 1 object of the type that it points to. In case 1 and case 2, it would be offset by 1 "pointer to type" object; in case 3 and case 4, it would be offset by 1 "array of n type" object, which is the same as ofsetting by n type objects.
The calculated (offset) pointer is now dereferenced. In cases 1 and 2, the result has type "pointer to type", in cases 3 and 4 the result has type "array of n type".
Next *(a + 1) + 2
is calculated. As in the first case, if *(a + 1)
is a pointer, then the value is used directly (this time, cases 1 and 2). If *(a + 1)
is an array (cases 3 and 4), then the address of the first element of that array is taken.
The resulting pointer (which, at this point, always has type "pointer to type") is now offset by 2 type objects. The final offset pointer is now dereferenced, and the type object is retrieved.
Let's say the definition of a
looks something like this:
int a[2][2] = {{1, 2}, {3, 4}};
Here's what the storage that the symbol a
looks like:
[ 1 ][ 2 ][ 3 ][ 4 ]
In C, when you perform arithmetic on a pointer, the actual amount by which the pointer value is incremented or decremented is based on the size of the type stored in the array. The type contained in the first dimension of a
is int[2]
, so when we ask C to calculate the pointer value (a + 1), it takes the location named by a
and increments it by the size of int[2]
, which results in pointer referring to the memory location containing the integer value [3]. So yes, when you dereference this pointer and then add 2 to it, the result is the integer value 5. When you then try to dereference that integer value, it makes no sense.
So now let's say the array contains pointers:
char const * one = "one",
two = "two",
three = "three",
four = "four";
char const * a[2][2] = {{one, two}, {three, four}};
Add 1 to a and then dereference it, and you get the char pointer referring to the string "three." Add two to this, and you'll get a pointer referring to a now shorter string "ree". Dereference that, and you get the char value 'r', but only by sheer luck did you avoid a memory protection fault.
精彩评论