搜索
bottom↓
回复: 218

今晚才基本弄明白STM32是怎样给结构体分配内存空间的?【有几道题大家做做,稍后给出今

[复制链接]

出0入0汤圆

发表于 2008-10-27 22:28:20 | 显示全部楼层 |阅读模式
之前对结构体的内存分配理解不够,特别是32位机,最近在STM32上移植M8的程序时遇到了问题,经过仔细分析是对内存分配理解不彻底造成的问题,今晚特地对STM32的结构体内存分配做了一下测试。现愿意与各位共享,特别是向我一样的菜鸟,呵呵

1、

struct student

{

    u8 num;

    u8 name;

    //u16 sex;

    u16 score;

};

struct student myStudent;

sizeof(myStudent) = ????

2、

struct student

{

    u8 num;

    u8 name;

    u16 sex;

    u16 score;

};

struct student myStudent;

sizeof(myStudent) = ????

3、

struct student

{ 

    u8 num;

    u8 name;

    u8 sex;

    u16 score;

};

struct student myStudent;

sizeof(myStudent) = ????

4、

struct student

{

    u8 num;

    u8 name;

    u32 sex;

    //u16 score;

    //u8 id;

};

struct student myStudent;

sizeof(myStudent) = ????

5、

struct student

{ 

    u8 num;//

    u8 name;//

    u32 sex;

    u16 score;//

    //u8 id;

};

struct student myStudent;

sizeof(myStudent) = ????

6、

struct student

{ 

    u8 num;

    u8 name;

    u32 sex;

    u16 score;

    u8 id;

};

struct student myStudent;

sizeof(myStudent) = ????;



大家做做看,测测自己的理解,呵呵



稍后会给出我的测试结果。

出0入0汤圆

发表于 2008-12-8 14:56:37 | 显示全部楼层
也就是说,定义结构体的时候,不同类型的元素排列顺序不一样也直接影响占用空间,看来以后要注意了,以前以为占用的内存都一样呢。

出0入0汤圆

发表于 2008-12-5 21:24:15 | 显示全部楼层
MARK

出0入0汤圆

 楼主| 发表于 2008-11-28 19:56:59 | 显示全部楼层
不好意思,好久没上来了。

说一下我的测试答案吧,

4、6、6、8、12、12



谢谢楼上的几个兄弟,我对这方面的理解又加深了。

出0入0汤圆

发表于 2008-10-29 09:06:24 | 显示全部楼层
再补充一下,上面各位所述应该受到编译环境的限制,比如IAR 4.42下设置如附图

iar下的设置 (原文件名:yi.JPG) 

出0入0汤圆

发表于 2008-10-29 09:03:04 | 显示全部楼层
有两个对齐,一个是结构体元素对齐,一个是结构体整体对齐。

结构体整体对齐是结构体元素对齐最大的。



上面说的填充对齐就是结构体整体对齐。



typedef struct 

{ 

u64    a;     

u32    b;

u16    c;

u8     d;

}TItem



整体对齐8,sizeof(TItem)=16

出0入0汤圆

发表于 2008-10-29 04:22:05 | 显示全部楼层
4,4,6,6,10,11



编译器默认按处理器字长对齐.否则使用紧缩.但很多处理器上使用紧缩方式将导致core error!X86是少数支持直接读取非对齐数据的处理器之一.



"对齐"是RISC计算机的"通病".所谓对齐,就是一个变量不会跨越2个字.为啥?这跟RISC的取指方式有关,RISC每次读取存储器,都是一个完整的字长,换个说法就是,每次取数据的首地址必为4的倍数,一次取4个字节.如果变量未对齐,则该变量会分散在两个字中,取数时势必只能取回该变量的一部分,于是出错.X86由于设计特殊,可以从任意地址开始读取一个字长宽度而不必非得从4的倍数地址开始,所以无需对齐.



对齐原则为:

单字节变量无需对齐,可放在任何地址

双字节变量起始地址为2的倍数

4字节变量首地址为4的倍数



struct {

u8    偏移量:0

u8    偏移量:1 ---- 单字节变量的首地址可以为任意位置

}



struct {

u8    偏移量:0

u16   偏移量:2 ---- 双字节变量的首地址必须为2的倍数,所以该成员之前空出一字节,偏移量1的单元被浪费了.

}



struct {

u8    偏移量:0

u32   偏移量:4 ---- 4字节变量的首地址必须为4的倍数,所以该成员之前空出3字节,偏移量1~3的单元都被浪费了.

}

本贴被 rainyss 编辑过,最后修改时间:2008-10-29,04:51:14.

出0入0汤圆

发表于 2008-10-29 00:35:20 | 显示全部楼层
我也要学一下。

出0入0汤圆

发表于 2008-10-28 16:52:51 | 显示全部楼层
对上面的一个例子,在avrstudio里面实测mega128

结果是0x20,试验了一下,int占2字节,char占1个

分析可能是avr是8字节为单位的,所以不会浪费空间吧。

typedef struct    

