Paul said:
Paul said:
So - do we agree that an array is a contigous set of like-typed
elements,
where the elements are accessed via the []-operator?
Yes I aggree with that.
So - next let's examine a rather simple declaration of an array
in C/C++.
int arr[5];
No do a dynamic array like so:
int* arr = new int[5];
Then there is no confusion about array objects, an array is simply
the region of memory storing the integer objects and nothing more.
Having read ahead you seem to be slowly introducing this "arr"
object into the equation without first aggreeing on a definition
of what exactly "arr" is. And that is what was the original argument.
In the above example of a dynamic array there is no confusion that
"arr" is a pointer.
You are correct - what you have declared there is a pointer to an
integer,
(the `int *arr' portion), and that pointer is initialized with
some dynamically
allocated space (the `new' portion.) The dynamically allocated
space is a
contiguous set of 5 'int' elements (the `int[5]' portion.) The
pointer's name is 'arr'.
That's all well and good, but it is very different from what I was
talking about.
What I'd like to focus on is this simple declaration:
int arr[5];
Can we agree that the declaration of
int arr[5];
represents, at runtime, a set of 5 contiguous 'int' elements. The
name of the set of 5 contiguous elements it 'arr'. The name 'arr'
refers to the runtime location of the set of 5 contiguous elements.
If we can, I'd like to restrict the conversation to just that for the
moment... we can get to the other stuff soon enough... but, one
thing at a time.
Yes I agree with this, but before we can proceed any further we need
to agree on what exactly "arr" is.
You are calling it a name that refers to the contiguous sequence of
elements here.
I would like to establish just what exactly it is because it seems
to store a memory address as can be show with:
int arr[5];
int* p = arr;
So lets try to agree about what exactly arr is.
As you know I have been calling it an array type object.
That would basically be the next step...
However, I want to hold to the definition; 'arr' is simply the name
of the 5 contiquous int elements.
Now - let's take up the problem of assignment to a pointer...
Let's consider this snippet:
int i;
int *ip;
ip = &i;
would you agree that the semantics of that would be that
'i' is the name of an `int'. `ip' is the name of a pointer to an `int'.
The expression:
&i
denotes "the address of the `int' named `i'"
And the expression:
ip = &i;
denotes "compute the address of the `int' named 'i', store that
value into the pointer-to-`int' named `ip'."
Does that sound correct?
If so, then we can begin to look at your example involving `arr';
and - we can compare the two with considerations toward just
what it means to assign a value to the `int' pointer named `ip'.
Yes this is agreeable. You may continue to the next step
OK - let's review - so we know we're on the same page:
int arr[5];
'arr' is the name of the set of 5 contiguous `int'-typed elements.
int *ip;
`ip' is the name of a variable that can contain the address of an `int',
it is a "pointer to int".
And:
int i;
`i' is the name of an `int'-tpe variable.
The expression:
ip=&i;
computes the address of `i', and places that value into the variable
`ip'.
This is what we've agreed to thus far... right?
This establishes that `ip' is not the same type as `arr'.
`arr' is the contiguous set of 5 `int' elements, `ip' can contain
the address of an `int', it can "point to" an `int'.
There is nothing new in that statement; it is a re-statement
of the preceeding items we've agreed to (in that `arr' is the
set, `ip' is a single pointer.)
Now - in a programming language like C/C++, typically the
language might enforce rules regarding assignent. One such
rule is that assignments can only be made from a value of the
destination type, and that if the types are not the same, then
some mechanism is employed to convert from oen type to another
(if possible).
As an example:
double d;
int i;
d = i;
The variable `i' is an `int'; `d' is a `double'. You might decide,
in a programming language, that such an assignment not be
allowed - but it is in C/C++ because the `int' value contained
in `i' will be, at runtime, converted to a `double' and then
stored in the variable `d'.
In this instance, would you say there was a "magic"/secret object
of type `double' that was created along with `i' to contain the
converted result before assigning the value to `d'? Or, would
you say that the conversion simply occurs as a matter-of-course?
I think, most people would say that the compiler generates code
which effects the conversion at runtime, so that `i' is, at runtime,
converted to a double, and the temporary value that results from
that conversion is stored in `d'.
But; this is a well-defined conversion... does the C/C++ language
definition provide any such mechanism for this snippet:
int *ip;
int arr[5];
ip = arr;
Given the definitions (to which we've all agreed), `ip' is a pointer
to an `int', and `arr' is a contigous set of 5 `int' elements. How could
those two types be reconciled to allow this assignment???
What is going on here??
As it so happens, the designers of C (and, by extension, C++) considered
this problem, and provide a mechanism by which such an assignment
is accomplished.
This mechanism has been written down in the various standard documents,
so that everyone can read it and truly understand what is meant by
that assignment.
First - recall, as you have agreed, that arr' is an array of 5 `int'
elements.
(we clearly cannot assign 5 `int' elements in-toto to a pointer to a
single `int',
in a reasonable type-system, at least.)
So - given that `arr' is an array, I will quote from the C standard,
which might
be a little more clear than the C++ one (simply for this discussion, the C++
definition is similar):
Section 6.3.2.1 Lvalues, arrays, and function designators
Paragraph #1:
An lvalue is an expression with an object type or an incomplete
type other than void.
(that is, `arr' is an "lvalue", it is an "lvalue" with an object type
of int[5].
An "lvalue" in C is basically what can appear on the left-hand-side of the
assignment operator, the opposing "rvalue" appears on the right-hand-side.
That is, you assign the rvalue to the lvalue. In this case, `arr' is
an lvalue,
until you get to the next step.)
Paragraph #3:
Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression
that has type "array of type" is converted to an expression
"pointer to type"
that points to the initial element of the array object and it is
not an lvalue.
In this example, `arr' is not the operand of sizeof(), nor the unary &,
an it
is not a string literal to initialize an array. Thus, the conversion
applies.
So... what do we have in this:
int *ip;
int arr[5];
ip = arr;
Let's take the expression 'arr', which is the value being assigned.
According to the rules, it is an "lvalue".. it's type (as we agreed) is
an array of 5 int elements. When such an expression is used, the
language definition indicates that it should be converted to an
expression that is a pointer to the element type. The value
of that expression is a pointer to the first element. After that,
the expression is no longer an "lvalue" (it can't be assigned to,
hence it is not `modifiable'). [As an aside, the effect of not being
an "lvalue" is that it can't appear on the left-hand-side of the assignment
operator, thus you cannot have: arr = <something>; because 'arr'
would be converted to the non-lvalue expression.]
So, following that logic; the semantics of the expression `arr',
are completely the same as if this was this expression:
ip = (&arr[0]);
Do you not agree that the expression "&arr[0]" is the address of
the first element of `arr'?
Clearly the type of "&arr[0]" (the address of the first `int' element
of `arr') is the address of a single `int'.
Thus, the assignment is valid, and the program is valid.
Now. did the C/C++ language definition provide a separate object to compute
"&arr[0]"?
I believe, we agreed before that there was no separate object
in this snippet:
int *ip;
int i;
ip = &i;
I recall we both agreed that the semantics of that were that the
address of `i' was computed, and that value was placed in `ip'.
Similarly, this snippet:
int *ip;
int arr[5];
ip = (&arr[0]);
would simply compute the address of the first (0th) element of `arr'
and place that value in `ip'. There would be no separate object
required to be a pointer to the array... there is simply the array
and the fact that we can compute its address when needed.
And, because of the rule I cited above, this snippet:
int *ip;
int arr[5];
ip = arr;
is equivalent to the previous one (in that `arr' is an lvalue expression of
an array type, and it is therefor converted to a pointer to the array's
first element.)
So, you see, there is no separate "object" of any kind defined by the
language,
nor required in the implementation. It's simply a matter of following
the rules
set out in the C/C++ definition.
If you agree with all of this, then I'd like to discuss how the
[]-operator (indexing)
is defined... it simply "falls out" from this very simple approach.
And, it
is frequently why people confuse pointers-and-arrays.
To recap:
1) An array is a contigous set of same-typed elements.
2) A pointer to a variable that contains an address.
3) A pointer is not an array, an array is not a pointer.
4) The C/C++ languages provide for situations where implicit
conversions occur.
5) One such situation is that when an expression is an lvalue,
and the type of that expression is an array; the expression
is converted to an expression that is a pointer to the first
element of the array.
So - you see - it's really rather simple, no actual "magic" behind
this at all.. no magic 'array type object' or anything like that.
Just some basic definitions on which to build...
If you agre with all of this, then, as I mentioned above, I'd like to
explore how the indexing operator [] works; especially its relationship
to pointer arithmetic. And, we should explore the caveats mentioned
in the conversion rule (particularly, the & operator when applied to
an expression of array type.)
- Dave Rivers -