打印

闲来无事,讨论个大家都容易忽略的问题

[复制链接]
楼主: kiton_law
手机看帖
扫描二维码
随时随地手机跟帖
21
kiton_law|  楼主 | 2009-11-30 19:24 | 只看该作者 回帖奖励 |倒序浏览
本帖最后由 kiton_law 于 2009-11-30 19:48 编辑

回19楼

你即便是转成U8,U16,U32不过是换个名字而已,在讨论对齐问题的时候用char short int是一样的,我已经明确说了int是32位的,转成U8,U16,U32是为了消除移植中基本变量长度改变的问题,但并不能消除变量对齐带来的问题,比如你把寄存器组结构定义成一个结构体,如果不明白变量对齐的原理和细节,即便是你定义了U8U16U32又怎么样呢,问题还是一样的。基本变量长度和数据对齐是两个问题,数据对齐关系到复杂类型变量的长度,你说的跟我说的根本就不是一回事。

至于你说的脱离了具体环境讨论是无意义的,这样说未免有狡辩的嫌疑,否则,就应该象20楼那样举出反例,而不是说一些谁都会说的没营养的话,如果你觉得这个问题无聊,你可以不回帖。

使用特权

评论回复
22
kiton_law|  楼主 | 2009-11-30 19:27 | 只看该作者
本帖最后由 kiton_law 于 2009-11-30 19:29 编辑

20楼上的IAR编译器肯定用了pack(4),自己找找,要不就是默认pack(4)的,注意最后那个是 long long,不是long。

使用特权

评论回复
23
arm_fan168| | 2009-11-30 19:31 | 只看该作者
对齐方式确实有个选项,一个是4,一个是8,但这两个都不影响使用吧?默认的选项就是4字节对齐。

使用特权

评论回复
24
kiton_law|  楼主 | 2009-11-30 19:45 | 只看该作者
本帖最后由 kiton_law 于 2009-11-30 19:50 编辑

ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。

也就是说如果结构体中最大的对齐边界是8,那么结构体的对齐边界必然不小于8。
也就是说,结构体的自然边界等于结构体内部最长字段的自然边界。
这是ANSI C规定的。

如果20楼测试的结果是20,并且没有任何的pack选项,那只能说明IAR里long long的对齐长度是4,要验证这个结果很容易,可以试着定义一些长度不同的变量,看看有没有long long类型的变量被安排在4或c结尾的地址上。如果IAR真是如此,那证明还真是有特例的,我手头没有IAR,所以只有IAR没有试。

但如果果真如此,那说明你在IAR中定义的一个具有4字节以上长度位段的结构体(比如含有longlong或double的结构体),他的sizeof值在RVMDK中将没有兼容性。如果这个结构体用于定位寄存器组,将产生错误。

使用特权

评论回复
25
kiton_law|  楼主 | 2009-11-30 19:46 | 只看该作者
至于此帖的讨论,有意义没意义大家自己看,反正程序写错是自己的事情,我属于吃饱了撑的。

使用特权

评论回复
26
kiton_law|  楼主 | 2009-11-30 19:55 | 只看该作者
本帖最后由 kiton_law 于 2009-11-30 19:59 编辑
对齐方式确实有个选项,一个是4,一个是8,但这两个都不影响使用吧?默认的选项就是4字节对齐。
arm_fan168 发表于 2009-11-30 19:31



有影响,这个应该就是pack值选项了(不是align选项),一般的编译器不做规定的时候,默认的pack值是无限大(全部按照自然边界对齐),但C语言中不会存在超过8字节的基础类型(最长就是longlong和double了,也就8字节),因此任何数据类型的自然边界都不会超过8,包括结构体,所以实际上pack值最大也就是8了,因此IAR选择8为pack参数相当于不指定pack值。

如果你选4,则相当于使用#pragma pack(4),这样所有比4字节长的基础类型都是按照4字节为自然边界的,结果自然是20。你可以试试,选择8,应该结果就是24了,并不影响一般数据的地址,但是对结构体长度有影响。

使用特权

评论回复
27
arm_fan168| | 2009-11-30 20:11 | 只看该作者
本帖最后由 arm_fan168 于 2009-11-30 22:01 编辑

“ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。
”。以前没有接触过这个理论,而且这句话也分怎么理解,stm32基于ARMv7架构,最严格的对齐方式是字访问的字对齐(LDM,STM多字节访问),而没有“双字对齐”一说。 结构体用于定位寄存器组,寄存器也没有双字的寄存器吧?毕竟是32位机。4字节对齐已经足够了。