{   

  unsigned char a;        //偏移地址0,对齐1 

  unsigned long long b;   //偏移地址8,对齐8 

  unsigned char c;        //偏移地址16,对齐8 

  unsigned long  long  d; //偏移地址24,对齐8 

  int e;                  //偏移地址32,对齐8 

}student1 



sizeof(student1)=40 

出0入0汤圆

发表于 2008-10-28 14:29:57 | 显示全部楼层
对就是这样的!

出0入0汤圆

发表于 2008-10-28 11:37:59 | 显示全部楼层
原来填充对齐方式不是固定4,而是由元素对齐方式在改变,只变大不变小。



、 

struct student    

{ 

    u8 num;   //偏移地址0,对齐1,填充对齐1

    u8 name;  //偏移地址1,对齐1,填充对齐1

    u16 score;  //偏移地址2,对齐2,填充对齐1

}; 

struct student myStudent; 

sizeof(myStudent) = 4



2、 

struct student 

{ 

    u8 num;  //偏移地址0,对齐1

    u8 name; //偏移地址1,对齐1

    u16 sex;  //偏移地址2,对齐2

    u16 score;  //偏移地址4,对齐2

}; 

struct student myStudent; 

sizeof(myStudent) = 6 





3、 

struct student 

{  

    u8 num;      //偏移地址0,对齐1,填充对齐1

    u8 name;     //偏移地址1,对齐1,填充对齐1

    u8 sex;      //偏移地址2,对齐1,填充对齐1

    u16 score;   //偏移地址4,对齐2,填充对齐2

}; 

struct student myStudent; 

sizeof(myStudent) = 6



 

4、 

struct student 

{ 

    u8 num;    //偏移地址0,对齐1,填充对齐1

    u8 name;   //偏移地址1,对齐1,填充对齐1

    u32 sex;   //偏移地址4,对齐4,填充对齐4

}; 

struct student myStudent; 

sizeof(myStudent) = 8 





5、 

struct student 

{  

    u8 num;      //偏移地址0,对齐1,填充对齐1

    u8 name;     //偏移地址1,对齐1,填充对齐1

    u32 sex;     //偏移地址4,对齐4,填充对齐4

    u16 score;   //偏移地址8,对齐4,填充对齐4

}; 

struct student myStudent; 

sizeof(myStudent) = 12



 

6、 

struct student 

{  

    u8 num;    //偏移地址0,对齐1,填充对齐1

    u8 name;   //偏移地址1,对齐1,填充对齐1

    u32 sex;   //偏移地址4,对齐4,填充对齐4

    u16 score; //偏移地址8,对齐2,填充对齐4

    u8 id;     //偏移地址10,对齐1,填充对齐4

}; 

struct student myStudent; 

sizeof(myStudent) = 12;





补充一个:



typedef struct   

{  

  unsigned char a;        //偏移地址0,对齐1,填充对齐1

  unsigned long long b;   //偏移地址8,对齐8,填充对齐8

  unsigned char c;        //偏移地址16,对齐1,填充对齐8

  unsigned long  long  d; //偏移地址24,对齐8,填充对齐8

  int e;                  //偏移地址32,对齐4,填充对齐8

}student1



sizeof(student1)=40





本贴被 ATmega32 编辑过,最后修改时间:2008-10-29,08:32:44.

出0入0汤圆

发表于 2008-10-28 10:33:15 | 显示全部楼层
  32CPU位会有32位对齐? 16位会有16位对齐这一说?

如果在64位电脑上模拟岂不更不对/。。。。 

出0入0汤圆

发表于 2008-10-28 10:25:52 | 显示全部楼层
有些问题试试就知道了

举个例子:

 1.    struct                                 2.     struct   

         {                                           {

           char a;                                       char a;

           int b;                                        char c;

           char c;                                       short d;

           short d;                                       int b;

         }                                             }

  存储方式:

 1.    |   *    |   *    |   *   |  a   |

       |b[31:24]|b[23:16]|b[15:8]|b[7:0]|

       |d[15:8] |d[7:0]  |   *   |  c   |



2.     | d[15:8]  | d[7:0] |  c   |  a   |

       |b[31:24]|b[23:16]|b[15:8]|b[7:0]|

这回大家明白了吧!

出0入0汤圆

发表于 2008-10-28 10:23:29 | 显示全部楼层
赞同2楼的解释,应该和编译器设置有关系

出0入0汤圆

发表于 2008-10-28 10:20:25 | 显示全部楼层
STM32好像不受存储器对齐限制的,去年参加宣讲会的时候会上是这么讲的



也就是用SIZEOF能得到实际使用的字节数,不会有对齐填塞产生的空字节

出0入0汤圆

发表于 2008-10-28 10:08:41 | 显示全部楼层
记忆中的这个规则应该是:以四字节对其,如果某个元素起始不是一个对齐存储块起始地址,且结尾超过了一个对齐存储块,则这个元素移到下一个对齐存储块起始位置存储。



1.4

