我很困惑为什么clientIf即使声明为uint32(unsigned int-size 4 bytes),也占用了8个字节。我已经明确地打印了sizeof(uint32)并显示了4个字节。
有人可以提出一些见解。
我在一台小端机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][0 00 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]
0x7fff113b0780-0x7fff113b0788-0x7fff113b0788 $
编辑:
为什么编译器会在clientIf之后添加填充,因为它已经是4个字节的倍数?
答案 0 :(得分:1)
由编译器决定如何对齐内存。如果使用GCC或类似的编译器,则可以尝试使用__attribute__ ((__packed__))
;如果使用Visual C,则可以尝试使用#pragma pack(push,1)
和#pragma pack(pop)
。
它不是可移植的做事方式,它可能会破坏某些编译器/系统,但它可以为你工作。
打包可以节省内存,但访问变量可能会更慢。
修改强>
使用packed时可能会破坏:
有些编译器在某些情况下可以忽略打包(例如,mingw-gcc中存在错误)
某些CPU架构无法处理对内存的未对齐访问。他们抛出异常/错误或他们只是使用错误的值。 (例如ARMv5和较旧的ARM内核)。编译器可以在某些情况下解决此问题,但并非在所有情况下都能解决。
答案 1 :(得分:1)
正如其他人在注释中指出的那样,结构中元素的偏移量不是衡量它的可靠方法,因为您的编译器可以自由地向结构体插入填充以强制执行特定的成员对齐。
如果由于各种原因希望结构不包含填充,则必须使用编译器特定的编译指示来执行此操作。
对于GCC,Clang和MSVC,您必须在结构定义之前添加#pragma pack(push, 1)
,在其之后添加#pragma pack(pop)
。
答案 2 :(得分:0)
通常会对结构进行填充以进行对齐。 8字节类型需要8字节对齐,因此如果它遵循4字节类型,则可能会添加4个字节的填充,因此8字节类型可以正确对齐。一些编译器具有扩展,允许您将风对齐(如其他一些答案中所示),但这伴随着依赖于特定于编译器的扩展和未对齐成员的问题,这取决于底层硬件,可以导致速度惩罚甚至陷阱(例如,SPARC无法处理未对齐的访问)。
如果你首先通过严格的对齐要求对结构进行排序,而不是依赖于这种行为,它应该很自然地在没有对齐问题的情况下很好地包装,尽管你可能仍然会在最后结束一些填充,因此结构本身可以对齐