Jump to page: 1 2 3
Thread overview
Address of a class object
Jan 01, 2023
Paul
Jan 01, 2023
H. S. Teoh
Jan 01, 2023
Ali Çehreli
Jan 01, 2023
Paul
Jan 01, 2023
matheus
Jan 02, 2023
Paul
Jan 04, 2023
Paul
Jan 04, 2023
Ali Çehreli
Jan 04, 2023
H. S. Teoh
Jan 04, 2023
Ali Çehreli
Jan 04, 2023
Ali Çehreli
Jan 04, 2023
Ali Çehreli
Jan 04, 2023
H. S. Teoh
Jan 04, 2023
Ali Çehreli
Jan 05, 2023
Paul
Jan 05, 2023
Ali Çehreli
Jan 05, 2023
Paul
Feb 01, 2023
Paul
Jan 05, 2023
H. S. Teoh
Jan 01, 2023
Ali Çehreli
January 01, 2023

Hello. Thanks for any assistance.

Can I acquire the address of a class object, not a class variable (i.e. the instantiations of the class) but the object definition itself?

class MyClass {char c}
...
MyClass MyClassVar;

writeln(&MyClassVar); // this compiles
writeln(&MyClass);    // this does not
December 31, 2022
On Sun, Jan 01, 2023 at 12:35:40AM +0000, Paul via Digitalmars-d-learn wrote:
> Hello.  Thanks for any assistance.
> 
> Can I acquire the address of a class object, not a class variable (i.e. the instantiations of the class) but the object definition itself?
> 
> ```d
> class MyClass {char c}
> ...
> MyClass MyClassVar;
> 
> writeln(&MyClassVar); // this compiles
> writeln(&MyClass);    // this does not
> ```

What's your purpose in doing this?  Maybe if you explain what you're trying to accomplish, we can better understand how to help you.

The class definition does not have an address, because it's an abstract definition that only exists during compilation.  At runtime the class definition isn't stored anywhere. So it isn't possible to take its address.

There is, however, typeid(MyClass), which is a runtime structure that gives you some amount of information about the class. But the use of typeid is discouraged due to some inherent issues with its design.


T

-- 
Designer clothes: how to cover less by paying more.
December 31, 2022
On 12/31/22 16:35, Paul wrote:

> Can I acquire the address of a class object,

Answering that question literally, yes, you can by casting the class variable to void*. But note: 'class object' means the instance of class in D.

> not a class variable (i.e.
> the instantiations of the class)

D terminology is different there: A class variable is a reference to the class object (that carries the member variables, etc. of a class instance.)

  auto c = new C();

'c' is the *variable*, providing access to the *object* in dynamic memory.

> but the object definition itself?

As H. S. Teoh answered, Python etc. can do that but not D's compilation model.

The following program tries to demonstrate that the members are offset two void* sizes further from the address of the object:

class C {
    int i;
}

void main() {
    auto c = new C();

    const likelyOffsetOfMembers = 2 * (void*).sizeof;

    // HERE:
    const objectAddrInDynamicMemory = cast(void*)c;

    assert(objectAddrInDynamicMemory + likelyOffsetOfMembers == &c.i);
}

If the class is defined as extern(C++), then you must replace 2 above with 1:

extern(C++) class C {
    int i;
}

Ali

January 01, 2023

Thanks all. Yes it seems my understanding and "D" vocabulary are still a bit confused.

So I'm taking a D course online and was trying to verify what I was learning. The course example printed out the size and alignment of types...something close to this:

import std.stdio;
import std.traits;

class MyClass {char c;}

void main() {
    writeln(" Size  Alignment  Type\n",
            "=========================");

        size_t size = __traits(classInstanceSize, MyClass);
        size_t alignment = classInstanceAlignment!MyClass;

    writefln("%4s%8s      %s",size, alignment, MyClass.stringof);

	// my test code added
	MyClass MyClassO1;
	MyClass	MyClassO2;
	writeln("\n",&MyClassO1,"\t",&MyClassO2);
}

...on my laptop it prints...

 Size  Alignment  Type
=========================
   9       4      MyClass

4FFB20  4FFB24

If the size of MyClass is 9 bytes why do MyClassO1 & O2 addresses only differ by 4 bytes?

Because those addresses(4FFB20 4FFB24) are the addresses of the class variables, not the addresses of the objects themselves?

So, I guess my question is actually how do I print out the addresses of the MyClassO1 & O2 objects themselves?

January 01, 2023
On Sunday, 1 January 2023 at 09:01:24 UTC, Paul wrote:
> ...
> If the size of MyClass is 9 bytes why do MyClassO1 & O2 addresses only differ by 4 bytes?
>
> Because those addresses(4FFB20  4FFB24) are the addresses of the class **variables**, not the addresses of the **objects** themselves?

Because MyClass01 and MyClass02 are pointers and in your case they differ 4 bytes each other.

Now you could do this:

import std.stdio, std.traits;

class MyClass {char[16] c;}

void main() {
    writeln(" Size  Alignment  Type\n",
            "=========================");

    size_t size = __traits(classInstanceSize, MyClass);
    size_t alignment = classInstanceAlignment!MyClass;

    writefln("%4s%8s      %s",size, alignment, MyClass.stringof);

    // my test code added
    MyClass MyClassO1;
    MyClass MyClassO2;
    writeln("\n",&MyClassO1,"\t",&MyClassO2);
    writeln("\n",&(MyClassO1.c),"\t",&(MyClassO2.c));
    MyClassO1 = new MyClass();
    MyClassO2 = new MyClass();
    writeln("\n",&MyClassO1,"\t",&MyClassO2);
    writeln("\n",&(MyClassO1.c),"\t",&(MyClassO2.c));
}