2.8

3.8

4.8

5.12

6.12



不知道对不对?

本贴被 ATmega32 编辑过,最后修改时间:2008-10-28,11:51:43.

出100入0汤圆

发表于 2008-10-28 09:48:38 | 显示全部楼层
这种分配,主要和编译器有关系,和什么CPU的关系可能不是很大。

有的时候和CPU的堆栈有关系(还是因为编译器要考虑编译的效率)



个人理解,但是移植的时候弄清楚编译器即可,还是和CPU 没关系。

出0入0汤圆

发表于 2008-10-28 08:58:04 | 显示全部楼层
什么时候出答案,我也想知道答案是什么?因为我对结构体实在了解很少。根本不知道如何计算占用的内存。

出0入0汤圆

发表于 2009-3-5 10:10:02 | 显示全部楼层
关注,我在写stm32的flash发现了这个问题。

出0入0汤圆

发表于 2009-3-5 13:05:27 | 显示全部楼层
在 IAR 中可以用 #pragma data_aligment 来设置

出330入0汤圆

发表于 2009-3-5 15:05:17 | 显示全部楼层
这个问题倒是值得深入,转一篇文章过来:
                                      填充与对齐——指定变量的地址偏移

前言
    由于填充与对齐与硬件架构有很大关系,所以填充与对齐在一般的编程工作中很少涉及,但网站还是有不少关于对齐的技术文章。坦白的说,我并不认为这些文章抓住了要点,或者说,即使那些作者自己抓住了要点但并没有描述清楚。也许我看的这方面文章不多,但我想就这个问题写一篇清晰而简单的文章,结合我的理解和经验,用逻辑的思路描述出来。
    大家看到我的标题是“填充与对齐”,我就是想明确一个观点,即使它们二者充满了联系,但填充与对齐是不同的。这时候有些人可能也觉得填充与对齐不同,但当自己讲到对齐问题,就又和填充混到一起了。
    填充和对齐存在的根本原因是硬件架构的要求,让我们先从硬件架构的要求说起。
    计算机主要的架构就分为两类,复杂指令集计算机(CISC)和精简指令集计算机(RISC)。CISC最有代表性的架构就是x86,RISC最有代表性的架构就是ARM。不管是什么架构,对要访问的一定长度的数据的地址是有要求的,比如要访问一个32位的整数,那么这个数据必须(最好)存储在以4字节(32/8=4)对齐的地方。一般来说,RISC对对齐要求的更严格些,非对齐访问可能会带来性能上的损失。这对程序在不同架构间移植非常重要,因为它极有可能导致你的程序崩溃。
    不要问我为什么硬件要这么要求,你可以询问专业的硬件工程师,但是现在记住:我们必须对齐!

什么是对齐
    如果一段长度为n字节的数据d所存放的地址m能被n整除,那么d就是对齐的。可见,数据的对齐和自身长度和地址有关。在32位机器上,对于short类型的数据来说,将他们存放在偶数地址上,都是对齐的,否则奇数地址都是非对齐;对于int类型的数据来说,要将他们存放在能被4整除的地址上,才是对齐的。

非对齐访问的程序表现
    程序上怎么描述非对齐访问?让我们看看下面小段程序:
int *p;
//do some pointer offset
//…
int i = *p; //access
    *p就是访问数据,我们知道*p就是要通过指向int型数据的指针p获取数据,p中存的是数据的地址,如果p本身的值不是能被4整除的,那么用*p来访问int型数据就是非法的。具体操作系统的表现因机而异。

结构的填充
    一般来说,我们现在的程序上对齐都由编译器来完成,因为我们根本无法确保数据放在什么地方。因为我们都运作在虚拟地址空间上。对于普通的基本数据类型,实现这点不是很难。对于复合结构呢?我们需要给结构设计一个结构。
    当我们定义结构时,实际我们并没有给结构指定存储,我们只是在设计一个模板,一个存储的模板,让编译器帮助我们当我们需要结构变量时,编译器会自动按照我们设计好的存储模板放置结构的成员。
    按照前面说的对齐的定义,要使*p(p指向结构变量)不出错,p的值必须是结构大小的整数倍。由于p本身的值由编译器指定,那么焦点就落在了结构的大小上了。由于我们定义结构时只是在定义一个模板而真正结构变量的地址由编译器保证,那么我们只需考虑结构成员相对偏移是符合对齐标准就行了。
