Niekoniecznie musisz używać do tego ASMa - język C/C++ również pozwala na taki zabieg.
Zmienne w strukturze (prostej, bez dziedziczenia) ułożone są według kolejności ich deklaracji. Pierwsza zmienna struktury ma adres samej struktury, do każdej kolejnej dostaniesz adres, dodając sumę adresów wszystkich poprzednich zmiennych struktury do adresu bazowego. Tylko tutaj należy się troszeczkę zastanowić, bo jest mała pułapka.
Domyślnie kompilator wyrównuje pola struktur do ilości bajtów, niezbędnej do optymalnego czasu dostępu.
Zobacz na poniższym kodzie (takie brzydkie rzutowania dla brzydkich zastosowań rodem z C):
#include <iostream>
using namespace std;
struct S
{
int a;
char b;
int c;
bool d;
};
int main()
{
S foo, *ptr_foo = &foo;
foo.a = 10;
foo.b = 'a';
foo.c = 20;
foo.d = false;
cout<< *(int*) ((int)ptr_foo) << endl; // zmienna a
cout<< *(char*) ((int)ptr_foo + sizeof(int)) << endl; // zmienna b
cout<< *(int*) ((int)ptr_foo + (sizeof(int) + sizeof(int)) ) << endl; // zmienna c
cout<< *(bool*) ((int)ptr_foo + (sizeof(int) + sizeof(int) + sizeof(int)) ) << endl; // zmienna d
return 0;
}
Każda kolejna zmienna jest oddalona na moim systemie i przy użyciu GCC4.9 od poprzedniej o sizeof(int), czyli 4 bajty.
Jeśli zaś chciałbyś wymusić rozłożenie zmiennych w strukturze zgodnie z ich faktycznym rozmiarem, musisz użyć dyrektywy preprocesora pragma pack(1), która ustawia wyrównanie do 1 bajta.
#include <iostream>
using namespace std;
#pragma pack(1)
struct S
{
int a;
char b;
int c;
bool d;
};
int main()
{
S foo, *ptr_foo = &foo;
foo.a = 10;
foo.b = 'a';
foo.c = 20;
foo.d = false;
cout<< *(int*) ((int)ptr_foo) << endl; // zmienna a
cout<< *(char*) ((int)ptr_foo + sizeof(int)) << endl; // zmienna b
cout<< *(int*) ((int)ptr_foo + (sizeof(int) + sizeof(char)) ) << endl; // zmienna c
cout<< *(bool*) ((int)ptr_foo + (sizeof(int) + sizeof(char) + sizeof(int)) ) << endl; // zmienna d
return 0;
}