Followers 0

# struct alignment question

## 9 posts in this topic

Hallo.

I understand that the size of a struct is not always the sum of it's innards, because there can be padding inbetween fields. What I am wondering is this:

Can I safely assume that two differently named structs types are the same (have the same size and same field offset) if they have the same arrangement of fields?

That is, can I assume that these two are the same:
struct a{
int a;
char b;
long c;
short d;
double e;
};

struct a{
int a;
char b;
long c;
short d;
double e;
};

even when they are defined in completely different places (different files, or different scope, or maybe anonymously defined)?
0

##### Share on other sites

Yes and no ;)

The names of the fields doesn't matter, just the type and ordering, so yes.

But you can change the packing with #pragma pack (or with a compile time switch) (on Visual C++ anyway, other compilers may have different options/pragmas), so no. The alignment can be different when the source or header file is compiled depending on what packing is active when the struct/class definition is parsed.

1

##### Share on other sites

Can I safely assume that two differently named structs types are the same (have the same size and same field offset) if they have the same arrangement of fields?  That is, can I assume that these two are the same, even when they are defined in completely different places (different files, or different scope, or maybe anonymously defined)?

Yes, their layout in memory should be the same.

For example, if you include the same structure declaration in various source files the objects must be interchangeable because they are the same layout.  If you have the object in foo.c and also in bar.c, you can pass the objects between functions in either file without problem.

As Paradigm Shifter pointed out, you can mess this up on your own by changing the alignment or padding options for the compiler, but that breaks lots of things.  Don't do that.

0

##### Share on other sites

The bigger question is WHY you would have 2 identical structs defined in multiple places which you wanted to treat as the same in multiple places. That's what header files are for.

0

##### Share on other sites
Ah, so as long as I don't use pragmas or __attribute__, it should be the same.

The reason I'm doing this is for convenience (and I'd say clarity of the code too).

I have this hash set type, and it can be used as a hash table if I just pass in a pair of values as a key. The thing is, I need to define a new struct for every pair (no std::pair<a, b> template in c). Rather than defining them at the top of the file with names that I might not remember, I think it is better to define it in the function where I actually need to use it.
0

##### Share on other sites

What does your implementation for your hash map look like? Does it use void* and size parameters or is it built with #define macros?

You might be better off defining an interface of function pointers instead and use that in the same way you would an abstract class in C++, (i.e. a list of function pointers like a vtbl) and passing one of those in to the create function of your hash map (which would be stored in the map and would call out to functions provided by concrete implementations for each type, like how qsort does for the comparison operator). Then your concrete implementations can cast from void* to the required type and back.

Or you could just use C++ ;)

0

##### Share on other sites
It looks like this:
When I create a new hash set, I pass in the element size, a comparison function pointer, and a hash function pointer. Adding a key involves using memcpy to copy "element size" number of bytes into the hash set's array. So yes, it's void* and element size.

Basically, I un-macro-ized the original macro implementation because it was getting messy to put things like "MAKE_HASHSET(int, INT_CMP, INTHASH)" everywhere.

The vtable you described there, what function pointer is needed? (besides comparison and hash) Edited by ultramailman
0

##### Share on other sites

That should do it (comparison and hash).

You can also store the element size and keysize when you create the hash map, then you can express the entire interface using void*, but you do lose the ability to use a literal key then (need to use a variable with an address).

hashmap* pMap = CreateHashSet(sizeof(int) /*keysize*/, sizeof(int) /* element size */, INT_CMP /* comparison func */. INTHASH /* hash func */);

int myKey = 0;

int myValue = 42;

pMap->InsertElement(&myKey, &myValue);

where the insert uses sizeof(int) stored as the keysize to copy the element, and InsertElement just uses void* parameters (size is known by the data associated with the map).

0

##### Share on other sites
That's pretty much how it is. Except it only takes keys as an argument.
/* inside a function*/
typedef struct int_pair{
int key, val;
}int_pair;
struct hashset * hs = hashset_new(sizeof(int_pair), &int_cmp, &int_hash);
hashset_insert(hs, &(int_pair){0, 42});

It's true that it can't take literals, but it still behaves as if it is because it does make a copy of the thing.
0

##### Share on other sites

The bigger question is WHY you would have 2 identical structs defined in multiple places which you wanted to treat as the same in multiple places. That's what header files are for.

In fact, that's what header files DO.

0

## Create an account

Register a new account