struct _Struct
…{
int i;
char c;
short s;
char c2;
};
    我们先来看第一个结构成员i,它在结构模板中相对偏移是0,也就是说只要结构对齐了(结构对齐的值肯定大于int)那么i肯定是对齐的。这里补说一个概念——自对齐,每个类型都有它的自对齐,对于char它的自对齐就是1,对于int它的自对齐就是4,依次推理。只要数据所在地址是该数据自对齐的整数倍,那该数据肯定是对齐的。结构也是有自对齐的,至于如何计算,我们后面再讲。我们接着偏移往后讲。
    由于存在访问结构成员的可能性,所以结构的成员也必须是对齐的。这个对齐很容易做到,因为结构内的成员都是根据相对偏移来做的,以0为基准。我们继续看前面定义的结构。i必然是对齐的,它开始存放的地址偏移为0,然后它占用了4个字节的自身长度的存储。c好养活,任何地址它都可以对齐,它也只占用了一个字节的存储,挨着i放。s怎么放呢?它为了对齐不能紧挨着c放,因为紧挨着c的地址偏移是5,显然不能被short类型的自对齐长度2整除,所以没办法,s需要向后挪一个字节开始存放,并从此向后占用2个字节的存储。问题出来了,s向后挪了一个字节,它与c之间没有存储任何有效数据的地方怎么办呢?这个地方会在生成结构变量时由编译器随机生成数据填补空间。我们把这样的方式叫做结构填充(pad),把填充的数据叫做填充数据(pading data)。接下来,数据c2也挨着s放,c2本身占用1个字节的存储。问题又出现了,c2后面还需要填充吗?填充多少?这两个问题非常关键,因为他们决定了结构的大小,这两个问题确定不下来,编译器就无法为我们生成结构变量。编译器很生气,后果很严重。

结构的自对齐
    根据我们解决基本数据类型的经验,我们只需要给结构规定一个自对齐就可以解决问题,它该填充多少就填充多少。那么结构的自对齐是如何确定的呢?结构的自对齐其实完全取决于它的成员。结构的自对齐取决于其成员的自对齐,结构的自对齐值等于其成员的自对齐的最大值。比如,前面定义的结构,结构成员自对齐最大的就是int了(等于4),那么结构的自对齐就是4。假如结构中有一double变量(长度和自对齐为8),那结构的自对齐就是8。前面的最后一个问题终于得到了解决,根据自对齐理论,前面的结构需要根据自身的自对齐进行补齐,所以后面还需要部3字节。这样整个结构的大小就确定了下来,回想一下前面说的,我们做个简单的加法:4+1+1+2+1+3=12(我为什么这么写?这些数字分别代表了什么意思?)。

结构的最大对齐及其控制
    结构本身的大小我们已经通过规范能够确定下来,当我们定义结构变量时,编译器就会根据其大小自动帮助我们将结构放置在对齐的合适的位置。是不是每个结构变量我们都需要按照结构的自对齐补齐呢?
    有时候我们需要生成一些紧凑的结构(compact structure),这种情况在写底层程序时比较常见,比如我们通过定义一个与硬件数据相符的程序结构,方便与硬件交换信息。那么这个时候结构本身的定义重点是在与硬件规范相符,而不是由编译器损益分配。这点和结构变量的对齐没关系,我们是说通过把结构内部变量紧缩排列,以达到符合硬件规范或缩减存储占用的目的,这个确实和结构的对齐没关系。
    VC(不是指风投也不是指维生素)为我们提供了一个机制让我们可以控制结构的内部排列——那就是结构最大对齐,也叫结构成员对其,我个人更倾向于前者的叫法。结构最大对齐和结构自对齐是与结构对齐有关的最重要的两个属性。通过调整结构最大对齐可以调用结构最后的补齐甚至成员的排列。
    调整结构最大对齐有两种方式:一种是静态的;一种是动态的。静态就是(VC6环境下)Alt+F7调出工程设置C/C++选项卡,Code Generation类别,Structure member alignment选项,如下图;动态就是利用编译器指令#pragma pack(n)在程序中动态设置,n就是结构最大对齐。


(原文件名:Structure member alignment.jpg)

    当然还有堆栈式用法,请参考MSDN。

不光最后的补齐受制于结构最大对齐,中间结构成员间的填充也受制于它。比如:
struct _Struct
…{
char c;
int i;
char c2;
};
    按照我们前面的里面这个结构的大小sizeof(_Struct)等于12。但我们这里动态给它设置结构最大对齐(和静态方式效果是一样的),代码如下:
#pragma pack(2)
struct _Struct
…{
char c;
int i;
char c2;
};
    大家猜猜该结构大小是多少?本来结构的自对齐是4(等于i的自对齐),但现在的结构最大对齐才是2,c与i之间还会按照以前的规则来填充吗?c2后面还会按照结构自对齐来补齐吗?答案就像结构最大对齐的名字一样,它是一种限制,只要能限制得了你(小于结构自对齐),它还真要亮亮它的剑。比如这个结构,c与i之间本来是应该编译器帮我们自动填充3个字节的,现在不行了,人家结构最大对齐发话了,最大按2对齐,这样c与i之间就只能填充1个字节,同理,c2后面也只能补一个字节。最终结构的大小为:1+1+4+1+1=8个字节。这个“万恶”的结构最大对齐。

