struct{int i, char u[16] ...}; Why 4 empty bytes after char[16]?

Hello,

My code:
1
2
3
4
5
6
7
8
9
struct tt
{
	int i;
	char u[16];
	double d;
	long long e;
	int y[4];
	int s;
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	tt a = { 123456, "Hello World", 69.96, 9876543210L, { 1, 2, 3, 4 }, 123 };
	char *p = (char *)&a;



	int i = *((int *)p); p += 4;
	char *u = (char *)p; p += 16;

	cout << "empty 4 bytes:" << *((int *)p) << endl; p += 4; // <-- why?

	double d = *((double *)p);  p += 8;
	long long e = *((long long *)p);  p += 8;
	int *y = (int *)p; p += 4 * 4;
	int s = *(int *)p;
	cout << "i:" << i << "/" << a.i << endl;
	cout << "u:" << u << "/" << a.u << endl;
	cout << "d:" << d << "/" << a.d << endl;
	cout << "e:" << e << "/" << a.e << endl;
	cout << "y:" << y[0] << "," << y[1] << "," << y[2] << "," << y[3] << "," << "/" << a.y[0] << "," << a.y[1] << "," << a.y[2] << "," << a.y[3] << "," << endl;
	cout << "s:" << s << "/" << a.s << endl;


output:
1
2
3
4
5
6
7
8
empty 4 bytes:13967393
i:123456/123456
u:Hello World/Hello World
d:69.96/69.96
e:9876543210/9876543210
y:1,2,3,4,/1,2,3,4,
s:123/123
Press any key to continue . . .


emtpy 4 bytes seems to be uninitialized because of the random value it gives everytime i run
my program.
Last edited on
It's padding.

Structs are often padded to have variables land on certain boundaries so they can be accessed more quickly by the processor. (Also called "alignment")

Here, since a 'double' is 8 bytes, for it to be optimally accessed, the compiler wants to align it on an 8-byte boundary. Since there are 20 bytes in the struct before it, it pads with 4 extra bytes to push that up to 24 -- giving it proper alignment.
Okay thanks.
This does make things a little complicated.

I'm trying to do the function what separates variables like int, long long, double etc..
from char * and char * gets its pointer from structure.

each variable type has it's own function what returns the variable and takes char *
after it returns the variable, it does char * + sizeof(variable type)

But it is hard to keep track on where alignment have happened since
variable functions don't have anyidea what were the previous variable types unless
i make a variable array or something what remembers.

Perhaps there is better way to do it.
Any ideas how could it be done?
Last edited on
Generally casting away the variable type (that is, casting pointers to 'char*' even though they don't point to a char) is a bad idea. I suspect your entire approach here has taken you down the wrong path.

What are you ultimately trying to do? I assume you have a structure, but what are you trying to do with that structure that requires pointer math?
I used char * since i can't use anything else to handle it.
void * doens't let me use +=

I use sockets to send data to other program's dll i wrote for program.
The problem is that program (Metatrader 4) has it's own c++ type of language what doens't
have pointers and only simple type of variable can be passed to dll.

My socket's data structure what has char messagetype and void * messagedata
has to be also casted to char * since that socket i am using (winsocket) only takes char*

void*messagedata should contain a structure of different type of variables what different message needs.

Edit:
Information about metatrader's c++:

http://docs.mql4.com/runtime/imports
Passing Parameters
All parameters of simple types are passed by values unless it is explicitly indicated that they are passed by reference. When a string is passed, the address of the buffer of the copied string is passed; if a string is passed by reference, the address of the buffer of this string without copying it is passed to the function imported from DLL.

Structures that contain dynamic arrays, strings, classes, other complex structures, as well as static or dynamic arrays of the enumerated objects, can't be passed as a parameter to an imported function.

When passing an array to DLL, the address of the beginning of the data buffer is always passed (irrespective of the AS_SERIES flag). A function inside a DLL knows nothing about the AS_SERIES flag, the passed array is a static array of an undefined length; an additional parameter should be used for specifying the array size.

data types:
http://docs.mql4.com/basis/types#base_types
Last edited on
Instead of hard-coding size and alignment, use the sizeof and alignof operators.
And to get an aligned address, use std::align()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <memory>
#include <iomanip>

template < typename T > T* address_of( void*& next_address, std::size_t& bytes_remaining )
{
    // http://en.cppreference.com/w/cpp/memory/align
    // http://en.cppreference.com/w/cpp/language/alignof
    auto address = static_cast<T*>( std::align( alignof(T), sizeof(T), next_address, bytes_remaining ) ) ;

    if(address) { next_address = address+1 ; bytes_remaining -= sizeof(T) ; }

    return address ;
}

struct tt
{
    int i;
    char u[16];
    double d;
    long long e;
    int y[4];
    int s;
};

int main()
{
    tt a = { 123456, "Hello World", 69.96, 9876543210L, { 1, 2, 3, 4 }, 123 };

    void* p = std::addressof(a) ;
    std::size_t sz = sizeof(a) ;

    const auto pi = address_of< decltype(tt::i) >( p, sz ) ; std::cout << *pi << ' ' ;
    const auto pu = address_of< decltype(tt::u) >( p, sz ) ; std::cout << ' ' << std::quoted(*pu) ;
    const auto pd = address_of< decltype(tt::d) >( p, sz ) ; std::cout << ' ' << *pd ;
    const auto pl = address_of< decltype(tt::e) >( p, sz ) ; std::cout << ' ' << *pl ;

    const auto py = address_of< decltype(tt::y) >( p, sz ) ;
    std::cout << " { " ; for( auto v : *py ) std::cout << v << ' ' ; std::cout << '}' ;

    const auto ps = address_of< decltype(tt::s) >( p, sz ) ; std::cout << ' ' << *ps ;
}

http://coliru.stacked-crooked.com/a/1b4f0384f3258746

We can use the offsetof macro if the definition of the StandardLayoutType is available to the library.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <memory>
#include <iomanip>

// http://en.cppreference.com/w/cpp/types/offsetof
#define address_of( Struct, member, ptr ) \
    ( reinterpret_cast< const decltype(Struct::member)* >( \
            reinterpret_cast< const char* >(ptr) + offsetof(Struct,member) ) )

struct tt
{
    int i;
	char u[16];
	double d;
	long long e;
	int y[4];
	int s;
};


int main()
{
    tt a = { 123456, "Hello World", 69.96, 9876543210L, { 1, 2, 3, 4 }, 123 };

    auto p = std::addressof(a) ;

    const int* pi = address_of( tt, i, p ) ; std::cout << *pi << ' ' ;
    const auto pu = address_of( tt, u, p ) ; ; std::cout << ' ' << std::quoted(*pu) ;
    const auto pd = address_of( tt, d, p ) ; std::cout << ' ' << *pd ;
    const auto pl = address_of( tt, e, p ) ; ; std::cout << ' ' << *pl ;

    const auto py = address_of( tt, y, p ) ;
    std::cout << " { " ; for( auto v : *py ) std::cout << v << ' ' ; std::cout << '}' ;

    const auto ps = address_of( tt, s, p ) ; ; std::cout << ' ' << *ps << '\n' ;
}

http://coliru.stacked-crooked.com/a/ec06091c5e16a425
Last edited on
Is there anyway to access structure tt variables when i don't have structure
but i know what variable types there are inside and also the order of them?
> Is there anyway to access structure tt variables when i don't have structure
> but i know what variable types there are inside and also the order of them?

Because tt is a StandardLayoutType, the code posted earlier
http://coliru.stacked-crooked.com/a/1b4f0384f3258746 is expected to work in practice.

Here's more formalised version of the same (returns a std::tuple<> containing the addresses of members):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <memory>
#include <tuple>
#include <type_traits>

template < typename T > T* address_of( void*& next_address, std::size_t& bytes_remaining )
{
    // http://en.cppreference.com/w/cpp/memory/align
    // http://en.cppreference.com/w/cpp/language/alignof
    auto address = static_cast<T*>( std::align( alignof(T), sizeof(T), next_address, bytes_remaining ) ) ;

    if(address) { next_address = address+1 ; bytes_remaining -= sizeof(T) ; }

    return address ;
}

template < typename T > std::tuple< T* > addresses_of_members( void* pv, std::size_t nbytes )
{ return std::tuple<T*>( address_of<T>( pv, nbytes ) ) ; }

template < typename T, typename... REST >
typename std::enable_if< sizeof...(REST) != 0, std::tuple< T*, REST*... > >::type
    addresses_of_members( void* pv, std::size_t nbytes )
{
    std::tuple<T*> first( address_of<T>( pv, nbytes ) ) ;
    return std::tuple_cat( first, addresses_of_members<REST...>( pv, nbytes ) ) ;
}

#include <iostream>
#include <iomanip>

void print_members_of_unknown_object( void* address, std::size_t sz )
{
    // invariant: address is the address of an object of some unknown standard layout type
    // we know that the types of members in the standard layout type, in declaration order are
    //   int, char[16], double, long long, int[4], int

    const auto ptrs = addresses_of_members< int, char[16], double, long long, int[4], int >( address, sz ) ;
    std::cout << *std::get<0>(ptrs) << ", ";
    std::cout << *std::get<1>(ptrs) << ", ";
    std::cout << *std::get<2>(ptrs) << ", ";
    std::cout << *std::get<3>(ptrs) << ", [ ";

    for( int v : *std::get<4>(ptrs) ) std::cout << v << ' ' ;

    std::cout << "] " << *std::get<5>(ptrs) << '\n' ;
}

int main()
{
    struct tt
    {
        int i;
        char u[16];
        double d;
        long long e;
        int y[4];
        int s;
    };

    tt a = { 123456, "Hello World", 69.96, 9876543210L, { 1, 2, 3, 4 }, 123 };

    print_members_of_unknown_object( std::addressof(a), sizeof(a) ) ;
}

http://coliru.stacked-crooked.com/a/5f2ae2f5b5db2bef

Microsoft C++ 2015 (19.00.23026 for x86) bombs http://rextester.com/PTCQTX67633
But on the one machine that I tested, with Update 1 (19.00.23506 for x64), it works as expected.

Note: Be forewarned that this is not type-safe; it is the programmers responsibility to ensure that the type involved is a StandardLayoutType and that the types of members in the standard layout type are correctly specified, in declaration order. In short, avoid this, if at all possible.
I've been extremely silly.

It is so much simpler (and cleaner) to define a local struct that is layout-compatible with the unknown StandardLayout struct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <iomanip>
#include <cassert>

void print_members_of_unknown_object( const void* address, std::size_t sz )
{
    // invariant: address is the address of an object of some unknown standard layout type
    // we know that the types of members in the standard layout type, in declaration order are
    //   int, char[16], double, long long, int[4], int

    struct layout_compatible_type
    {
        int first;
        char second[16];
        double third;
        long long fourth;
        int fifth[4];
        int sixth;
    };

    assert( sizeof(layout_compatible_type) == sz ) ;
    const layout_compatible_type* p = static_cast< const layout_compatible_type* >(address) ;

    std::cout << p->first << ", ";
    std::cout << p->second << ", ";
    std::cout << p->third << ", ";
    std::cout << p->fourth << ", [ ";

    for( int v : p->fifth ) std::cout << v << ' ' ;

    std::cout << "] " << p->sixth << '\n' ;
}

int main()
{
    struct tt
    {
        int i;
        char u[16];
        double d;
        long long e;
        int y[4];
        int s;
    };

    tt a = { 123456, "Hello World", 69.96, 9876543210L, { 1, 2, 3, 4 }, 123 };

    print_members_of_unknown_object( std::addressof(a), sizeof(a) ) ;
}

http://coliru.stacked-crooked.com/a/1ed2c357312baf5f
Topic archived. No new replies allowed.