www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why does reverse also flips my other dynamic array?

reply "Namal" <sotis22 mail.ru> writes:
import std.stdio;

void main(){

   int [] a = [1,2,3,4,5];
   int [] b = a;

	writeln(b, " ", a);
	a.reverse;
	writeln(b, " ", a);
}


I get:

[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
[5, 4, 3, 2, 1] [5, 4, 3, 2, 1]

Why is also b flipped here? This doesn't happen if I use static 
arrays.
Sep 12 2015
parent reply "Namal" <sotis22 mail.ru> writes:
 Why is also b flipped here? This doesn't happen if I use static 
 arrays.
nvm. I need to .dup that.
Sep 12 2015
parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Sat, 12 Sep 2015 10:55:50 +0000
schrieb "Namal" <sotis22 mail.ru>:

 Why is also b flipped here? This doesn't happen if I use static 
 arrays.
nvm. I need to .dup that.
Correct, static arrays are value types and copied on assignment. Dynamic arrays on the other hand are generally slices of memory on the garbage collected heap represented by just a pointer to the first element and a length. When you assign those you only get a second reference to the same data. Note that often the original dynamic array has additional capacity beyond its length. This can be used to ~= additional items without causing a reallocation, but is lost when you do the assignment "b = a". (This is so you can't accidentally append different items to both a and b and have them overwrite each other.) You can query the actual capacity with a.capacity. Just felt like writing down some knowledge you might need along the way. :) -- Marco
Sep 12 2015
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 09/12/2015 02:29 PM, Marco Leise wrote:

 Note that often the original dynamic array has additional
 capacity beyond its length. This can be used to ~= additional
 items without causing a reallocation, but is lost when you
 do the assignment "b = a".
Actually, the capacity is still there, useful to the runtime. Interestingly, the first dynamic array that is appended the new element becomes the owner of that capacity. The capacity of the other dynamic array becomes 0. import std.stdio; void main(){ int [] a = [1,2,3,4,5]; int [] b = a; writeln(a.ptr, " ", b.ptr); writeln(a.capacity, " ", b.capacity); a ~= 42; // <-- change to b, now b owns the capacity writeln(a.ptr, " ", b.ptr); writeln(a.capacity, " ", b.capacity); } Ali
Sep 12 2015
next sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Saturday, September 12, 2015 14:59:23 Ali Çehreli via Digitalmars-d-learn
wrote:
 On 09/12/2015 02:29 PM, Marco Leise wrote:

  > Note that often the original dynamic array has additional
  > capacity beyond its length. This can be used to ~= additional
  > items without causing a reallocation, but is lost when you
  > do the assignment "b = a".

 Actually, the capacity is still there, useful to the runtime.
 Interestingly, the first dynamic array that is appended the new element
 becomes the owner of that capacity. The capacity of the other dynamic
 array becomes 0.

 import std.stdio;

 void main(){

    int [] a = [1,2,3,4,5];
    int [] b = a;

    writeln(a.ptr, " ", b.ptr);
    writeln(a.capacity, " ", b.capacity);
    a ~= 42;    // <-- change to b, now b owns the capacity
    writeln(a.ptr, " ", b.ptr);
    writeln(a.capacity, " ", b.capacity);
 }
It's not really the case that the capacity is owned. There simply is no available memory passed the end of b, because what would be the next element in b if it were appended to is now the last element of a. It's the same boat you're in if you simply did int[] a = [1, 2, 3, 4, 5]; int[] b = a[0 .. $ - 1]; In both cases, there's no room for b to grow into, because a is using the space immediately after b. - Jonathan M Davis
Sep 13 2015
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 09/13/2015 08:21 AM, Jonathan M Davis via Digitalmars-d-learn wrote:
 On Saturday, September 12, 2015 14:59:23 Ali Çehreli via 
Digitalmars-d-learn wrote:
 On 09/12/2015 02:29 PM, Marco Leise wrote:

   > Note that often the original dynamic array has additional
   > capacity beyond its length. This can be used to ~= additional
   > items without causing a reallocation, but is lost when you
   > do the assignment "b = a".

 Actually, the capacity is still there, useful to the runtime.
 Interestingly, the first dynamic array that is appended the new element
 becomes the owner of that capacity. The capacity of the other dynamic
 array becomes 0.

 import std.stdio;

 void main(){

     int [] a = [1,2,3,4,5];
     int [] b = a;

     writeln(a.ptr, " ", b.ptr);
     writeln(a.capacity, " ", b.capacity);
     a ~= 42;    // <-- change to b, now b owns the capacity
     writeln(a.ptr, " ", b.ptr);
     writeln(a.capacity, " ", b.capacity);
 }
It's not really the case that the capacity is owned. There simply is no available memory passed the end of b, because what would be the next
element
 in b if it were appended to is now the last element of a. It's the 
same boat
 you're in if you simply did

 int[] a = [1, 2, 3, 4, 5];
 int[] b = a[0 .. $ - 1];

 In both cases, there's no room for b to grow into, because a is using the
 space immediately after b.

 - Jonathan M Davis
Thanks for clarifying. My point was about capacity not being lost; rather, being used (owned) by the _first_ slice that gets extended. If we append to 'a' first, then 'a' extends in place and 'b' gets relocated. If we append to 'b' first, then vice versa... Ali
Sep 13 2015
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, September 13, 2015 17:17:01 Ali Çehreli via Digitalmars-d-learn
wrote:
 On 09/13/2015 08:21 AM, Jonathan M Davis via Digitalmars-d-learn wrote:
  > On Saturday, September 12, 2015 14:59:23 Ali Çehreli via
 Digitalmars-d-learn wrote:
  >> On 09/12/2015 02:29 PM, Marco Leise wrote:
  >>
  >>   > Note that often the original dynamic array has additional
  >>   > capacity beyond its length. This can be used to ~= additional
  >>   > items without causing a reallocation, but is lost when you
  >>   > do the assignment "b = a".
  >>
  >> Actually, the capacity is still there, useful to the runtime.
  >> Interestingly, the first dynamic array that is appended the new element
  >> becomes the owner of that capacity. The capacity of the other dynamic
  >> array becomes 0.
  >>
  >> import std.stdio;
  >>
  >> void main(){
  >>
  >>     int [] a = [1,2,3,4,5];
  >>     int [] b = a;
  >>
  >>     writeln(a.ptr, " ", b.ptr);
  >>     writeln(a.capacity, " ", b.capacity);
  >>     a ~= 42;    // <-- change to b, now b owns the capacity
  >>     writeln(a.ptr, " ", b.ptr);
  >>     writeln(a.capacity, " ", b.capacity);
  >> }
  >
  > It's not really the case that the capacity is owned. There simply is no
  > available memory passed the end of b, because what would be the next
 element
  > in b if it were appended to is now the last element of a. It's the
 same boat
  > you're in if you simply did
  >
  > int[] a = [1, 2, 3, 4, 5];
  > int[] b = a[0 .. $ - 1];
  >
  > In both cases, there's no room for b to grow into, because a is using the
  > space immediately after b.
  >
  > - Jonathan M Davis

 Thanks for clarifying. My point was about capacity not being lost;
 rather, being used (owned) by the _first_ slice that gets extended. If
 we append to 'a' first, then 'a' extends in place and 'b' gets
 relocated. If we append to 'b' first, then vice versa...
Yeah. Only dynamic arrays whose last element refers to the last element that has been expanded into in a GC-allocated memory buffer can be appended to without reallocating. If the memory block wasn't specifically allocated by the GC for dynamic arrays, then there won't be any memory to expand into, and even if it is a GC-allocated buffer for dynamic arrays (as is usually the case), if another dynamic array has ever referred to any elements past the end of the dynamic array you're looking at, then that dynamic array can't be appended to without being reallocated, because it would potentially stomp on another dynamic array. So, if you have multiple dynamic arrays referring to the same memory, only one of them can expand into the unused memory past their ends. Anything else would allow stomping. The result of this is that if you're doing a lot of appending, you probably don't want to be doing a lot of slicing unless the slices aren't being appended to. It'll still work if they're appended to, but pretty quickly it'll mean that most of the appending that you're doing is causing reallocations, which could really harm performance. - Jonathan M Davis
Sep 13 2015
prev sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Thanks for the clarification.
Sep 14 2015