真正的对齐
    就像文章开始时说的,对齐和填充最容易混淆,主要是术语上的含糊导致理解上的模糊。经过前面一大段的说明,你是否对对齐有点模糊了呢?我现在就把它提出来说。
    其实,我们前面就几乎没说对齐的问题,说的都是填充!你还记得前面我说过一句话,对齐默认情况下是由编译器保证的,包括基本变量、结构变量等。这样的话,我们说的自对齐、最大对齐实际上都是填充的概念。真正的对齐是什么概念?结合最初我们对对齐的定义,我们不难得出,真正的对齐就是要我们有控制编译器放置变量位置的能力。
    VC(2005)为我们提供了这样一个机制,一个编译器指令让我们控制变量的对齐:__declspec(align(n))
    在具体解释这个指令如何使用前,我希望先说一下,为什么对齐会影响填充。其实前面的解释也已经有了隐隐约约的印象。对于基本类型变量,指定了对齐,“根本”就没有填充的问题,它只占用自己的自然长度,而对于结构这样的复合类型,就有问题了。我们用归谬法来说明问题。假设指定对齐只管存放结构变量的首地址,而不管其填充,尤其是补齐时填充,那么当你要生成结构数组时,编译器就会毫无办法,因为它不知道结构的大小。不管什么情况,即使有多种约束,但编译时,结构的大小必须是确定的。
    让我们看看对齐指令(__declspec(align(n)))对结构大小的影响。如:
struct __declspec(align(16)) _Struct
…{
int a;

};
    这里没有动态利用prama指令设置结构最大对齐,那么就是说结构的最大对齐采用的是默认的静态配置的8字节。结构定义时,我们通过对齐指令定义了其对齐为16字节,也就是说它的放置地址肯定是能被16整除的,再加上要解决其他问题(数组排列)还需要对结构进行补齐填充。到底填多少呢?我们可以这么理解,开始阶段结构最大对齐和结构自对齐他们经过商量,达成协议根据4字节对结构进行填充,对齐指令来了后,发现结构的对齐不符合自己的要求,就按照自己的设置16字节对结构进行补齐填充,最终结构末端被填充了16-4=12字节。可以说,结构补齐填充是个软蛋,谁都可以欺负它,当然最终还是按照实力强(对齐数值大)的规矩办。所以最终上面结构的大小是16字节。
    那对齐指令对结构成员有什么影响呢?让我们看看下面这个例子:
struct _Struct
…{
char c;
int __declspec(align(8)) i;
short s;
};
    我们知道结构的自对齐和基本类型的自对齐是对应的,那这里对基本类型的对齐进行设置和前面对结构对齐进行设置有什么联系呢?其实是一回事。对于基本类型,不管是全局的,还是结构成员,还是将其自对齐和对齐指令设置的对齐进行比较,最终将数据挪到符合要求的地方。所以像结构中成员i的偏移地址就是8而非原来的自对齐4。对齐指令还是厉害啊。
    对于这个结构的大小,就需要结合我们开始讲的原理了,既然成员i的对齐已经成了8,那么它最终必将影响结构的自对齐,也就是说最终结构的自对齐就等于结构中成员的最大对齐即8。所以最终结构补齐填充还是要按照结构的自对齐来办,所以这个结构的最终大小为1+7+4+2+2=16(我怎么计算的?)。有兴趣的可以试试在全局定义一个变量,如:
char __declspec(align(16)) global_c;
printf(“%08X “, &global_c);
    看看16进制的地址最后一位是不是为0。

总结
    本文旨在通过从原理上和逻辑上的描述,帮助理解,加深记忆。当然为了我叙述的准确我还是做了试验的,一些显而易见的我就偷了懒。各位可以对上面的原理进行测试验证,欢迎批评指正。有兴趣的,也可以参考其他有关填充与对齐的文章,但我觉得还是我描述的最清晰了,如果有不清晰的,那就请告诉我,我会使它更清晰。

出330入0汤圆

