Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

scott455

macrumors member
Original poster
Jun 5, 2022
40
0
within cocoa/objective-c, xcode 13.x, how to prevent structure sizes getting changed in static/run time for all architectures whether Arm or Intel?. Any flags that could be added during the build?.
Code:
typedef struct {
   char a;
   unsigned long long b;
   char c;
}s;
 

casperes1996

macrumors 604
Jan 26, 2014
7,485
5,649
Horsens, Denmark
Preamble:
It is generally not recommended to need to know a fixed size for your structs. It is advised to use operators like sizeof to adapt to the relevant conditions. If you can avoid needing it, do so.

Answer:
There's a few things to consider. First off, the primitive types, char, int, long, long long are not fixed width. By making the fields of your struct types like char and unsigned long long you're already kinda saying in your code that they do not need to be a specific size. char is *at least* a byte. int is *at least* 2 bytes, long is *at least* 4 and long long is *at least* 8. But all of these are specified as "at least". Instead use types like uint64_t to be more exact.

Second, if the fields of your struct don't conform to architecture alignment it may be padded for optimisation. Padding can happen in between fields and at the end. For example, your chars may get an additional 7 bytes of padding if the architecture and compiler in question really like things to be 64-bit aligned. To avoid this, you can use the compiler directive of "packed". You put this right after your struct keyword as such
Code:
typedef struct __attribute__((packed)) {

If I am not mistaken and missing something, both of these together should ensure the size of your struct is maintained exactly across architectures. Note however that the __attribute__((packed)) may not function on all compilers, but should work fine on Apple Clang used by Xcode's toolchain - at least last I tried
 
Last edited:

f54da

macrumors 6502
Dec 22, 2021
355
131
+1 to above. The only situation in which you should really be relying on packed behavior is if you're implementing bitfields or something, in which case combination of packed attribute and explicitly specifying bitfield width is what you should be doing. (Either use uinit32_t and/or use "int x : 3" to specify the widths you'll use).

Otherwise if you are relying on this for serialization/deserialization, use a dedicated serialization format.
 

scott455

macrumors member
Original poster
Jun 5, 2022
40
0
1.
can I use these fixed sizes in my structure to make sure field sizes do not change?.
Code:
int8_t    for char, even though we know char is always 1 byte.
int16_t
int32_t
int64_t
 
uint8_t
uint16_t
uint32_t
uint64_t

i.e., does not this make sense?.
typedef struct __attribute__((packed)) {
   int8_t  a;
   uint64_t b; //expect this always to be 8 bytes.
   int8_t  c;
}s;

2. how in objC release and debug use assert to make sure sizeof(s) is always 10. I read about nsassert.
 

casperes1996

macrumors 604
Jan 26, 2014
7,485
5,649
Horsens, Denmark
1.
can I use these fixed sizes in my structure to make sure field sizes do not change?.
Code:
int8_t    for char, even though we know char is always 1 byte.
int16_t
int32_t
int64_t
 
uint8_t
uint16_t
uint32_t
uint64_t

i.e., does not this make sense?.
typedef struct __attribute__((packed)) {
   int8_t  a;
   uint64_t b; //expect this always to be 8 bytes.
   int8_t  c;
}s;

2. how in objC release and debug use assert to make sure sizeof(s) is always 10. I read about nsassert.
1) Yes. Fun fact though; While char is always 1 byte, it is technically permitted for that byte to be, for example, 32-bits. But yes; All this is good

2)
I would personally use _Static_assert(cold, msg) - cond is a bool like sizeof(struct) == 10, msg is a string to print if it fails.
Since it's a static assert it will fail at compile-time not at runtime.
 

casperes1996

macrumors 604
Jan 26, 2014
7,485
5,649
Horsens, Denmark
+1 to above. The only situation in which you should really be relying on packed behavior is if you're implementing bitfields or something, in which case combination of packed attribute and explicitly specifying bitfield width is what you should be doing. (Either use uinit32_t and/or use "int x : 3" to specify the widths you'll use).

Otherwise if you are relying on this for serialization/deserialization, use a dedicated serialization format.

Bit fields can also get rather funky to work with though :p

But yeah agree. Also used packed while making an OS and frequently switching between assembly and C, but yeah generally advised to avoid when possible
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.