Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p><strong>Beware of Alignment !</strong></p> <p><code>public_t</code> native alignment is 1 since <code>char</code> are aligned to 1 byte. <code>private_t</code> alignment is set to the highest alignment requirement of its members, which is certainly not 1. It's probably aligned on the size of a pointer (<code>void *</code>), but there's a <code>double</code> inside a substructure which might require an alignment on 8 bytes. You may seen various kind of alignment depending on the ABI.</p> <p>Let's try a example program, compiled and tested on i386/i686 with gcc (code source follow):</p> <pre><code> kind name address size alignment required type | foo_t | N/A | 48 | N/A | 4 type | priv_t | N/A | 56 | N/A | 4 type | pub_t | N/A | 56 | N/A | 1 object | u8_0 | 0xfff72caf | 1 | 1 | 1 object | u8_1 | 0xfff72cae | 1 | 2 | 1 object | u8_2 | 0xfff72cad | 1 | 1 | 1 object | pub0 | 0xfff72c75 | 56 | 1 | 1 object | u8_3 | 0xfff72c74 | 1 | 4 | 1 object | pub1 | 0xfff72c3c | 56 | 4 | 1 object | u8_4 | 0xfff72c3b | 1 | 1 | 1 object | priv0 | 0xfff72c00 | 56 | 1024 | 4 object | u8_5 | 0xfff72bff | 1 | 1 | 1 object | priv1 | 0xfff72bc4 | 56 | 4 | 4 object | u8_6 | 0xfff72bc3 | 1 | 1 | 1 pointer | pubp | 0xfff72c75 | 56 | 1 | 1 pointer | privp | 0xfff72c75 | 56 | 1 | 4 **UNALIGNED** object | privp-&gt;val | 0xfff72c75 | 4 | 1 | 4 **UNALIGNED** object | privp-&gt;ptr | 0xfff72c79 | 4 | 1 | 4 **UNALIGNED** object | privp-&gt;f | 0xfff72c7d | 48 | 1 | 4 **UNALIGNED** </code></pre> <p>Source code of the test:</p> <pre><code>#include &lt;stdalign.h&gt; #ifdef __cplusplus /* you will need to pass -std=gnu++11 to g++ */ #include &lt;cstdint&gt; #endif #include &lt;stdint.h&gt; #include &lt;stdio.h&gt; #include &lt;inttypes.h&gt; #ifdef __cplusplus #define alignof __alignof__ #endif #define PRINTHEADER() printheader() #define PRINTSPACE() printspace() #define PRINTALIGN(obj) printobjalign("object", #obj, &amp;obj, sizeof(obj), alignof(obj)) #define PRINTALIGNP(ptr) printobjalign("pointer", #ptr, ptr, sizeof(*ptr), alignof(*ptr)) #define PRINTALIGNT(type) printtypealign(#type, sizeof(type), alignof(type)) static void printheader(void) { printf(" %8s %10s %18s %4s %9s %8s\n", "kind", "name", "address", "size", "alignment", "required"); } static void printspace(void) { printf(" %8s %10s %18s %4s %9s %8s\n", "", "", "", "", "", ""); } static void printtypealign(const char *name, size_t szof, size_t alof) { printf(" %8s | %10s | %18s | %4zu | %9s | %8zu \n", "type", name, "N/A", szof, "N/A", alof); } static void printobjalign(const char *tag, const char *name, const void * ptr, size_t szof, size_t alof) { const uintptr_t uintptr = (uintptr_t)ptr; uintptr_t mask = 1; size_t align = 0; /* get current alignment of the pointer */ while(mask != UINTPTR_MAX) { if ((uintptr &amp; mask) != 0) { align = (mask + 1) / 2; break; } mask &lt;&lt;= 1; mask |= 1; } printf(" %8s | %10s | %18p | %4zu | %9zu | %8zu%s\n", tag, name, ptr, szof, align, alof, (align &lt; alof) ? " **UNALIGNED**" : ""); } /* a foo struct with various fields */ typedef struct foo { uint8_t f8_0; uint16_t f16; uint8_t f8_1; uint32_t f32; uint8_t f8_2; uint64_t f64; uint8_t f8_3; double d; uint8_t f8_4; void *p; uint8_t f8_5; } foo_t; /* the implementation struct */ typedef struct priv { uint32_t val; void *ptr; struct foo f; } priv_t; /* the opaque struct */ typedef struct pub { uint8_t padding[sizeof(priv_t)]; } pub_t; static int test(pub_t *pubp) { priv_t *privp = (priv_t *)pubp; PRINTALIGNP(pubp); PRINTALIGNP(privp); PRINTALIGN(privp-&gt;val); PRINTALIGN(privp-&gt;ptr); PRINTALIGN(privp-&gt;f); PRINTSPACE(); return privp-&gt;val; } int main(void) { uint8_t u8_0; uint8_t u8_1; uint8_t u8_2; pub_t pub0; uint8_t u8_3; pub_t pub1; uint8_t u8_4; priv_t priv0; uint8_t u8_5; priv_t priv1; uint8_t u8_6; PRINTHEADER(); PRINTSPACE(); PRINTALIGNT(foo_t); PRINTALIGNT(priv_t); PRINTALIGNT(pub_t); PRINTSPACE(); PRINTALIGN(u8_0); PRINTALIGN(u8_1); PRINTALIGN(u8_2); PRINTALIGN(pub0); PRINTALIGN(u8_3); PRINTALIGN(pub1); PRINTALIGN(u8_4); PRINTALIGN(priv0); PRINTALIGN(u8_5); PRINTALIGN(priv1); PRINTALIGN(u8_6); PRINTSPACE(); return test(&amp;pub0); } </code></pre> <p><strong>Analysis</strong>:</p> <p><code>pub0</code> is allocated on the stack and is passed as argument to function <code>test</code>. It is aligned on 1 byte, so that, when cast'ed as a <code>priv_t</code> pointer, <code>priv_t</code> structure members are not aligned.</p> <p>And that can be bad:</p> <ul> <li>bad for correctness: some architectures/CPUs will silently corrupt read/write operations to unaligned memory address, some others will generate a fault. The latter is better.</li> <li>bad for performance: if supported, unaligned access/load/store are still known to be poorly handled: you're likely asking the CPUs to read/write twice the memory size of the object ... you might hit the cache badly this way.</li> </ul> <p>So, if you really want to hide structure content, you should take care of the alignment of the underlying structure: don't use <code>char</code>.</p> <p>By default, use <code>void *</code>, or if there can be <code>double</code> in any members of the structure, use <code>double</code>. This will works until someone use a <code>#prama</code> or <code>__attribute__(())</code> to choose an higher alignment for (a member of) the hidden structure.</p> <p>Let's define correctly <code>pub_t</code>:</p> <pre><code>typedef struct pub { double opaque[(sizeof(priv_t) + (sizeof(double) - 1)) / sizeof(double)]; } pub_t; </code></pre> <p>It might sound complex, and it is ! This way the <code>pub_t</code> structure will have correct alignment and be at least as big as underlying <code>priv_t</code>.</p> <p>If <code>priv_t</code> was packed (with <code>#pragma</code> or <code>__attribute__(())</code>), using <code>sizeof(priv_t)/sizeof(double)</code>, <code>pub_t</code> could be smaller than <code>priv_t</code> ... which will be even worst than the problem we were trying to solve initially. But if the structure was packed, who care of the alignment.</p> <p><strong>malloc()</strong></p> <p>If <code>pub_t</code> structure was allocated by <code>malloc()</code> instead of being allocated on stack, the alignment would not be a problem since <code>malloc()</code> is defined to return a memory block aligned to the greatest memory alignments of the C native types, eg. <code>double</code>. In modern <code>malloc()</code> implementations alignment could be up to 32 bytes.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload