段大小端转换故事集在哪看

二次元同好交流新大陆
扫码下载App
汇聚2000万达人的兴趣社区下载即送20张免费照片冲印
扫码下载App
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
亦学习,亦工作,亦生活……
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
而在大端模式CPU内存中的存放方式则为:内存地址0x40000x4001存放内容0x120x3432位宽的数0x在小端模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:内存地址0x40000x40010x40020x4003存放内容0x780x560x340x12而在大端模式CPU内存中的存放方式则为:内存地址0x40000x40010x40020x4003存放内容0x120x340x560x78&&&&&& 我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。Note:采用大端方式进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。(我的理解:小端模式在低字节就放一个低位)下面这段代码可以用来测试一下你的编译器是大端模式还是小端模式:int main(){char x0,x1;x=0x1122;x0=*((char*)&x); //低地址单元 ,或者((char*)&x)[0];x1=*((char*)&x + 1); //高地址单元,或者((char*)&x)[1];printf("x0=%x\nx1=%x\n",x0,x1);}若x0=0x11,则是大端; 若x0=0x22,则是小端......本文引用通告地址:下面很深刻,拜读一下:大端(Big Endian)与小端(Little Endian)详解【大端(Big Endian)与小端(Little Endian)简介】Byte Endian是指字节在内存中的组织,所以也称它为Byte Ordering,或Byte Order。&&&& 对于数据中跨越多个字节的对象, 我们必须为它建立这样的约定:(1) 它的地址是多少?(2) 它的字节在内存中是如何组织的?&&& 针对第一个问题,有这样的解释:&&& 对于跨越多个字节的对象,一般它所占的字节都是连续的,它的地址等于它所占字节最低地址。(链表可能是个例外, 但链表的地址可看作链表头的地址)。&&& 比如: int x, 它的地址为0×100。 那么它占据了内存中的Ox100, 0×101, 0×102, 0×103这四个字节(32位系统,所以int占用4个字节)。&&& 上面只是内存字节组织的一种情况: 多字节对象在内存中的组织有一般有两种约定。 考虑一个W位的整数。&&& 它的各位表达如下:[Xw-1, Xw-2, ... , X1, X0],它的&&& MSB (Most Significant Byte, 最高有效字节)为 [Xw-1, Xw-2, ... Xw-8];&&& LSB (Least Significant Byte, 最低有效字节)为 [X7,X6,..., X0]。&&& 其余的字节位于MSB, LSB之间。LSB和MSB谁位于内存的最低地址, 即谁代表该对象的地址?这就引出了大端(Big Endian)与小端(Little Endian)的问题。如果LSB在MSB前面, 既LSB是低地址, 则该机器是小端; 反之则是大端。DEC (Digital Equipment Corporation,现在是Compaq公司的一部分)和Intel的机器(X86平台)一般采用小端。IBM, Motorola(Power PC), Sun的机器一般采用大端。当然,这不代表所有情况。有的CPU即能工作于小端, 又能工作于大端, 比如ARM, Alpha,摩托罗拉的PowerPC。 具体情形参考处理器手册。具体这类CPU是大端还是小端,应该和具体设置有关。(如,Power PC支持little-endian字节序,但在默认配置时是big-endian字节序)一般来说,大部分用户的操作系统(如windows, FreeBsd,Linux)是Little Endian的。少部分,如MAC OS ,是Big Endian 的。所以说,Little Endian还是Big Endian与操作系统和芯片类型都有关系。Linux系统中,你可以在/usr/include/中(包括子目录)查找字符串BYTE_ORDER(或_BYTE_ORDER, __BYTE_ORDER),确定其值。BYTE_ORDER中文称为字节序。这个值一般在endian.h或machine/endian.h文件中可以找到,有时在feature.h中,不同的操作系统可能有所不同。对于一个数0×1122使用Little Endian方式时,低字节存储0×22,高字节存储0×11而使用Big Endian方式时, 低字节存储0×11, 高字节存储0×22经一网友指正,才知道,上面的描述,是不准确的.想了下,觉得如下描述可能更合适:使用Little Endian方式存储数据时,数据的LSB相对最没意义的数据位,存放在低地址位置,这里的LSB也就是22了.也即,低地址存储0×22, 高地址存储0×11而使用Big Endian方式存储数据时,数据的MSB最有意义的数据位,存放在低地址位置,这里的MSB也就是11了.也即低地址存储0×11, 高地址存储0×22助记:1)所谓MSB (Most Significant Byte),名字很复杂,不知是否有人没搞懂,反正我开始看到这个词时候,就很糊涂,有点不完全理解.其实简单说MSB就是,一个数字中,最重要的那位,举例来说,12004,中文读作,一万两千零四,那最高位的1,就表示了一万,此处就称作MSB,最有意义的位.2)一般常见的数据存储,用文字写出来的时候,其内容书写格式,多数是从低地址到高地址.(更符合人类思维的原因)举例,一个16进制数是 0×11 22 33, 而存放的位置是地址0×3000 中存放11地址0×3001 中存放22地址0×3002 中存放33连起来就写成地址0×02中存放了数据0×112233.而这种存放和表示方式,正好符合大端.解释的有点乱,希望有人能看懂.如果还有哪里有误,还请各位继续指正.谢谢.【用函数判断系统是Big Endian还是Little Endian】bool IsBig_Endian()//如果字节序为big-endian,返回//反之为&& little-endian,返回false{&&& unsigned short test = 0×1122;&&& if(*( (unsigned char*) &test ) == 0×11)&&&&&& return TRUE;else&&& return FALSE;}//IsBig_Endian()转自========================================================================================================================================http://wxxweb./blog/static//大端模式与小端模式一、概念及详解  在各种体系的计算机中通常采用的字节存储机制主要有两种: big-endian和little-endian,即大端模式和小端模式。  先回顾两个关键词,MSB和LSB:  MSB:Most Significant Bit ——- 最高有效位LSB:Least Significant Bit ——- 最低有效位  大端模式(big-edian)  big-endian:MSB存放在最低端的地址上。  举例,双字节数0×1234以big-endian的方式存在起始地址0×中:| data |&– address| 0×12 |&– 0×| 0×34 |&– 0×   在Big-Endian中,对于bit序列中的序号编排方式如下(以双字节数0×8B8A为例):bit | 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15——MSB———————————-LSBval | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |+——————————————–+= 0×8 B 8 A   小端模式(little-endian)   little-endian:LSB存放在最低端的地址上。  举例,双字节数0×1234以little-endian的方式存在起始地址0×中:| data |&– address| 0×34 |&– 0×| 0×12 |&– 0×   在Little-Endian中,对于bit序列中的序号编排和Big-Endian刚好相反,其方式如下(以双字节数0×8B8A为例):bit | 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0——MSB———————————–LSBval | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |+———————————————+= 0×8 B 8 A二、数组在大端小端情况下的存储:  以unsigned int value = 0×为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:  Big-Endian: 低地址存放高位,如下:高地址—————buf[3] (0×78) — 低位buf[2] (0×56)buf[1] (0×34)buf[0] (0×12) — 高位—————低地址Little-Endian: 低地址存放低位,如下:高地址—————buf[3] (0×12) — 高位buf[2] (0×34)buf[1] (0×56)buf[0] (0×78) — 低位————–低地址  
阅读(15093)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_084068',
blogTitle:'大端模式&小端模式',
blogAbstract:'& 在C语言中除了8位的char型之外,还有16位',
blogTag:'大端模式,小端模式,msb,lsb',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:3,
publishTime:9,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:4,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'亦学习,亦工作,亦生活……',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}看到有留言說喜歡「段小端故事集」的,鼻子都酸了,謝謝你們看,我一定會找時間繼續往下畫的>_<。另外新的作品正在用心籌備中,到時候會在「快看漫畫」APP上更新,想為你們帶來更加好看的作品(≧?≦)!
同時轉發到微博请写段程序,能够判断当前CPU的大端小端模式。 - 嵌入式资料区 -
嵌入式开发联盟
请写段程序,能够判断当前CPU的大端小端模式。
UID1&帖子4185&积分20609&联盟金币:7627 元&在线时间4567 小时&注册时间&
请写段程序,能够判断当前CPU的大端小端模式。
悬赏金额: 10
请写段程序,能够判断当前CPU的大端小端模式。
大家多想想啦,我看看有多少种答案!呵呵!
在ARM体系中,每个字单元包含4个字节单元或者两个半字单元。在字单元中,4个字节哪一个是高位字节,哪一个是低位字节则有两种不同的格式:big-endian和little-endian格式。在小端模式中,低位字节放在低地址,高位字节放在高地址;在大端模式中,低位字节放在高地址,高位字节放在低地址。
在C语言中,不同于结构体,共用体(联合体)中的几种不同类型的变量存放在同一段内存单元中。利用这一特点,可以用联合体变量判断ARM或x86环境下,存储系统是是大端还是小端模式。
#include &stdio.h&
int main()
&&&&//4 bytes
&& //1 byte
&&if (c.b==1)
&&printf(&It is Little_endian!\n&);
&&printf(&It is Big_endian!\n&);
&&return 1;
1&&在c中,联合体(共用体)的数据成员都是从低地址开始存放。
2&&若是小端模式,由低地址到高地址c.a存放为0x01 00 00 00,c.b被赋值为0x01;
&&————————————————————————————
& &地址 0xxxx
& &c.a&&01& && && &00& && && &00& && && &00
& &c.b&&01& && && &00& && &&&
&&————————————————————————————&&
3&&若是大端模式,由低地址到高地址c.a存放为0x00 00 00 01,c.b被赋值为0x0;
&&————————————————————————————
& &地址 0xxxx
& &c.a&&00& && && &00& && && &00& && && &01
& &c.b&&00& && && &00& && && && && &&&
&&————————————————————————————&&
4&&根据c.b的值的情况就可以判断cpu的模式了,现在XP环境下的intel CPU是小端模式,不信你可测试下!
UID14745&帖子77&积分178&联盟金币:168 元&在线时间38 小时&注册时间&
在ARM体系中,每个字单元包含4个字节单元或者两个半字单元。在字单元中,4个字节哪一个是高位字节,哪一个是低位字节则有两种不同的格式:big-endian和little-endian格式。在小端模式中,低位字节放在低地址,高位字节放在高地址;在大端模式中,低位字节放在高地址,高位字节放在低地址。
在C语言中,不同于结构体,共用体(联合体)中的几种不同类型的变量存放在同一段内存单元中。利用这一特点,可以用联合体变量判断ARM或x86环境下,存储系统是是大端还是小端模式。
#include &stdio.h&
int main()
&&&&//4 bytes
&& //1 byte
&&if (c.b==1)
&&printf(&It is Little_endian!\n&);
&&printf(&It is Big_endian!\n&);
&&return 1;
1&&在c中,联合体(共用体)的数据成员都是从低地址开始存放。
2&&若是小端模式,由低地址到高地址c.a存放为0x01 00 00 00,c.b被赋值为0x01;
&&————————————————————————————
& &地址 0xxxx
& &c.a&&01& && && &00& && && &00& && && &00
& &c.b&&01& && && &00& && &&&
&&————————————————————————————&&
3&&若是大端模式,由低地址到高地址c.a存放为0x00 00 00 01,c.b被赋值为0x0;
&&————————————————————————————
& &地址 0xxxx
& &c.a&&00& && && &00& && && &00& && && &01
& &c.b&&00& && && &00& && && && && &&&
&&————————————————————————————&&
4&&根据c.b的值的情况就可以判断cpu的模式了,现在XP环境下的intel CPU是小端模式,不信你可测试下!
[通过 QQ、MSN 分享给朋友]
网页右侧QQ悬浮滚动在线客服
站长的QQ:
邀请码QQ:
网页右侧QQ悬浮滚动在线客服
站长的QQ:
邀请码QQ:1011人阅读
计算机体系结构
1, 内存对齐
为什么要讨论内存对齐问题呢?因为最近在写BMP头文件的时候出现了些问题,后来发现是结构体中内存对齐所致的。
当时情况如下:
16 typedef struct
18&&&& uint16_t& &
19&&&& uint32_t& file_&&
20&&&& uint16_t& reserved1;&&
21&&&& uint16_t& reserved2;&
22&&&& uint32_t& bmp_&
23 }BITMAPFILEHEADER;
// 对这个结构体赋值
277&&&& BITMAPFILEHEADER&&
278&&&& memset(&bfh, 0, sizeof(BITMAPFILEHEADER));
279&&&& bfh.identifier = 0x4d42; // 'B','M'
280&&&& bfh.file_size& = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + rgb24_
281&&&& bfh.reserved1& = 0;
282&&& &bfh.reserved2& = 0;
283&&&& bfh.bmp_offset = bfh.file_size - rgb24_
// 把这个结构体写入到头文件中
304&&&& if (fwrite(&bfh, 14, 1, fp) & 0){
305&&&&&&&& perror("write_rgb24_bmp:fwrite BITMAPFILEHEADER:");
306&&&&&&&& return -1;
运行程序后,用十六进制格式查看文件,竟然多了个0000,一时十分不解。
d42 <span style="color: # 00 00
将内存中的数据写入文件时,文件中的数据排列与内存中的是一样的,所以肯定0000是分配结构体变量空间时所分配的一个字节。故对结构体变量成员内存空间的分配做了如下探讨。
先看一下程序:
1 #include
& 2 struct t1_stru
& 5&&&& int&
&10 int main()
&12&&&& t1.ch = 0x12;&& // 这三句赋值语句可以不用理会
&13&&&& t1.in = 0x3456789A;
&14&&& &t1.sh = 0xBCDE;
&15&&&& printf("sizeof(t1.ch)=%d/n", sizeof(t1.ch));
&16&&&& printf("sizeof(t1.in)=%d/n", sizeof(t1.in));
&17&&&& printf("sizeof(t1.sh)=%d/n", sizeof(t1.sh));
&18&&&& printf("sizeof(t1)=%d/n", sizeof(t1));
&19&&&& return 0;
输出结果:
sizeof(t1.ch)=1
sizeof(t1.in)=4
sizeof(t1.sh)=2
sizeof(t1)=12
为什么会出现这样的结果呢?其实是编译器其对结构体成员进行内存对齐的所导致的。
在默认情况下,C/C++的编译器会将结构体,栈中的成员进行内存对齐。
什么是内存对齐呢?即把成员安排在某一些符合某种规律的内存空间地址上,从而加快CPU对数据的读写速度。
如果想深入了解内存对齐如何加快CPU对数据的读写速度请参考:
Data alignment:Straighten up and fly right
在讲述内存对齐规则之前,首先说明一下#pragma pack (n)语句
#pragma pack (n) 这个语句用于设置结构体的内存对齐方式,具体作用下面再说。在linux gcc 下n可取的值为:1,2,4,当n大于4时按4处理。如果程序中没用显试写出这个语句,那么在linux gcc下,它会对所有结构体都采用#pragma pack (4)的内存对齐方式。需要注意的是,在不同的编译平台上默认的内存对齐方式是不同的。如在VC中,默认是以#pragma pack (8) 的方式进行对齐。
#pragama pack (n)使用方法
#pragama pack (2)
struct structname
#pragama pack ()
上面表示在#pragama pack (2) 到 #pragama pack()之间采用n为2的内存对齐方式。#pragma pack () 表示取消自定义字节对齐方式,则在#pragama pack ()以下的程序不在使用#pragma pack (2) 的对齐方式,恢复#pragma pack (4) 这种编译器默认的对齐方式。当然没有#pragma pack ()也可以,那么则表示#pragma pack (2)以下到程序尾都采用此对齐方式。
内存对齐总规则:
结构体成员的地址必须安排在成员大小的整数倍上或者是#pragma pack(n) 所指定的n的倍数上;取两者的最小值,即MIN(sizeof(mem), n),称MIN(sizeof(mem), n)为该结构体的成员对齐模数。同时该结构体的总大小必须为MIN(n, MAX(sizeof(mem1), siezof(mem2)&))的整数倍;而称MIN(n, MAX(sizeof(mem1), siezof(mem2)&))为该结构体的对齐模数。
内存对齐细则:
下面的3条细则符合上面所说的总规则;这里的偏移指某一个数据成员的首地址到该结构体头的地址偏移。
(1) 对结构体的成员,第一个数据位于偏移为0的位置,以后每个数据成员的偏移量必须是成员对齐模数的倍数。
(2) 为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的偏移是否为成员对齐模数的整数倍,若是,则存放本成员,反之,则在本成员与上一成员之前填充一定的字节,从而达到整数倍的要求。
(3) 在数据成员完成自身的对齐后,结构体本身也要进行对齐。意思是该结构体的大小必须是结构体的对齐模数的整数倍。如果其大小不是,那么则在最后一个成员的后面填充字节。
首先计算出成员对齐模数与结构体的对齐模数:
ch_mod = MIN(4, sizeof(t1.ch)) = 1;
in_mod = MIN(4, sizeof(t1.in)) = 4;
sh_mod = MIN(4, sizeof(t1.sh)) = 2;
t1_mod = MIN(4, MAX(ch_mod, in_mod, sh_mod)) = 4;
然后用gdb调试上面的程序分析内存对齐的规则:
(gdb) p &t1
$1 = (struct t1_stru *) 0x80496d8& // t1结构体的首地址
(gdb) p &t1.ch&&&&&&&&&&&&&&&& &&&&&&&
$2 = 0x80496d8 ""&&&& &&&&&&&&&&&&&&&& // ch的首地址
(gdb) p &t1.in
$3 = (int *) 0x80496dc&&&&&&&&&& &&& // in的首地址
(gdb) p &t1.sh
$4 = (short int *) 0x80496e0&&&&& & // sh的首地址
根据细则1:
可以知道t1的结构体的首地址就是t1结构体第一个成员ch的首地址。
根据细则2:
当为in开辟空间时,编译器检查预开辟空间的偏移,即ch后面一个一节的地址空间d9偏移,为1,不是in_mod的整数倍,所以向后找,一直到地址dc,偏移为4,刚好为in对齐模数的1倍,故在此开辟向后开辟4字节的地址空间。在d8与dc之间的地址中填充数据。
根据细则3:
当为sh分配空间后,此时结构体的大小为4+4+2 = 10, 10并不是t1_mod的整数倍,故在sh后填充两字节的数据,从而达到结构体自身的对齐。所以结构体总大小为12。
从下图可以看出该结构体成员赋值的过程:
再看一下程序:
1 #include
& 2 #include
& 4 struct t1_stru
& 6&&&& uint8_t&
& 7&&&& uint32_
& 8&&&& uint16_
&11 struct t2_stru
&13&&&& uint8_
&14&&&& struct t1_stru t1;
&15&&&& uint16_
&18 int main()
&20&&&& t2.ch&&& = 0x12;
&21&&&& t2.t1.ch = 0x23;
&22&&&& t2.t1.in = 0x3456789A;
&23&&&& t2.t1.sh = 0xABCD;
&24&&&& t2.sh&&& = 0xACEF;
&25&&&& printf("sizeof(t2) = %d/n", sizeof(t2));
&26&&&& return 0;
输出结果为:
sizeof(t2) = 20
分析的方法跟上面的例子一样,当结构体中含有结构体时,计算其大小时,其实就是根据细则不断的嵌套的计算。
首先计算出t2成员对齐模数与t2结构体的对齐模数:
t2.ch_mod = MIN(4, sizeof(t2.ch) = 1;
t2.t1_mod = MIN(4, sizeof(t2.t1)) = MIN(4, 12) = 4;
(计算siezeof(t2.t1)则是按照上面的例子那样计算,得12)
t2.sh_mod = MIN(4, sizeof(t2.sh)) = 2;
t2_mod = MIN(4, MAX(t2.ch_mod, t2.t1_mod, t2.sh_mod)) = 4;
故sizeof(t2) = 20;
下图为t2的内存示意图:
位段是以位为单位定义结构体(或共用体)中成员所占存储空间的长度。含有位段的结构体类型成为位段结构。对于定义位段的变量类型只能为:字符型与整形。在Linux gcc 下,对不同类型的位段结构采用压缩存放的方法(下面规则的3,4点体现了不同类型的压缩存放的方法)。
位段结构的定义格式:类型 &成员名&:&占用位数&
在说明位段的一些规则前先解释一些名词的含义:
类型的存储单元:sizeof(类型)字节长度的内存空间
成员类型的位宽:sizeof(类型) * 8 bit
类型的存储单元位宽=成员类型的位宽
以下是位段的一些规则:
(1)第一个位段成员的偏移满足内存对齐的原则。 &(2) 相邻的位段结构成员的类型相同。若它们的位宽之和不大于该类型的存储单元的位宽,则后面的成员紧接在前一个成员后;若否,则为后面的成员开辟一个新的存储单元。新的存储单元的偏移满足内存对齐的原则。
(3)相邻的位段结构成员的类型不同,前一成员类型的位宽大于后一成员类型的位宽。若前一成员所在的存储单元还有空间容纳后一成员,则把后一成员紧接在前一成员后;若否,则为后一成员开辟新的存储单元。新的存储单元的偏移满足内存对齐的原则。
(4)相邻的位段结构成员的类型不同,前一成员类型的位宽小于后一成员类型的位宽。若把前一成员所在的存储单元的位宽扩展为后一成员类型的位宽的大小后,能把后一成员容纳下的,则把对前一成员的存储单元进行位宽扩展,并把后一成员紧接在前一成员后;若否,则为后一成员开辟新的存储空间,其存储空间的偏移满足内存对齐的原则。(存储单元的位宽扩展的原则:若前一成员所在的字节单元的偏移不为后一成员大小的整数倍,则先向前兼并字节单元扩展,直到向前找到偏移为后一成员大小的整数倍的字节单元,此时判断扩展的位数是否足够,如果不够则从后开辟新字节单元进行扩展)
(5)可以通过定义长度为0的位段的方式使下一位段从下一存储单元开始。 (6)可以定义无名位段。 (7)定义位段时其长度不能大于存储单元的长度。 (5)位段无地址,不能对位段进行取地址运算。 (6)位段可以以%d,%o,%x格式输出。 (7)位段若出现在表达式中,将被系统自动转换成整数。
(8)位段的最大取值范围不要超出二进制位数定的范围,否则超出部分会丢弃。
下图是编译器分配位段空间时的算法流程:
&看下面的列子:
& 1 #include
& 2 #include
& 4 struct s1_stru
& 6&&&& uint8_t& ch1:6;
& 7&&&& uint8_t& ch2:1;
& 8&&&& uint16_t sh:2;
&11 struct s2_stru
&13&&&& uint8_t& ch:7;
&14&&&& uint16_t sh:9;
&17 struct s3_stru
&19&&&& uint8_t ch:7;
&20&&&& uint16_t sh:10;
&23 struct s4_str
&25&&&& uint8_t& ch:7;
&26&&&& uint16_t sh:10;
&27&&&& uint32_t in:1;
&28&&&& uint16_t sh2:2;
&31 struct s5_stru
&33&&&& uint8_t& ch1:4;
&34&&&& uint16_t sh1:10;
&35&&&& uint32_t in1;
&36&&&& uint8_t& ch2:4;
&37&&&& uint16_t sh2:4;
&41 int main(void)
&43&&&& t1.ch1 = 0b111111;
&44&&&& t1.ch2 = 0b1;
&45&&&& t1.sh = 0b11;
&46&&&& t2.ch = 0b1111111;
&47&&&& t2.sh = 0b;
&48&&&& t3.ch = 0b1111111;
&49&&&& t3.sh = 0b;
&50&&&& t4.ch = 0b1111111;
&51&&&& t4.sh = 0b;
52&&&& t4.in = 0b1;
&53&&&& t4.sh2 = 0b11;
&54&&&& t5.ch1 = 0b1111;
&55&&&& t5.sh1 = 0b;
&56&&&& t5.in1 = 0xFFFFFFFF;
&57&&&& t5.ch2 = 0b1111;
&58&&&& t5.sh2 = 0b1111;
&59&&&& printf("sizeof(t1)=%d/n", sizeof(t1));
&60&&&& printf("sizeof(t2)=%d/n", sizeof(t2));
&61&&&& printf("sizeof(t3)=%d/n", sizeof(t3));
&62&&&& printf("sizeof(t4)=%d/n", sizeof(t4));
&63&&&& printf("sizeof(t5)=%d/n", sizeof(t5));
&64&&&& return 0;
输出结果:
sizeof(t1)=2
sizeof(t2)=2
sizeof(t3)=4
sizeof(t4)=4
sizeof(t5)=12
结构体变量t1的内存示意图:这幅图说明了两种情况,一是同位段类型的且能够存放在同一存储单元;二是不同位段类型的,但是经过位宽扩展后能够存放在同一单元的。
结构体变量t3的内存示意图:这幅图说明了不同类型的位段成员间需要开辟新空间的情况。
结构体变量t4的内存示意图:这幅图是上面两幅图情况的综合。
下面用图说明一下存储单元位宽扩展的原则
struct examp1
&&& uint8_t ch1:7;
&&& uint32_t in1:3;
ch1所在偏移为0,是in1类型的大小的整数倍,故从ch1向后开辟新空间扩展。一共开辟3个字节。
struct exampl2
&&& uint8_t ch1:7;
&&& uint8_t ch2:7;
&&& uint32_t in1:3;
ch2所在的偏移为1,不是in1类型的大小的整数倍,故先从ch2向前兼并字节单元,直到找到偏移为in1类型的大小的整数倍的地址。可知,ch1所在的空间为in1类型的大小的整数倍。故一共向前兼并了1个字节,但是还差两个字节,所以要从ch2向后再开辟两个字节的空间。
类似的有以下两幅图:
struct exampl3
&&& uint8_t ch1:7;
&&& uint8_t ch2:7;
&&& uint8_t ch3:7;
&&& uint32_t in1:3;
struct exampl4
&&& uint8_t ch1:7;
&&& uint8_t ch2:7;
&&& uint8_t ch3:7;
&&& uint8_t ch4:7;
&&& uint32_t in1:3;
3,结合位段结构再谈大小端
& 1 #include
& 2 #include
& 3 struct s1_srtuct
& 5&&&& uint8_t b1:1;
& 6&&&& uint8_t b2:1;
& 7&&&& uint8_t b3:1;
& 8&&&& uint8_t b4:1;
& 9&&&& uint8_t b5:1;
&10&&&& uint8_t b6:1;
&11&&&& uint8_t b7:1;
&12&&&& uint8_t b8:1;
&16 int main(void)
&18&&&& uint8_t *p = (uint8_t *)&t1;
&19&&&& t1.b1 = 1;
&20&&&& printf("t1=%d/n", *p);
&21&&&& return 0;
在linux gcc下,结构体成员空间而是按照由地址低到地址高的顺序分配的。又因为linux是为小端模式的系统,故输出为t1=1;若程序运行在大端模式的系统上,输出则为t1=128。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:54543次
排名:千里之外
原创:26篇
转载:25篇
评论:11条
(14)(4)(5)(2)(2)(2)(2)(7)(7)(2)(4)

我要回帖

更多关于 大小端判断 的文章

 

随机推荐