2

I have two structures as specified below. The comments specify the size of the data members and padding values that the compiler would augment.

#include <stdio.h>
#include <stdint.h>


typedef struct peer_msg_hdr
{
  char type;            /**< 1 */
                        /**< pad[3] */
  uint32_t id;          /**< 4 */
  uint64_t timestamp;   /**< 8 */
} PEER_MSG_HDR;         /**< 16 */

typedef struct peer_msg 
{
  PEER_MSG_HDR hdr;      /**< 16 */
  uint16_t listen_port;  /**< 2 */
                         /**< pad[2] */

  uint32_t num_nodes;    /**< 4 */
  uint32_t num_proc;     /**< 4 */
} PEER_MSG;


int main()
{
  printf("%lu\n", sizeof(PEER_MSG));
  return 0;
}

Now in a x86_64 host machine, first I calculate the size of PEER_MSG. It turns out to be 32. Next, if I calculate the size with -m32 option in gcc, the size is 28.

Difference between these two arise in padding after the last data member, num_proc. In case of -m32 option compilation, there is no padding. But without -m32 option, there is a padding of 4 bytes (to align the whole structure to 8 bytes, as it should be because the widest data member size is 8 bytes (uint64_t timestamp)).

My question is: with -m32 option, size of uint64_t remains 8 bytes, but the alignment of the structure PEER_MSG is of 4 bytes, which contradicts the general rule of padding (structure alignment should be equal to the alignment of its widest data member). So what is the compiler rule here for padding with -m32 option?

rashok
  • 12,790
  • 16
  • 88
  • 100
Soumen
  • 1,068
  • 1
  • 12
  • 18
  • 2
    In m32 mode, `double` can be 4-byte aligned even though it is an 8-byte type and must be 8-byte aligned in m64 mode. It’s moderately likely a similar rule applies to 8-byte integers too. – Jonathan Leffler Mar 20 '18 at 06:39
  • @JonathanLeffler that makes complete sense of what is happening here. Can you provide me with some references for this? – Soumen Mar 20 '18 at 06:43
  • 2
    Only empirical evidence equivalent to what you've got already. I have a `typesize` program that, when compiled for 32-bit, produces `12 = sizeof(struct { char a; double b; })` but for 64-bit produces `16 = sizeof(struct { char a; double b; })`, and also `12 = sizeof(struct { char a; long long b; })` vs `16 = sizeof(struct { char a; long long b; })`. That's hardly a 'reference' for it. You might be able to find something useful in the ABI specifications for 32-bit vs 64-bit Intel chips. See [Where is the x86-64 System V ABI documented?](https://stackoverflow.com/questions/18133812) – Jonathan Leffler Mar 20 '18 at 07:09
  • `sizeof` returns `size_t` which must be printed out using [`%zu`](https://stackoverflow.com/q/940087/995714). [You're getting UB](https://stackoverflow.com/q/16864552/995714) – phuclv Apr 11 '18 at 14:56

2 Answers2

2

On a 32 bit machine, the processing word size is 4 bytes, hence the structure gets aligned according to that which why you get the size of PEER_MSG as 28 bytes. On a 64 byte system, since the processing word size will be 8 bytes, you get the size of PEER_MSG as 32 bytes.

When you specify -m32 option, the compiler is made to assume that the final executable is going to be run on 32 byte system and hence does the padding appropriately.

Jay
  • 24,173
  • 25
  • 93
  • 141
  • Yes, I get that. But suppose there is an array PEER_MSG[2]. With -m32 option (structure size of 28 bytes), what would be the offset of the timestamp field in the 2nd PEER_MSG? It would be (28 + 8) = 36 bytes, which is not 8 byte aligned. But timestamp field requires 8 bytes alignement. So what would happen here? – Soumen Mar 20 '18 at 06:41
  • In a 32 Bit System, if we define a struct like `struct a { uint8_t one; uint8_t two; uint8_t three; }` How much memory will it take? Will it work like [1] pad[3] [1] pad[3] [1] pad[3] ? – ugola Jul 16 '18 at 07:54
  • No. As per my understanding, it should be [1][1][1]pad[1] as the chars are consecutive. If you were to add variables of some other data type in between, it may do padding differently. You can always check it by doing a 'sizeof' of the final structure. – Jay Jul 16 '18 at 10:56
2

My question is: with -m32 option, size of uint64_t remains 8 bytes, but the alignment of the structure PEER_MSG is of 4 bytes,

  • This is exactly correct. In 32 bit system also size of uint64_t is 8 bytes. And struct alginment is done based on word size 4 bytes.

Your point of structure alignment should be equal to the alignment of its widest data member is wrong.

  • Structure alginment is based on the word size of the architecture (not the widest data member). In 32 bit architecture word size is 4 byte, where as in 64 bit architecture word size is 8 byte.
rashok
  • 12,790
  • 16
  • 88
  • 100