Am 15.03.2011 21:14, schrieb Paul:
Peter Remmers said:
Am 15.03.2011 19:17, schrieb Paul:
The important, practical consequence is that array size information is
lost as soon as the array identifier is converted into a pointer. You
can't even get it back when the information is still available to the
compiler:
int arr[] = {1,2,3};
int * p = arr;
sizeof(arr) == 12
sizeof(p) == 4
With your interpretation a dynamic array is not an array, which is
obvioulsy
incorrect.
An array created by 'new' is an array that has no name. Just like with
everything that is dynamically created by new, there is no identifier that
directly identifies the object created on the heap. All you get is a
pointer to the object which must be stored in a pointer variable that you
have to provide. That pointer is not the array itself. It is a different
object, with its own identifier and its own region of storage. This is
obvious if you consider what sizeof() returns for the two.
Consider this:
new int[3];
This statement does not contain any identifiers, and yet an array has been
created on the heap. And because the pointer that new returned was not
saved, the array is leaked. There is just no way to get hold of the array
without an identifier. A pointer only gives you indirect access to the
array.
The pointer is the arrays identifier.
No, it's not. An identifier is just a string of characters in the source
code - a compile-time entity. The pointer value returned by new is a
run-time entity. This value is stored in a pointer variable, which has
an identifier, an address (possibly in a completely different storage
type as the array), and a value (the address of the array's first
element) which is of course different from its own address.
int arr[10];
int *p1 = &arr[0];
int *p2 = new int[10];
Here, p1 and p2 are completely equivalent in that they both are pointers
to int, and that they both point to the first element of a (each a
different) array of 10 ints. The difference is the way the respective
array has been created. p1 points to an array in automatic storage. p2
points to an array in dynamic storage. The array p1 points to has a name
- embodied by the identifier "arr". p2's array does not have a name and
so there is no identifier.
What do you mean by , " it only gives indirect access"?
It's the only way to access the array, at all.
Im the example above, p1 and p2 only provide indirect access because
they are distinct objects of pointer-type that only point to an array,
but they are not the array itself. In the case of p1, "p1" gives
indirect access, "arr" is direct access. For p2, there is no direct
access because the array does not have a name, and so p2 is the only
(indirect) means of access.
If I have a piece of paper with a picture of Paul on it, would you say
the paper *is* Paul himself? It is just a picture of Paul. The
4-character-string "Paul" is the identifier that represents the name of
Paul. If I name my piece of paper "Judy", "Judy" is an identifier that
represents the name of the Picture. "Judy" certainly is not an
identifier for Paul, and the piece of paper is even less so.
If I give you a picture of a person you don't know, then to you, that
person does not have a name, and you can not directly refer to that
person by their name, but only indirectly as "the person on that
picture" ("the array that the pointer points to"). If I happen to know
that the person's name is "Jerry", I can say "Jerry's hair is black",
while you can only say "The color of the hair of the person on that
picture is black".
What you mean is the difference between an array-type and pointer-type,
can't be more obvious.
If two objects have a different type, they can't be the same object...
nor can one object be the identifier of the other.
I think what you are trying to explain is the following:
#include<iostream>
typedef int t_arr16[16];
void foo1(int* par){
std::cout<< "sizeof:"<< sizeof(par)<< "\t typeinfo:"<< typeid(par).name();}
void foo2(t_arr16& par){
std::cout<< "sizeof:"<< sizeof(par)<< "\t typeinfo:"<< typeid(par).name();}
int main(){
t_arr16 arr1 ={0};
std::cout<< "Calling foo1: ";
foo1(arr1);
std::cout<< std::endl<< "Calling foo2: ";
foo2(arr1);
}
That works because an array variable can be implicitly converted to a
pointer to its element type.
Try this:
int *p = &arr1[0]; // or int *p = arr1;
foo1(p); // OK
foo2(p); // error
The output I get for
foo1(arr1); // implicit conversion from int[16] to int*
foo2(arr1); // passed as reference
foo1(p); // p is already int*
//foo2(p) error: int* cannot be converted to int(&)[16]
is this:
sizeof:4 typeinfo
i
sizeof:64 typeinfo:A16_i
sizeof:4 typeinfo
i
Different sizes, different types, different objects.
Of course, foo2's "par" is just an alias for the array and not the array
itself, because it is a reference type - for which we now that the
compiler most likely just uses a masqueraded pointer. It does not
actually pass those 10 integers by value.
This does not mean the following is not an array:
int* arr1 = new int[16];
++arr1;
It means that arr1 is a pointer, initialized with the address of the
first element of some unnamed integer array on the heap, and then its
value is changed to be the address of the second element of that array.
And it doesn't suggest an array must always have zero based indexing. The
C++ standard specifically states the rules about array indexing and its
clear that C++ allows non-zero based indexing.
What possible reason, even in the most idiotic minds, would there be to
restrict the language in such a way?
int arr1[10];
If you access the array's elements via the "arr1" identifier, you can't
use a negative index because that would be UB because your access would
be out of range. That's because "arr1" can only ever be converted to a
pointer to the *first* element (the one with the index zero) of the
array. arr1 connot be assigned to point to something else, say the
second element, because it *is not* a pointer whose value can be
changed. It can conly be *converted* to a pointer.
int *p = &arr1[1];
If you create a pointer variable, you can have it point anywhere you
want, for example somewhere in the middle of arr1. You can then use
positive or negative indices as offset to that pointer base, as long as
you don't try to access something that is not backed up by some object's
storage. Of course, interpreting the data you read from that address as
an int may still be UB.
Playing word games can work both ways, example:
We're not playing word games. The terms "identifier", "name", "object",
and "array" etc. are clearly defined by the standard.
int x;
The actual integer object is just a a piece of memory, x is just an
identifier. So are we wrong to say x is an integer? Likewise:
int* arr = new int[16];
The actual array object is just a piece of memory, arr is just an
identifier. So are we wrong to say arr is an array?
I agree that in a programmer's everyday language, one could say "I
create a dynamic array 'arr' of 16 integers". But that does not mean
that you can close your eyes to the fact that formally, and in reality,
"arr" is just a pointer to an integer that is initialized to point to
the first integer element of some unnamed array in dynamic storage. It's
just that the formal laguange is too verbose to use it all day long. So
we use the formally incorrect short forms, where everybody knows what is
meant by it, but where also everybody knows that it is not completely
correct.
It's up to you how you want to look at it, I think my views are quite clear,
and I'm happy with my view on the subject.
I agree that you could say "foo::bar() is a member function of xyz",
where xyz is some object of class-type foo. I use such informal speech
myself, and I admit that you can get pretty far with such a point of
view. But I do keep in mind that formally, foo() is a member of xyz's
class, because that's what it really is behind the scenes. And if it
comes to corner cases, and you don't have to go very far, these details
become apparent.
Peter