发表于 2009-3-5 15:55:28 | 显示全部楼层
结构体对齐的具体含义(#pragma pack)
作者:panic 2005年4月2日

还是来自csdn的帖子:
主  题:   探讨:内存对齐
作  者:   typedef_chen ((名未定)(我要骗人))
等  级:   
信 誉 值:   100
所属论坛:   C/C++ C++ 语言
问题点数:   50
回复次数:   1
发表时间:   2005-04-02 22:53:27
   
朋友帖了如下一段代码:
  #pragma pack(4)
  class TestB
  {
  public:
    int aa;
    char a;
    short b;
    char c;
  };
  int nSize = sizeof(TestB);
  这里nSize结果为12,在预料之中。

  现在去掉第一个成员变量为如下代码:
  #pragma pack(4)
  class TestC
  {
  public:
    char a;
    short b;
    char c;
  };
  int nSize = sizeof(TestC);
  按照正常的填充方式nSize的结果应该是8,为什么结果显示nSize为6呢?

    事实上,很多人对#pragma pack的理解是错误的。#pragma pack规定的对齐长度,实际使用的规则是:结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。

具体解释
#pragma pack(4)
  class TestB
  {
  public:
    int aa; //第一个成员,放在[0,3]偏移的位置,
    char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
    short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
    char c; //第四个,自身长为1,放在[8]的位置。
  };
    这个类实际占据的内存空间是9字节
    类之间的对齐,是按照类内部最大的成员的长度,和#pragma pack规定的值之中较小的一个对齐的。
    所以这个例子中,类之间对齐的长度是min(sizeof(int),4),也就是4。
    9按照4字节圆整的结果是12,所以sizeof(TestB)是12。

    如果
#pragma pack(2)
    class TestB
  {
  public:
    int aa; //第一个成员,放在[0,3]偏移的位置,
    char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
    short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
    char c; //第四个,自身长为1,放在[8]的位置。
  };
//可以看出,上面的位置完全没有变化,只是类之间改为按2字节对齐,9按2圆整的结果是10。
//所以 sizeof(TestB)是10。

最后看原贴:
现在去掉第一个成员变量为如下代码:
  #pragma pack(4)
  class TestC
  {
  public:
    char a;//第一个成员,放在[0]偏移的位置,
    short b;//第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。
    char c;//第三个,自身长为1,放在[4]的位置。
  };
//整个类的大小是5字节,按照min(sizeof(short),4)字节对齐,也就是2字节对齐,结果是6
//所以sizeof(TestC)是6。

感谢 Michael 提出疑问,在此补充:

    当数据定义中出现__declspec( align() )时,指定类型的对齐长度还要用自身长度和这里指定的数值比较,然后取其中较大的。最终类/结构的对齐长度也需要和这个数值比较,然后取其中较大的。

    可以这样理解, __declspec( align() ) 和 #pragma pack是一对兄弟,前者规定了对齐的最小值,后者规定了对齐的最大值,两者同时出现时,前者拥有更高的优先级。
    __declspec( align() )的一个特点是,它仅仅规定了数据对齐的位置,而没有规定数据实际占用的内存长度,当指定的数据被放置在确定的位置之后,其后的数据填充仍然是按照#pragma pack规定的方式填充的,这时候类/结构的实际大小和内存格局的规则是这样的:
    在__declspec( align() )之前,数据按照#pragma pack规定的方式填充,如前所述。当遇到__declspec( align() )的时候,首先寻找距离当前偏移向后最近的对齐点(满足对齐长度为max(数据自身长度,指定值) ),然后把被指定的数据类型从这个点开始填充,其后的数据类型从它的后面开始,仍然按照#pragma pack填充,直到遇到下一个__declspec( align() )。
    当所有数据填充完毕,把结构的整体对齐数值和__declspec( align() )规定的值做比较,取其中较大的作为整个结构的对齐长度。
特别的,当__declspec( align() )指定的数值比对应类型长度小的时候,这个指定不起作用。

出0入0汤圆

发表于 2009-3-5 16:31:14 | 显示全部楼层
#progma pack
好像只是微软 C/C++ 的扩展,并不通用吧?
我查了查 ARM RVDS 3.1 编译器手册没有看到 #progma pack,或许没有看到?

出0入0汤圆

发表于 2009-3-11 14:41:16 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-3-11 18:02:20 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-3-11 20:02:06 | 显示全部楼层
ads下是用  __packed

出0入0汤圆

发表于 2009-3-11 22:45:44 | 显示全部楼层
MARK

出0入0汤圆

发表于 2009-3-12 16:33:21 | 显示全部楼层
mark

good

出0入0汤圆

发表于 2009-3-13 09:18:08 | 显示全部楼层
用__packed 了事~省空间,也不用烦,读写文件和数据必须用的

出0入0汤圆

发表于 2009-3-13 11:26:22 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-3-13 23:29:42 | 显示全部楼层
据说可以对齐的吧

出0入0汤圆

发表于 2009-3-17 14:37:04 | 显示全部楼层
顶,学习了

出10入95汤圆

发表于 2009-3-17 22:37:36 | 显示全部楼层
好贴!遇到过,但没有像楼主和楼上各位,这样深的研究下来。

出0入0汤圆

发表于 2009-4-5 01:43:13 | 显示全部楼层
学习了,踩一脚收藏。

出0入0汤圆

发表于 2009-4-5 12:10:11 | 显示全部楼层
好,都理解了。

struct _Struct
…{
char a[4];
int b;
};
这个sizeof(_Struct)又是多少呢?=8?

出0入0汤圆

发表于 2009-4-6 00:58:36 | 显示全部楼层
12楼说的很关键啊,画龙点睛啊

出0入0汤圆

发表于 2009-4-6 01:00:27 | 显示全部楼层
回zhangxk
struct _Struct  
…{  
char a[4];  
int b;  
};
这个sizeof(_Struct)又是多少呢?=8?
应该=6吧

出75入4汤圆

发表于 2009-4-6 08:55:37 | 显示全部楼层
mark。讨论的好。

出0入0汤圆

发表于 2009-4-12 14:37:01 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-4-13 08:32:20 | 显示全部楼层
32位机对齐方式和PC机软件对齐方式是一样的
都是根据大小调整对齐方式

8位机只按1字节方式对齐

我用8位机和电脑数据通讯,接收的数据是没有问题,但是就是由于对齐方式不一样,转换出来的数据不一样
使用共同体进行转换时 出现了偏差,

后来只好在8位机上人工对齐, 才行

出0入0汤圆

发表于 2009-4-14 18:31:09 | 显示全部楼层
楼主你的问题问的很对么??
应该是编译器分配内存吧 晕倒

出0入0汤圆

发表于 2009-4-15 15:02:33 | 显示全部楼层
good
我看了20楼前半部分的讨论,认为楼主给的答案是正确的
但20楼没有说明白最后一个数据怎样放置才能让整个结构体对齐
也没有说明白为什么第一个一定是对齐的
我觉得:
在出现结构体数组的情况下,结构提的字节数必须被结构体自对齐整除,这样才能保证紧着着的下一个结构体对齐,这就解释了最后一个数据该pad几个,还有因为数据都是1248~~~~所以最大的一定能让最小的整除,所以第一个一定是字对齐的,不管什么类型

欢迎拍砖~~~~~~~~~~~~~~~~~~~~~

出0入0汤圆

发表于 2009-4-15 15:17:05 | 显示全部楼层
习惯使用__packed处理

出0入0汤圆

发表于 2009-4-19 21:26:21 | 显示全部楼层
说两句:

__packed如果不考虑效率的话,自然是很好的方法。如果考虑效率,就不好了,不如调整排列顺序来的合算。

结构体成员的对齐,是编译器实现决定的。不在一个具体的体系结构和编译器框架下讨论,很难得出有意义的结果。但是有助于理解含义。C99关于这个得描述是:
Each non-bit-field member of a structure or union object is aligned in an implementation-defined manner appropriate to its type.

对于结构体起始地址的定义,C99是这么规定的:
Apointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.
也就是说,结构体起始地址是多少对齐的,没有规定。比如一个uint8_t类型,可以是任意字节对齐,只要给指针的地址指向这个变量,对于后边结构体成员的Offset编译器能正确处理,就行了。只是说,开始不能有padding。

对于结构体整体对其,C99有这么句话:There may be unnamed padding at the end of a structure or union.
也就是说,如果编译器把结构体整体算成,比如4字节倍数,那么sizeof也可能比预想的大,不仅仅包含数据,中间的padding。由可能有后边的。padding。

出0入0汤圆

发表于 2009-4-19 22:23:10 | 显示全部楼层
答案应为:4、6、6、8、12、12,对齐的时候应以结构体中字节数最大的成员做参考,对于小于最大成员的的连续成员可以加在一起来计算。

出0入0汤圆

发表于 2009-4-25 12:15:32 | 显示全部楼层
看看

出0入0汤圆

发表于 2009-7-19 10:33:52 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-7-30 10:54:30 | 显示全部楼层
牛人还是不少!

出0入0汤圆

发表于 2009-7-31 15:38:37 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-7-31 16:03:10 | 显示全部楼层
顶下

出0入0汤圆

发表于 2009-8-28 16:20:36 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-8-28 16:41:07 | 显示全部楼层
最近在看arm调试与优化这本书的时候,看到作者强调边界不对齐和字节排序的问题,这个问题以前老师也

提到过,没引起重视,这次上网查查,又在keil上走了一遍,发现还真有问题,如下:

char buff[8] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xbc, 0xcd};
int v32, *p32;
short v16, *p16;
p32 = (int*)&( buff[1] ); //unalignment
p16 = (short*)&( buff[1] ); //unalignment
v32 = *p32; //what’s the result?
v16 = *p16; //what’s the result?

