Chinaunix首页 | 论坛 | 博客
  • 博客访问: 330247
  • 博文数量: 81
  • 博客积分: 4016
  • 博客等级: 上校
  • 技术积分: 800
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-17 18:38
文章分类

全部博文(81)

文章存档

2016年(5)

2015年(2)

2010年(1)

2008年(1)

2007年(4)

2006年(68)

我的朋友

分类: LINUX

2006-03-23 17:24:12

The GNU manual says,

 

Without a value specified, pack all structure members together without holes.When a value is specified (which must be a small power of two), pack structure members according to this value, representing the maximum alignment (that is, objects with default alignment requirements larger than this will be output potentially unaligned at the next fitting location.

 

The GCC compiler ignores "#pragma pack" statement when compiling for ARM platform, so

you should use "__attribute__((packed, aligned))", like this:

 

#if defined(__WINS__)

#define PACKED

#else

#define PACKED __attribute__((packed, aligned(1)))

#endif

 

#pragma pack(1)

 

typedef struct {

char sig[104];

unsigned char verl,key;

int nFile, dFile, crc;

}PACKED avl1;

 

typedef struct {

char name[22];

int length, crc32;

}PACKED avd1;

 

#pragma pack(4)

 

解答:

对于问题1/2:

默认的内存对齐方式以及内存对齐规则在不同的系统上是有区别的,所以GCC编译器在这一点上在不同的系统上也是区别对待的。虽然到现在也没有看到GCCLinux系统下的使用 #pragma pack(N) 的内存对齐规则,但是从我测试的结果来看是这样:默认的对齐是按照 int 型(4字节)对齐,如果指定 #pragma pack(N) 中的 N 的话,N 不能大于默认对齐指定的长度,即如果默认对齐是 4 的话,N的取值可以是 124,超过4 之后作为 4 处理。在 Windows 等系统上似乎没有这个限制。

对于gcc在编译时使用 -fpack-struct[=n] 选项

 

对于问题3:

每个变量占用的空间是由 sizeof 值决定的,所以应该是12而不是16。至于两个变量的地址之差是16而不是12说明了这两个变量是不连续的,中间有“空隙”,但这并不违背C/C++标准。至于这样做的原因,可能是将结构体变量的地址对齐到内存地址是16的倍数的地址上,从而提高结构体的存取效率。

如果你定义一个上述结构体类型的数组然后检查相邻元素的地址,就会发现相邻元素之间是连续的

----------------------------------------------

问题1

GCC4 #pragma pack(8) 似乎有问题。

[code]#pragma pack(8)

struct A {

  char a;     // 1

  double b; // 8

};

[/code]

这个结构体的大小在 VC 下得到的是 16(按照8bytes补齐),但是在GCC4FC4)下得到的是12。我请教了其它朋友,分别试验了Linux + GCC 3.2 FreeBSD + GCC 3.4.2,得到的都是 12MinGW 3.3 Windows 平台得到的是 16。在官方文档中没有找到相关的信息,Google 看到说 GCC是支持 1, 2, 4, 8, 16 pragma pack(N) 的。gcc3.3.5也是12,并且如果定义两个A的临时变量并打印他们的地址,就会发现更奇怪了.变量竟然占用16个字节的空间..不过用int代替double就很正常,double真是奇怪..

 

问题2

#pragma pack(16)

typedef struct t1{

        char c;

        int d;

} t1;

//#pragma pack(1)

main()

{

        t1 a,b;

 

        printf("%d\n",sizeof(a));

        printf("%d\n",sizeof(b));

 

        printf("%p\n",&a);

        printf("%p\n",&b);

}

[/code]

输出的是

8

8

0xbfffed40

0xbfffed38

为什么sizeof还是8

 

问题3

再来一个double的问题

[code]

#pragma pack(4)

typedef struct t1{

       char c;

       double d;

} t1;

 

main()

{

       t1 a,b;

 

       printf("%d\n",sizeof(a));

       printf("%d\n",sizeof(b));

 

       printf("%p\n",&a);

       printf("%p\n",&b);

}

[/code]

输出的是

12

12

0xbfffed30

0xbfffed20

 

为什么变量b占用的空间是16个字节呢?

======================================

有的嵌入式处理器的寻址方式决定了在内存中占2字节的int16、uint16等类型数据只能存放在偶数内存地址处,占4字节的int32 、uint32 等类型数据只能存放在4的整数倍的内存地址处;占8字节的类型数据只能存放在8的整数倍的内存地址处;而在内存中只占1字节的类型数据可以存放在任意地址处。由于这些限制,在这些平台上编程时有很大的不同。首先,结构体成员之间会有空洞,比如这样一个结构:

    typedef struct test{
    char a;
    uint16 b;
    }TEST

    结构TEST在单字节对齐的平台上占内存三个字节,而在以上所述的嵌入式平台上有可能占三个或四个字节,视成员a的存储地址而定。当a存储地址为偶数时,该结构占四个字节,在a与b之间存在一个字节的空洞。对于通信双方都是对结构成员操作的,这种情况不会出错,但如果有一方是逐字节读取内容的(通信协议大都如此),就会错误地读到其它字节的内容。其次,若对内存中数据以强制类型转换的方式读取,字节对齐的不同会引起数据读取的错误。因为假如指针指在基数内存地址处,我们想取得占内存两个字节的数据存放在uint16型的变量中,强制类型转换的结果是取得了该指针所指地址与前一地址处的数据,并没有按照我们的愿望取该指针所指地址与后一地址处的数据,这样就导致了数据读取的错误。

    解决字节对齐有许多方法,比如可以在GCC的项目管理文件MakeFile中增加编译选项--pack-struct;但这种方法只能去除结构中的空洞,并不能解决强制类型转换引起的错误。为了增强软件的可移植性以及和同类其它平台产品的互通性,我们在收数据包处增加了拆包的函数,发数据包处增加了组包的函数。这两个函数解决了字节序的问题,也解决了字节对齐的问题。即组包时根据参数中的格式字符串将内存中的不同数据类型的某段数据放在指定地址处,组成包发给下层;拆包时,根据参数中的格式字符串将收到的内存中的数据存放在不同类型的变量或结构成员中。在函数中针对不同的数据类型作不同的处理。

阅读(3705) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~