Reputation: 472
I'm teaching myself C++, and as such have been writing some example code to really nail my understanding of pointers and arrays.
I have written this:
int myints[] = {20, 40, 60, 80, 100};
// C style array? should be stored on stack? is myint's type pointer to int or an array of int? how does it differ from myotherints?
int* myotherints = new int[5]{20, 40, 60, 80, 100}; // new always returns pointer, is this a C++ style array?
// does this pointer get created on stack while the elements themselves are created in free heap?
int j = 5; // should be stored on stack
cout << "myints: " << myints << endl; // decays to pointer, shows address array myints is stored at
cout << "*myints: " << *myints << endl; // myints decays to pointer and is dereferenced to return value stored at start of array myints
cout << "myints[0]: " << myints[0] << endl; // [] dereferences and returns value for element 0 (20)
cout << "myotherints: " << myotherints << endl; // some value?? this is totally unlike the others, why? what is this?
cout << "*myotherints: " << *myotherints << endl; // dereferences pointer myotherints to get address that holds value 20 for first element
cout << "myotherints[0]: " << myotherints[0] << endl; // [] dereferences pointer to get address that holds value 20 for first element
cout << "j: " << j << endl << endl; // 5, sure
cout << "&myints: " << &myints << endl; // array behaving as pointer, gives address of myints[0]
cout << "&myints[0]: " << &myints[0] << endl; // array behaving as pointer, gives address of myints[0]
cout << "&myotherints: " << &myotherints << endl; // address of myotherints, is this where the pointer to the array is stored?
cout << "&myotherints[0]: " << &myotherints[0] << endl; // [] dereferences the pointer that myotherints points to and returns element 0
cout << "&j: " << &j << endl; // address of j
/*
myints: 0x7fff096df830 <-- this makes sense to me, array decays to pointer to give first element address
*myints: 20 <-- this makes sense to me, dereference first element address for value
myints[0]: 20 <-- [] dereferences implicitly, returns value from pointer
myotherints: 0x2308010 <-- myotherints is a pointer to an array of ints, but its address is much lower compared to myints and j, why is that?
*myotherints: 20 <-- getting the value from above address returns 20
myotherints[0]: 20 <-- [] dereferences to address pointed to by pointer myotherints, returns value
j: 5
&myints: 0x7fff096df830 <-- same as below
&myints[0]: 0x7fff096df830 <-- same as above, meaning *myints and myints[0] are the same thing, this address
&myotherints: 0x7fff096df828 <-- how can the pointer to myotherints array be stored here when dereferencing it (*) returns 20 and...
&myotherints[0]: 0x2308010 <-- dereferencing this address with [] also returns 20, yet they are different memory locations unlike myints
&j: 0x7fff096df824
*/
Is it true to say that myints is a "C style array" while myotherints is a "C++ style array"?
If I'm understanding correctly, myotherints is a pointer yet myints is an array that most of the time behaves like a pointer? So while you can do pointerish things with myints, there are times when it does not behave like a pointer, namely using & to reveal its address. This means myints is of a different type to a pointer. Is its type "array of ints"?
Where is myints (the thing that is myints, not the values in its array) stored, and how can I reveal its address if it always automatically dereferences to the location the array is stored at unlike the pointer returned with the C++ style new array?
Are these represented in memory in a functionally different way?
Any tips or directions to documentation that can really solidify my understanding here would be much appreciated. Thank you!
Upvotes: 7
Views: 911
Reputation: 308
It sounds like you have questions about pointers v arrays more than anything, Ill try and go through and hit all of your questions though,
int myints[] = {20,40,60,80,100};
C style array?
Well yes that is how you declare a C style array
is this on the stack?
Yes the entire array (all 5 variables) is located on the stack, this means that when its block goes out of scope the data itself goes out of scope as well, being "freed" by moving the stack pointer, this does not mean you cannot make a pointer to this array for use in other functions using the & operator though.
how is this different from myOtherInts?
This is a value on the stack, whereas myotherints is a value on the heap, with a pointer on the stack.
int* myOtherInts = new int[]{20,40,60,80,100};
does this pointer get created on stack while the elements themselves are created in free heap?
Yes declaring an int*
means that you are declaring a pointer on the stack (the pointer not the value is created on the stack) and when you assign that variable to what the new
function returns, (which should be memory allocated on the heap which must be freed later) you then have a stack stored pointer to a heap stored value.
cout << "&myotherints: " << &myotherints << endl;
address of myotherints, is this where the pointer to the array is stored?
Yes this returns the address of which location in memory holds the pointer to your actual data which is different than where your data is actually stored.
//myotherints: 0x2308010 <-- myotherints is a pointer to an array of ints, but its address is much lower compared to myints and j, why is that?
the value you are printing here is the address where your data is stored which is different than the location that pointer is stored in. The reason why this value is much lower is because this address that this memory resides on is controlled by the heap, which typically holds the lower address spaces of the program.
&myotherints: 0x7fff096df828 <-- how can the pointer to myotherints array be stored here when dereferencing it (*) returns 20 and...
&myotherints[0]: 0x2308010 <-- dereferencing this address with [] also returns 20, yet they are different memory locations unlike myints
This ones a little bit tricky. Recall that the & operator means "give me the address of" so to translate your code down to english a little bit you have
give me the address of myotherints
recall that myotherints is a pointer that you allocated on the stack, however this pointer points to memory on the heap. so you should expect to get a value that looks like it came from the stack (ie, generally a higher value)
give me the address of the first element of myotherints
now, again myotherints
is a pointer to a value on the heap, and when you dereference it with [0]
you get the value of your first element, which is stored on the heap, so when you ask for the address of something stored on the heap, you should expect a different result from something stored on the stack. (ie. a rather low number representing heap data)
EDIT: for the truly C++ way to allocate arrays you should probably be using std::array
see Manu's answer
Upvotes: 4
Reputation: 275740
C++ got type decay from C.
In C there are few ways to usefully use an entire array. You cannot pass them to functions, return them from functions, perform []
or ==
or +
or almost anything on them, at least directly.
Instead the array 'decays' to a pointer to its first element whenever you, well, look at it funny. (basically, whenever you use it in all but the few situations where it is treated as an actual array, it decays into a pointer).
So arr[3]
becomes (&(first element of arr))[3]
or *((&(first element of arr))+3)
(these mean the same thing).
Similar thing happen when you return arr;
or pass it to a function (in C). Your cout << arr
means cout.operator<<( arr )
, which is just a function. Well, it could also be operator<<( cout, arr )
. In C++, you can pass a reference to an actual array to a function, but it takes a bit of work and is not happening in your example code.
If you type &arr
decay does not occur, and you get a pointer to the entire array. This matters because of pointer arithmetic, among other reasons, and is how arrays-of-arrays can work with zero overhead. (&arr)+1
points to past-the-end of the array, no matter how big it is -- ((&(arr[0]))+1)
points to the second element of the array.
This is how int arr[3]={4,5,6};
works, or int arr[]={4,5,6};
(same thing -- second one just determines the number 3
for you). arr
in both cases is of type int[3]
. It decays to int*
easily, but it is of type int[3]
. sizeof(arr)
is three times the sizeof(int)
, not sizeof(int*)
.
When you new int[3]
, you do not get a pointer to int[3]
, instead you get a pointer the first element of the int[3]
array. In a sense, it gets pre-decayed. This is the type of the pointer -- the address of the array and first element is the same. But type information (which is a complie time concept) differs!
It is also stored on the free store, and not automatic storage (aka the heap and stack repectively). But that is not the fundamental difference.
The only thing C++ about the new int[3]
was that you used new
-- you can use malloc
to get data space on the free store, and get a pointer to the first element as an int*
in C as well.
Upvotes: 6
Reputation: 14174
Both are things inherited from C (The proper C++ array thing is std::array
), but you are confusing things:
The first is a C array, that is, a thing with static/automatic storage duration which represents a block of memory. The size and "position" (Address) of that block is determined at compile-time.
The second is a pointer to a dynamically allocated memory block. In other words, you use a pointer to store the address of the memory block you requested to the OS. This is confusing for novices since this thing is sometimes called dynamic array. Is not an array in the same sense a C array is, but in practice we use both in the same way, but for different purposes.
Regarding C++:
C arrays behave like pointers to the memory block with some suggar (Array indexing, etc), so they are always passed by reference (Since which we have is the address passed by value, not the array itself), and in fact they decay automatically into pointers in many situations. These issues make std::array
a much better alternative, since it has correct value semantics and no implicit decaying.
In C++ manual memory management should be avoided, you should use the containers the Standard Library provides (The best well known std::vector
). The C++ language provides features for automatic, deterministic, and safe resource management; memory resources included. You should use these. Manual memory management is provided only for very low-level programming and creation of your own resource management handler.
Upvotes: 10
Reputation: 1447
new
operator, in fact, is a special kind of method and in a nutshell it requests operating system to give some free memory and returns address of newly allocated memory. So, it returns standard memory address. myints
is a mere address in a nutshell. Memory to store it is not allocated on the stack. You can perform basic pointer algebra on it but you can't modify it's address. If you're familiar with asm, you may think of myints
as a simple label. In C and C++ you can indeed define labels in your methods like this:
...
some_label:
/* some code here*/
goto some_label;
The compiler shall generate code that shall instruct processor to use some kind of jump instruction. Some jmp stack_pointer + some_label
. Likewise, trying to modify myints
fourth value number instructs compiler to generate call similar to previous example like "write this value on address stack_pointer + myints + 4 * sizeof(member of myints)
" or something like that.
Upvotes: 1