Get Position of a struct var AKA Offset of record field
Asked Answered
B

2

4

I would like to get a "position" of a structure/record.

Say I have this record:

type
  MyStruct = record
    MyInteger   : Integer;
    MyInteger2  : Integer;
    MyInteger3  : Integer;
    MyFunc      : function (FirstParam : WideString; SecondParam : String) : Integer;
    MyString    : String;
    MyString2   : WideString;
    MyPchar     : pchar;
end;

As you can see this record has a size of 28 bytes (7 vars x 4 bytes). Basically because all the vars are either 4 byte vars (such as Integer) or pointers (also 4 bytes). Now let's say we have this struct loaded in an memory-address (X = 0) (which also means the address for MyInteger would be 0). The address of MyInteger3 (for example) would be 8 (be aware of the X = 0 !) How can I get the position (number/address) of the struct dynamically?

Hope you guys know what I mean? Thanks in advance.

BTW: Is any Var always 4 bytes in a struct? EDIT: This is wrong if you fix the spcae : String[100]

Blackmon answered 31/10, 2012 at 23:38 Comment(2)
No, variables in a struct are not always 4 bytes (thing types smaller than 4 bytes, like Word, Byte, Boolean, enums, etc), and are not always aligned on 4-byte memory boundaries (packed records, Word-aligned or Int64-aligned records, etc). And Pointers are 8 bytes on 64-bit systems.Enfield
@Remy - you also not mentioned compiler directives, that may change alignment to 1,2,4 or 8 bytes boundary at developer's optionSatiny
E
10

You can get the offset of any record member using some pointer arithmetic:

type
  PMyStruct = ^MyStruct;

var
  Offset: Integer;
begin
  Offset := Integer(@(PMyStruct(nil).MyInteger3));
  // or:
  // Offset := Integer(Addr(PMyStruct(nil).MyInteger3));
end;

If you want the offset of the function you need to code it like this:

Offset := Integer(@@PMyStruct(nil).MyFunc);
// or:
// Offset := Integer(Addr(@PMyStruct(nil).MyFunc));
Enfield answered 1/11, 2012 at 2:39 Comment(5)
Note that this doesn't work for the function. See my example to see how you could work around that.Morgun
Is such an expression accepted by the compiler as a constant?Lariat
@WoutervanNifterick You can do it Remy's instance free way too readily enough. See my update.Lariat
@DavidHeffernan : Creative workaround. I'll nominate it for Nick's fun code of the week :)Morgun
Should your compiler version be Delphi-2006 or later, you can add a class function inside MyStruct: class function MyInteger3Offs : Integer; static; and implementation: class function MyStruct.MyInteger3Offs: Integer; begin Result := Integer(@(PMyStruct(nil).MyInteger3)); end;Aramaic
M
7
program OffsetTest; {$APPTYPE CONSOLE}

type
  f = function (FirstParam : WideString; SecondParam : String) : Integer;
  TStruct = record
    MyInteger   : Integer;
    MyInteger2  : Integer;
    MyInteger3  : Integer;
    MyFunc      : ^f;
    MyString    : String;
    MyString2   : WideString;
    MyPchar     : pchar;
  end;

var MyStruct :TStruct;

begin
  Writeln( int64(@(MyStruct.MyInteger )) - int64(@(MyStruct)));
  Writeln( int64(@(MyStruct.MyInteger2)) - int64(@(MyStruct)));
  Writeln( int64(@(MyStruct.MyInteger3)) - int64(@(MyStruct)));
  Writeln( int64(@(MyStruct.MyFunc    )) - int64(@(MyStruct)));
  Writeln( int64(@(MyStruct.MyString2 )) - int64(@(MyStruct)));
  Writeln( int64(@(MyStruct.MyPchar   )) - int64(@(MyStruct)));
  Readln;
end.

Results with 32bits version:

0 
4 
8
12
20
24

Results with 64bits version:

0
4
8
16
32
40

Results with 64bits, packed record:

0
4
8
12
28
36

Showing the different results to point out that it might be a bad idea to rely on these offsets in your code. Or at least be very aware that different circumstances might result in different offsets.

Morgun answered 1/11, 2012 at 2:47 Comment(2)
This works just as fine! The very minor disadvantage is that you have to allocate a struct.Blackmon
+1, also for pointing out the pitfalls of different alignments.Downspout

© 2022 - 2024 — McMap. All rights reserved.