在keil上得到的结果是:

v32 = 0x9a785634
v16 = 0x5634

分析原因:由于buffer中的数据是字节排列的,在keil中buffer的起始地址为:


2 (原文件名:2.jpg)


可见,起始地址最低位为:0b1100,汇编伪指令为:

     9:         p32 = (int*)&( buff[1] ); //unalignment
0x000001C4 E28D2001 ADD       R2,R13,#0x00000001
    10:         p16 = (short*)&( buff[1] ); //unalignment
0x000001C8 E28DC001 ADD       R12,R13,#0x00000001
    11:         v32 = *p32;
0x000001CC E5921000 LDR       R1,[R2]
    12:         v16 = *p16;
0x000001D0 E1DC30F0 LDRSH     R3,[R12]

在第11行LDR R1,[R2]中R2的地址为0b1101,此时为非对齐地址,LDR从一个非对齐地址得到一个字数据,会导致取数据错误!

同理对于16位数据的取出也会造成错误。如何解决这个问题,在对速度要求不是很严格的地方,可以编写两个函数,解决从边界可能不对其的存储器中取出4字节或2字节数据的问题。

函数如下:

int byte_to_word(char *data)
{
int a0,a1,a2,a3;
a0=*(data++);
a1=*(data++);
a2=*(data++);
a3=*(data++);
return a0|(a1<<8)|(a2<<16)|(a3<<24);
}


