0

I am confused why clientIf even though declared as uint32(unsigned int - size 4 bytes), is taking up 8 bytes. I have explicitly printed sizeof(uint32) and that shows 4 bytes.

Can someone throw some insight.

I am running this code on a little endian machine x86.

#include <stdio.h>
#include <string.h>

/* network defines */
typedef char uint8;
typedef short uint16;
typedef unsigned int uint32;
typedef uint32 ipv4Addr;

/***********************    V6 IP ADDRESS   **********************************/
typedef struct _v6IpAddr
{
    union _Bytes
    {
        uint8       rbyte[16];
        uint16      doublebyte[8];
        uint32      wordbyte[4];
        long long   dwordbyte[2];
    }unionv6;
#define dbyte   unionv6.doublebyte
#define wbyte   unionv6.wordbyte
#define dwbyte  unionv6.dwordbyte
#define xbyte   unionv6.rbyte
}v6IpAddr;

typedef struct _v6IpAddr uint128;

typedef union _comIpAddr
{
    ipv4Addr v4Ip;
    v6IpAddr v6Ip;
}comIpAddr;

typedef struct abc
{
    /*|*/uint32         clientIf;          /*|*/
    /*|*/comIpAddr      clientIp;          /*|*/
    /*|*/uint8          mac[6];            /*|*/
    /*|*/uint16         zero;              /*|*/
}ABC;

void print_bytes(const void *object, size_t size)
{
  // This is for C++; in C just drop the static_cast<>() and assign.
  const unsigned char * const bytes = object;
  size_t i;

  printf("[ ");
  for(i = 0; i < size; i++)
  {
    printf("%02x ", bytes[i]);
  }
  printf("]\n");
}

int main()
{
    ABC test;
    memset(&test,0,sizeof test);
    printf("sizeof test = %u\n", sizeof test);
    printf("%d-%d-%d\n", sizeof(uint8), sizeof(uint16), sizeof(uint32));

    print_bytes(&test, sizeof test);
    test.clientIf = 10;
    test.clientIp.v4Ip = 0xAABBCCDD;
    print_bytes(&test, sizeof test);
    printf("%p-%p-%p\n", &(test.clientIf), &(test.clientIp), &(test.clientIp.v4Ip));
    return 0;
}

$ ./a.out

sizeof test = 32

1-2-4

[ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ]

[ 0a 00 00 00 00 00 00 00 dd cc bb aa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ]

0x7fff113b0780-0x7fff113b0788-0x7fff113b0788 $

Edit:

Why will compiler add padding after clientIf since it is already a multiple of 4 bytes?

Kingkong Jnr
  • 1,128
  • 4
  • 18
  • 29

3 Answers3

1

It is up to the compiler how it aligns memory. You can try to use __attribute__ ((__packed__)) if using GCC or similar compiler or #pragma pack(push,1) and #pragma pack(pop) if using Visual C.

It is not portable way to do things and it can break on some compilers/systems but it can work for you.

Packing can save the memory but accessing variables may be slower.

Edit:

What can break if using packed:

Some compilers can ignore packing at all in some cases (e.g. there was/is? a bug in mingw-gcc)

Some CPU architectures can't handle unaligned access to memory. They throw an exception/fault or they just use wrong values. (e.g. ARMv5 and older ARM cores). Compiler can overcome this issue in some cases but not in all cases.

j123b567
  • 3,110
  • 1
  • 23
  • 32
1

As others pointed out in comments, offset of an element in a struct is not a reliable way to measure its as your compiler is free to insert padding to structs in order to enforce specific member alignment.

If you want your structs not to include padding for various reason, you have to use a compiler specific pragma to do it.

For GCC, Clang and MSVC, you have to add #pragma pack(push, 1) right before your struct definition and #pragma pack(pop) right after it.

0x400921FB54442D18
  • 725
  • 1
  • 5
  • 18
0

Structs will typically be padded for alignment purposes. An 8-byte type will want 8-byte alignment, so if it follows a 4-byte type, there may be 4 bytes of padding added so the 8-byte type can have its proper alignment. Some compilers have extensions that will allow you to throw alignment to the wind (as shown in some of the other answers), but that comes with the problem of relying on compiler-specific extensions and unaligned members which, depending on the underlying hardware, can result in speed penalties or even traps (SPARC, for example, cannot handle unaligned access).

Instead of relying on such behavior, if you order the struct by strictest alignment requirements first, it should naturally pack nicely without the alignment issues, though you might still wind up with some padding at the end so the struct itself can be aligned.

Christian Gibbons
  • 4,272
  • 1
  • 16
  • 29