array initialization problem

Denis Koroskin 2korden at gmail.com
Fri Jan 16 23:03:26 PST 2009


On Sat, 17 Jan 2009 00:19:46 +0300, Qian Xu <quian.xu at stud.tu-ilmenau.de> wrote:

> Hi All,
>
> I have accidentally written a buggy class.
>
> Briefly described as follows:
>   1. The class contains a list of string
>   2. The list of string is assigned to a constant in constructor
>   3. Try to change the value of the list
>   4. Create another class by repeating step 1-3 again
>   5. Add both of them to a LinkSeq object
>   6. Print their values again
> Now you will find their lists have the same values now.
>
> Can someone explain, why the values are different before they are  
> inserted into a list?
> And why this.str has no problem?
>
>
> The console output and the source are included below.
>
> ##################### console output begin ############################
>
> list: [111,222,]
>   str: hello
> -----------------------------
> list: [333,444,]
>   str: world
> -----------------------------
> --- after insert ---
> -----------------------------
> list: [333,444,]
>   str: hello
> -----------------------------
> list: [333,444,]
>   str: world
> -----------------------------
>
> ##################### console output end ############################
>
> ######################### code begin ############################
>
> module test;
>
> import tango.io.Console;
> import tango.util.collection.LinkSeq;
>
> const char[][] CLIST = [null, null];
> const char[] CSTR = "hello";
>
> class Entity
> {
>    char[][] list;
>    char[] str;
>
>    this()
>    {
>      this.list = CLIST;
>      this.str = CSTR;
>    }
>
>    void print()
>    {
>      Cout.opCall("list: [");
>      foreach (char[] s; list)
>      {
>        Cout.opCall(s ~ ",");
>      }
>      Cout.opCall("]\n");
>      Cout.opCall(" str: "~this.str);
>      Cout.opCall("\n-----------------------------\n");
>    }
> }
>
> void main()
> {
>    Entity e = new Entity();
>    e.list[0] = "111";
>    e.list[1] = "222";
>    e.str = "hello";
>    e.print();
>
>    Entity e2 = new Entity();
>    e2.list[0] = "333";
>    e2.list[1] = "444";
>    e2.str = "world";
>    e2.print();
>
>    Cout.opCall("--- after insert ---\n-----------------------------\n");
>    LinkSeq!(Entity) l = new LinkSeq!(Entity)();
>    l.append(e);
>    l.append(e2);
>
>    foreach (Entity entity; l)
>    {
>      entity.print();
>    }
> }
>
> ######################### code end ############################
>
>

You have two instances of class Entity. Both point to the same variables - CLIST and CSTR.
Thus, modifying CSTR and CLIST variables' content would have an effect on e.str, e.list, e2.str and e.list, because they are sharing the data (as opposite to owning it).

For example, let's modify CSTR and see what happens:
CSTR[0] = 'J'; // now it is "Jello"

printing e.str and e2.str gives us the following output:
Jello
Jello

i.e. both strings have been changed, too! Once again, this happens because they don't own the data but share it with CSTR. It happens because arrays are not copied upon assignment, i.e. the following line:

this.str = CSTR;

makes sure that there is only 1 instance of "Hello" in memory, not three distinct copies (CSTR, e.str and e2.str). Therefore modifying either CSTR, e.str or e2.str would have an effect on all 3 variables. Here is a picture for you:


                   CSTR:
                      length = 5
                      ptr = ----------------  Hello
                                              /  |
                                             /   |
                                            /    |
                   e.str:                  /     |
                      length = 5          /      |
                      ptr = -------------*       |
                                                 |
                                                 |
                                                 |
                   e2.str:                       |
                      length = 5                 |
                      ptr = ---------------------*


If you want to be able to modify without affecting others, make a copy!

this.str = CSTR.dup;

This way memory will contain three copies of "Hello" - CSTR, e.str and e2.str .

I hope this is clear, let's move on.

Just like modifying e.str contents, modifying e.list contents will have an effect on all variables - CLIST, e.list and e2.list . That's what happens step by step:

0 - Program startup
State: CLIST : [null, null];
       e     : <doesn't exist>;
       e2    : <doesn't exist>

1 - Entity e = new Entity();
State: CLIST : [null, null];
       e     : list = [null, null]; str = "hello";
       e2    : <doesn't exist>
.
2 - e.list[0] = "111";
State: CLIST : ["111", null];   // note that CLIST has been changed, too!
       e     : list = ["111", null]; str = "hello";
       e2    : <doesn't exist>

3 - e.list[1] = "222";
State: CLIST : ["111", "222"];   // note that CLIST has been changed, too!
       e     : list = ["111", "222"]; str = "hello";
       e2    : <doesn't exist>

4 - Entity e2 = new Entity();
State: CLIST : ["111", "222"];
       e     : list = ["111", "222"]; str = "hello";
       e2    : list = ["111", "222"]; str = "hello"; // !!!

5 - e2.list[0] = "333";
State: CLIST : ["333", "222"];
       e     : list = ["333", "222"]; str = "hello";
       e2    : list = ["333", "222"]; str = "hello";

6 - e2.list[1] = "444";
State: CLIST : ["333", "444"];
       e     : list = ["333", "444"]; str = "hello";
       e2    : list = ["333", "444"]; str = "hello";

7 - e2.str = "world";
State: CLIST : ["333", "444"];
       e     : list = ["333", "444"]; str = "hello";
       e2    : list = ["333", "444"]; str = "world";


Hope it helps.


More information about the Digitalmars-d-learn mailing list