int byte_to_halfword(char *data)
{
int a0,a1;
a0=*(data++);
a1=*(data++);
return a0|(a1<<8);
}

此时再测试:

char buff[8] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xbc, 0xcd};
int v32, *p32;
short v16, *p16;
v32 = byte_to_word(&buff[1]); //unalignment
v16 = byte_to_halfword(&buff[1]); //unalignment

得到了正确的数据:


3 (原文件名:2.jpg)
这个是我通过实验得到的结论

出0入0汤圆

发表于 2009-8-28 17:14:46 | 显示全部楼层
好帖自动马克

出0入0汤圆

发表于 2009-8-28 17:39:02 | 显示全部楼层
这个 觉得可以 cool?

出0入0汤圆

发表于 2009-9-17 14:41:29 | 显示全部楼层
struct的对齐一般是保证下一个成员的偏移位置为该成员大小的整数倍。

出0入0汤圆

发表于 2009-9-17 16:55:56 | 显示全部楼层
做好标记,慢慢看

出0入0汤圆

发表于 2009-9-17 18:35:44 | 显示全部楼层
支持一哈

出0入0汤圆

发表于 2009-9-18 15:59:38 | 显示全部楼层
MARK

出0入0汤圆

发表于 2009-9-20 09:00:15 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-9-23 11:15:59 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-9-23 14:37:01 | 显示全部楼层
记个号以后好查阅

出0入4汤圆

发表于 2009-9-27 10:29:49 | 显示全部楼层
绝对值得学习的帖子,留下标记。。。

出0入0汤圆

发表于 2009-9-28 23:42:23 | 显示全部楼层
说的都不太准备。只和编译器有关》加入PACKED修饰符你再试试。看是怎么分配空间的。
不加的时候分配是怎么分配的》

出0入0汤圆

发表于 2009-12-11 16:10:41 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-11 16:45:06 | 显示全部楼层
是啊 ,这个跟编译器的设置有关,看你怎么配置对齐方式了,你弄成字节对齐试试,肯定和字节数一样多 。

出0入0汤圆

发表于 2009-12-20 19:18:24 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-12-20 20:47:48 | 显示全部楼层
MARK

出0入0汤圆

发表于 2009-12-20 21:13:46 | 显示全部楼层
good

出0入0汤圆

发表于 2010-1-3 02:20:50 | 显示全部楼层
记号

出0入0汤圆

发表于 2010-3-15 23:28:17 | 显示全部楼层
爪子

出0入0汤圆

发表于 2010-4-19 11:09:19 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-19 16:24:59 | 显示全部楼层
记号

出0入0汤圆

发表于 2010-4-19 16:53:16 | 显示全部楼层
记号

出0入0汤圆

发表于 2010-4-19 17:17:22 | 显示全部楼层
顶一下!!!

出0入0汤圆

发表于 2010-4-23 09:20:04 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-23 11:22:25 | 显示全部楼层
记号

出0入0汤圆

发表于 2010-4-23 11:48:22 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-23 12:26:06 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-23 13:26:44 | 显示全部楼层
先马克一下

出0入0汤圆

发表于 2010-4-23 16:47:24 | 显示全部楼层
学习了

出0入0汤圆

发表于 2010-4-23 17:08:20 | 显示全部楼层
呵呵,谢谢,刚刚解决了我那个奇怪的问题!
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3993571&bbs_page_no=1&bbs_id=3020

出0入0汤圆

发表于 2010-4-23 20:32:56 | 显示全部楼层
值得一看

出0入0汤圆

发表于 2010-4-25 19:29:06 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2010-4-26 17:22:24 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-2 12:46:49 | 显示全部楼层
绝对要顶下
MARK

出0入0汤圆

发表于 2010-5-2 15:10:25 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-2 15:53:29 | 显示全部楼层
学习了!

出0入0汤圆

发表于 2010-5-5 15:20:08 | 显示全部楼层
受教了~~~

出0入0汤圆

发表于 2010-5-5 17:19:14 | 显示全部楼层
顶顶

出0入0汤圆

发表于 2010-5-5 18:33:49 | 显示全部楼层
学习了

出0入0汤圆

发表于 2010-5-12 00:28:15 | 显示全部楼层
学习了

出0入0汤圆

发表于 2010-5-12 08:23:51 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-5-27 15:39:22 | 显示全部楼层
mark  以后也许会看的

出0入0汤圆

发表于 2010-7-21 21:50:56 | 显示全部楼层
mark,以后看看

出0入0汤圆

发表于 2010-7-21 22:30:27 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2010-7-22 08:03:11 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-7-22 08:16:26 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-22 09:02:50 | 显示全部楼层
mark!!!

出0入0汤圆

发表于 2010-8-16 23:16:31 | 显示全部楼层
向大家学习!

出0入0汤圆

发表于 2010-8-27 20:55:04 | 显示全部楼层
mark,学习。。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-5 09:24

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表