In this machine it will print:

 Size  Alignment  Type
=========================
  32       8      MyClass

7FFD890C6410	7FFD890C6418 <- &MyClassO1 &MyClassO2
10	10                   <- Note here  [&(MyClassO1.c), &(MyClassO2.c)]
7FFD890C6410	7FFD890C6418 <- &MyClassO1 &MyClassO2 (Same address)
7FD0435D8010	7FD0435D8030 <- Now after instantiation! [&(MyClassO1.c), &(MyClassO2.c)]

Finally as you can see I changed your:

class MyClass {char c;}

to:

class MyClass {char[16] c;}

Because from char[1] to char[16] it will keep the address difference for [&(MyClassO1.c), &(MyClassO2.c)] by 0x20 (32):

7FD0435D8010	7FD0435D8030

If I change to char[17]

The difference goes up from 0x20 (32) to 0x30 (48), and keeps that way until char[32]:

7FD0435D8010	7FD0435D8040

char[33] will increase again by 16 bytes and so on.

Matheus.
January 01, 2023
On 1/1/23 01:01, Paul wrote:

> ...on my laptop it prints...
> ```
>   Size  Alignment  Type
> =========================
>     9       4      MyClass
>
> 4FFB20  4FFB24
> ```

> If the size of MyClass is 9 bytes why do MyClassO1 & O2 addresses only
> differ by 4 bytes?

As matheus said, classes are reference types, meaning, class variables are implemented as pointers. The values above are the addresses of those pointers. The fact that those values are different by 4 tells us that the code was compiled for 32 bits.

On the other hand, matheus's values were 8 apart because that compilation was 64 bits.

> So, I guess my question is actually how do I print out the addresses of
> the MyClassO1 & O2 objects themselves?

You must have missed my earlier answer: You need to cast the variable to 'void*'. That special operation will give you the address of the object. I am adding the following last line to matheus's example:

// ...
    writeln("\n",&(MyClassO1.c),"\t",&(MyClassO2.c));
    writeln("\n",cast(void*)MyClassO1,"\t",cast(void*)MyClassO2);

The output:

[...]
7F125FE77010	7F125FE77030  <-- The addresses of the 'c' members

7F125FE77000	7F125FE77020  <-- The addresses of the objects

The reason why objects are 0x10 bytes before the 'c' members is because a class object has two hidden initial members: the vtbl pointer and the monitor, both of which are size of a pointer (4 on your system, and 8 on matheus's system and mine).

Additionally, if you define the class as extern(C++), there will not be the monitor member. The reason for that is C++ does not have that concept and it would break interoperability.

Ali

January 02, 2023
Thank you, Teoh, Ali, & Matheus


January 04, 2023

matheus, using dmd64 on my laptop to compile and run this:

import std.stdio, std.traits;

class MyClass {char[16] c;}

void main() {
    writeln(" Size  Alignment  Type\n",
            "=========================");

    size_t size = __traits(classInstanceSize, MyClass);
    size_t alignment = classInstanceAlignment!MyClass;
    writefln("%4s%8s      %s",size, alignment, MyClass.stringof);

    // my test code added
    auto MyClassO1 = new MyClass();
    auto MyClassO2 = new MyClass();
    writeln("\nMyClassObj1     MyClassObj2");
    writeln(cast(void*)MyClassO1,"\t",cast(void*)MyClassO2);
}

I get this:

 Size  Alignment  Type
=========================
  32       8      MyClass

MyClassObj1     MyClassObj2
21EF8E22000     21EF8E22020

If I change this line to:

class MyClass {char[1] c;}

I get this:

 Size  Alignment  Type
=========================
  17       8      MyClass

MyClassObj1     MyClassObj2
27727202000     27727202020

If my size is 17 bytes and my alignment is 8 bytes, shouldn't my MyClassObj2 in this example be @ 27727202018 ?

January 04, 2023
On 1/3/23 20:01, Paul wrote:

>   Size  Alignment  Type
> =========================
>    17       8      MyClass
>
> MyClassObj1     MyClassObj2
> 27727202000     27727202020
> ```
> If my size is 17 bytes and my alignment is 8 bytes, shouldn't my
> MyClassObj2 in this example be @ 277272020**18** ?

Good question.

I made some guesses about object layouts, GC allocation schemes, etc. but did not like any of them.

Ali

January 04, 2023
On Wed, Jan 04, 2023 at 09:51:05AM -0800, Ali Çehreli via Digitalmars-d-learn wrote:
> On 1/3/23 20:01, Paul wrote:
> 
> >   Size  Alignment  Type
> > =========================
> >    17       8      MyClass
> >
> > MyClassObj1     MyClassObj2
> > 27727202000     27727202020
> > ```
> > If my size is 17 bytes and my alignment is 8 bytes, shouldn't my
> > MyClassObj2 in this example be @ 277272020**18** ?
> 
> Good question.
> 
> I made some guesses about object layouts, GC allocation schemes, etc. but did not like any of them.
[...]

Allocations are not necessarily consecutive; the GC may have its own strategy of allocation that doesn't follow a linear sequence. Furthermore, GC-allocated blocks may be larger than the request size because there may be some extra management information stored in the block (but outside the pointer range returned).

Basically, addresses returned by an allocator (GC or otherwise) should be treated as opaque handles, whose exact values are implementation- dependent and not really relevant to application code other than identifying the allocated object.


T

-- 
We've all heard that a million monkeys banging on a million typewriters will eventually reproduce the entire works of Shakespeare.  Now, thanks to the Internet, we know this is not true. -- Robert Wilensk
« First   ‹ Prev
1 2 3