使用特权

评论回复
28
arm_fan168| | 2009-11-30 20:14 | 只看该作者
本帖最后由 arm_fan168 于 2009-11-30 22:14 编辑

编译器在给变量分配内存时考虑了对齐问题,无非是想达到两个目的,一是使硬件正常工作不出错,二是提高存取效率。要达到这两个目的,在编译STM32这类芯片的程序时指定#pragma pack(4)已经足够,使用#pragma pack(8)就有点过尤不及了,会浪费内存空间。即便是为ARM920T这种v4架构的处理器编译代码,只要编译器不使用STRD/LDRD这种双字访问指令,同样不需要#pragma pack(8)。IAR就是在默认的#pragma pack(4)情况下,避免使用STRD/LDRD,可能是为了在存取效率和内存占用之间取个折中。

使用特权

评论回复
29
arm_fan168| | 2009-11-30 20:20 | 只看该作者
有影响,这个应该就是pack值选项了(不是align选项),一般的编译器不做规定的时候,默认的pack值是无限大(全部按照自然边界对齐),但C语言中不会存在超过8字节的基础类型(最长就是longlong和double了,也就8 ...
kiton_law 发表于 2009-11-30 19:55

刚才没看清,4和8那个是“stack align”选项,堆栈对齐方式,选了8同样是20。

使用特权

评论回复
30
sinadz| | 2009-11-30 21:20 | 只看该作者
笔试考这种,看难有困难了

使用特权

评论回复
31
kiton_law|  楼主 | 2009-12-1 00:46 | 只看该作者
本帖最后由 kiton_law 于 2009-12-1 00:51 编辑
“ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。
”。以前没有接触过这个理论,而且这句话也分怎么理解,stm32基于ARMv7架构,最严格的对齐方式是字访问的字对齐(LDM,S ...
arm_fan168 发表于 2009-11-30 20:11


双字对齐只是编译器处理长数据类型的一种模式,与处理器无关(比如可以1字节强制对齐),在寄存器只有4字节的情况下,编译器仍然可以以8节对齐,编译器的双字对齐在处理器级别依然是字对齐,因为双字地址必然是一个字地址。但编译器采用变量自然边界对齐后的确有助于处理器操作。

处理器层面的数据对齐和编译器层面的数据对齐不是一个概念,处理器更关注的是可否一次存取数据,或者说可否在地址总线得到正确的译码,而编译器关注的是变量的存放位置,以及汇编后的代码长度。最典型的例子就是如果编译器中不是自然边界对齐,也可以编译成汇编的,但代码长度自然会增加,而在处理器中一条语句的操作地址不是对齐的,则会发生访问错误(当然,支持非对齐访问的处理器除外)

“ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。”这句话是从ANSI标准来的,这句话是对编译器的要求,是用来规范编译器在数据对齐上的处理方法的,我前面说了这个和处理器对齐是两个概念。

我发这个帖子的目的也是让大家了解数据对齐概念在嵌入式程序设计中的重要性,至于我的观点是对是错,我觉得已经不重要了,大家可以自己试验,也会有自己的理解。

使用特权

评论回复
32
kiton_law|  楼主 | 2009-12-1 00:54 | 只看该作者
编译器在给变量分配内存时考虑了对齐问题,无非是想达到两个目的,一是使硬件正常工作不出错,二是提高存取效率。要达到这两个目的,在编译STM32这类芯片的程序时指定#pragma pack(4)已经足够,使用#pragma pack(8) ...
arm_fan168 发表于 2009-11-30 20:14


这倒是有可能,IAR编译后的代码比RVMDK小些,我刚才查了一下,gcc同样是以最大4字节对齐的,gcc把所有自然边界超过2的变量的自然边界都当作4来处理(前面是搞错了),所以gcc编译的代码比win32小些.

在这点上gcc和IAR是类似的,为了获得更高的存储器效率而牺牲双字处理的速度,毕竟双字不常用。

使用特权

评论回复
33
arm_fan168| | 2009-12-1 08:51 | 只看该作者
本帖最后由 arm_fan168 于 2009-12-1 11:37 编辑
双字对齐只是编译器处理长数据类型的一种模式,与处理器无关(比如可以1字节强制对齐),在寄存器只有4字节的情况下,编译器仍然可以以8节对齐,编译器的双字对齐在处理器级别依然是字对齐,因为双字地址必然是一 ...
kiton_law 发表于 2009-12-1 00:46

我觉得这段还是有点问题:
一,“编译器的双字对齐在处理器级别依然是字对齐,因为双字地址必然是一个字地址。”这句话是不对的,v5以及v5以前版本的ARM,在双字访问的指令(STRD/LDRD)中,地址必须是双字对齐的,否则会发生存取错误。v6及以后版本的ARM虽然在半字、字和双字访问中都不需要严格对齐,但实际上,处理器是把一个非对齐的访问分解为多个对齐的访问,只是这一过程是编程者看不到的,但确实会影响存取效率。而且v6以后的版本虽然支持非对齐的单个访问,但是STM/LDM这种多字访问仍然需要字对齐,所以目前的ARM编译器,4个字节的字对齐是必要的,否则会不支持多字访问。
二,“处理器层面的数据对齐和编译器层面的数据对齐不是一个概念”。处理器的数据对齐和相应编译器的数据对齐是有联系的。试想如果有一个处理器,既能支持所有的非对齐访问,又不会因为是非对齐访问而影响存取效率,那么编译器在给变量分配内存空间时考虑对齐问题还有什么意义呢?只会白白浪费内存而已。编译器为了能够支持双字访问指令(不出错或达到最快速),最大的对齐选项是8。但在应用中需要使用双字指令的时候较少,所以一个折中的办法就是4字节对齐。
    讨论楼主的这个问题还是有点意义的,能够弄清一些平时不太注意的底层的东西,但答案却不是固定的。

使用特权

评论回复
34
lxyppc| | 2009-12-1 09:06 | 只看该作者
个人觉得如果不去做编译器没有必要把这些弄这么清楚
只要知道有这么回事就行了
需要的时候再看看所用编译器的帮助文档就行了

P.S. 貌似没多少人看(查阅)过编译器的帮助文档

使用特权

评论回复
35
HWM| | 2009-12-1 09:09 | 只看该作者
关于变量人为布局的一点建议:

首先给变量定长,这就是自己定一系列定长类型——如U8,U16,U32等。

如果将这些定长的变量包装成一个结构(类也相仿),若是长度不敏感的话,随便咋弄。但如果是长度敏感的话,建议严格按类型长度的自然分界来布局变量,这不仅是可移植性问题,也是效率问题。

使用特权

评论回复
36
pkat| | 2009-12-1 09:48 | 只看该作者
说的有点道理,做技术的就是要把问题弄清楚

使用特权

评论回复
37
kiton_law|  楼主 | 2009-12-1 18:10 | 只看该作者
我觉得这段还是有点问题:
一,“编译器的双字对齐在处理器级别依然是字对齐,因为双字地址必然是一个字地址。”这句话是不对的,v5以及v5以前版本的ARM,在双字访问的指令(STRD/LDRD)中,地址必须是双字对齐的,否 ...
arm_fan168 发表于 2009-12-1 08:51



被你越搞越乱,我只能说个人理解角度不同

使用特权

评论回复
38
kiton_law|  楼主 | 2009-12-1 18:16 | 只看该作者
关于变量人为布局的一点建议:

首先给变量定长,这就是自己定一系列定长类型——如U8,U16,U32等。

如果将这些定长的变量包装成一个结构(类也相仿),若是长度不敏感的话,随便咋弄。但如果是长度敏感的话,建议 ...
HWM 发表于 2009-12-1 09:09


你说的这个做法其实大多数人都是这么做的,结构体内的填充大部分人也是手动填充的。

但不能说我们全部都使用了手动填充就不需要去了解编译器是如何处理对齐的,这是一个知其然和知其所以然的问题。

我在这里讨论的是为什么,而你总是跳出来说怎么做。但是怎么做大部分人都知道,不用谁来建议,我想大家更想知道的是为什么,麻烦您就不要在来捣乱了。

使用特权

评论回复
39
chuaji| | 2009-12-1 19:08 | 只看该作者
IAR编译器做过处理的,标准的LONG 是32位的,LONG LONG是64位的,IAR把LONG LONG定义成和LONG一样32位的,所以你的结果是20

使用特权

评论回复
40
arm_fan168| | 2009-12-1 19:15 | 只看该作者
IAR编译器做过处理的,标准的LONG 是32位的,LONG LONG是64位的,IAR把LONG LONG定义成和LONG一样32位的,所以你的结果是20
chuaji 发表于 2009-12-1 19:08

IAR的long long类型是多少位不是说说就行了,你自己试试就知道是多少位了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则