Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++ array created with new behave differently to C style array?

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!

like image 295
Ross Avatar asked Aug 01 '14 12:08

Ross


People also ask

What is the difference between arr and &arr?

arr is an integer pointer (int*) which points the first element of the array. &arr is an integer array pointer (int*)[5] which points the whole array. (all five elements.) &arr is a pointer to an entire array.

Are C style arrays faster than vectors?

A std::vector can never be faster than an array, as it has (a pointer to the first element of) an array as one of its data members. But the difference in run-time speed is slim and absent in any non-trivial program. One reason for this myth to persist, are examples that compare raw arrays with mis-used std::vectors.

Can an array in C have different types?

Array in C are of two types; Single dimensional arrays and Multidimensional arrays. Single Dimensional Arrays: Single dimensional array or 1-D array is the simplest form of arrays that can be found in C. This type of array consists of elements of similar types and these elements can be accessed through their indices.

Is arr [] same as * ARR in C?

To summarize, arr[i] is same as (*(arr + i)), as compiler uses pointer arithmetic to access array elements.


2 Answers

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.

like image 95
Manu343726 Avatar answered Sep 27 '22 17:09

Manu343726


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.

like image 26
Yakk - Adam Nevraumont Avatar answered Sep 27 '22 17:09

Yakk - Adam Nevraumont