版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、结构体成员的内存分布与对齐 马国峻 maguojun2005 我们先看一道 IBM 和微软的笔试题:IBM 笔试题:structshorta1;shorta2;shorta3;A;structlonga1;shorta2;B;sizeof( A)=6, sizeof(B)=8,为什么? 注:sizeof(short)=2,sizeof(Iong)=4微软笔试题:struct exampIe1short a ;Iong b;struct exampIe2char c;exampIe1 struct1;short e;int main(int argc, char* argv)exampIe2 e
2、2;int d=(unsigned int)&e2.struct1-(unsigned int)&e2.c; printf("%d,%d,%dn",sizeof(exampIe1),sizeof(exampIe2),d); return 0;输出结果?要能清除的分析上面的问题就要搞清楚结构体变量的成员在内存里是如何分布的、成员先后顺序 是怎样的、成员之间是连续的还是分散的、还是其他的什么形式?其实这些问题既和软件相关又和硬 件相关。所谓软件相关主要是指和具体的编程语言的编译器的特性相关, 编译器为了优化 CPU 访问内 存的效率,在生成结构体成员的起始地址时
3、遵循着某种特定的规则, 这就是所谓的 结构体成员“对齐”; 所谓硬件相关主要是指 CPU的“字节序”问题,也就是大于一个字节类型的数据如int类型、short类型等,在内存中的存放顺序,即单个字节与高低地址的对应关系。字节序分为两类:Big-Endian 和Little-Endian,有的文章上称之为“大端”和“小端”,他们是这样定义的:LittIe-Endian 就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端; Big-Endian 就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。In tel、VAX和Un isys处理器的计算机中的数据的字节顺序是Littl
4、e-E ndian,IBM大型机和大多数Unix平台的计算机中字节顺序是 Big Endian。关与 Big-Endian 和 Little-Endian 问题本文暂不做详细讨论,本文将以小端机(此处为 intel x86 架 构的计算机)、 OS: WindowsXp 和 VC+6.0 编译器来详细讨论结构体成员的“对齐”问题。前面说了,为了优化CPU访问内存的效率,程序语言的编译器在做变量的存储分配时就进行了分配 优化处理,优化规则大致原则是这样 :对于n字节的元素(n=2,4,8,.),它的首地址能被n整除,这种原则称为“对齐”,如W0RD(2字 节)的值应该能被2整除的位置,DWORD
5、 (4字节)应该在能被4整除的位置。对于结构体来说,结构体的成员在内存中顺序存放,所占内存地址依次增高,第一个成员处于低地址处,最后一个成员处于最高地址处,但结构体成员的内存分配不一定是连续的,编译器会对其成 员变量依据前面介绍的“对齐”原则进行处理。对待每个成员类似于对待单个n字节的元素一样,依次为每个元素找一个适合的首地址,使得其符合上述的“对齐”原则。通常编译器中可以设置一个对 齐参数n,但这个n并不是结构体成员实际的对齐参数,VC+6.0中结构体的每个成员实际对齐参数N通常是这样计算得到的N= min(sizeof(该成员类型),n)( n为VC+6.0中可设置的值)。成员的内存分配规
6、律是这样的: 从结构体的首地址开始向后依次为每个成员寻找第一个满足条件 的首地址x,该条件是x % N = 0,并且整个结构的长度必须为各个成员所使用的对齐参数中最大的 那个值的最小整数倍,不够就补空字节。结构体中所有成员的对齐参数 N的最大值称为结构体的对齐参数。VC+6.0中n默认是8个字节,可以修改这个设定的对齐参数,方法为在菜单“工程”的“设置” 中的“ C/C+ ” 选项卡的“分类”中“ Code Gen eratio n ”的“ Struct member alig nment ” 中设置,Ibyte、2byte、4byte、8byte、16byte 等几种,默认为 8byte也可
7、以程序控制,采用指令:#pragmapack(xx)控制如#pragma pack(1),1 字节对齐,#pragma pack(4),4 字节对齐#pragma pack(16), 16 字节对齐接下来我们将分不同的情况来详细讨论结构体成员的分布情况,顺便提醒一下,常见类型的长度:Int4byte,Short 2byte,Char 1byte,Double 8byte,Lo ng 4byte让我们先看下例:struct Acharc;1bytedoubled;/8byteshorts;2byteinti;/4byte;int main (i nt argc, char* argv)A str
8、ua;prin tf("%le n:dn",sizeof(A);prin tf("%d,%d,%d,%d",&strua.c,&strua.d,&strua.s,&strua.i); return 0;1) n设置为8byte时结果:len:24,1245032,1245040,1245048,1245052内存中成员分布如下:Strua1245032<A4c补04byte4byteds补012450401245048strua.c分配在一个起始于8的整数倍的地址1245032(为什么是这样读者先自己思考,读完就会明
9、白), 接下来要在strua.c之后分配strua.d,由于double为8字节,取N=min(8,8), 8字节来对齐,所以从strua.c 向后找第一个能被8整除的地址,所以取1245032+8得1245040, strua.s为2byte小于参数n所以N=min(2,8),即N=2,取2字节长度对齐,所以要从strua.d后面寻找第一个能被2整除的地址来存储strua.s 由于strua.d后面的地址为1245048可以被2整除,所以strua.s紧接着分配,现在来分配strua.i,int为4byte, 小于指定对齐参数8byte,所以N=min (4,8)取N=4byte对齐,str
10、ua.s后面第一个能被4整除地址为 1245048+4,所以在1245048+4的位置分配了 strua.i,中间补空,同时由于所有成员的N值的最大值为 8,所以整个结构长度为8byte的最小整数倍,即取24byte其余均补0.于是该结构体的对齐参数就是8byte。2) 当对齐参数n设置为16byte时,结果同上,不再分析3) 当对齐参数设置为4byte时上例结果为:Le n:201245036,1245040,1245048,1245052内存中成员分布如下:4byteStruabc补01245036亠d1245040s补012450481245052.iStrua.c起始于一个4的整数倍的
11、地址,接下来要在 strua.c之后分配strua.d,由于strua.d长度为8byte, 大于对齐参数4byte,所以N=min (8, 4)取最小的4字节,所以向后找第一个能被4整除的地址来作 为strua.d首地址,故取1245036+4,接着要在strua.d后分配strua.s, strua.s长度为2byte小于4byte, 取N=min (2, 4) 2byte对齐,由于strua.d后的地址为1245048可以被2整除,所以直接在strua.d后面分配,strua.i的长度为4byte,所以取N=min (4, 4) 4byte对齐,所以 从strua.s向后找第一个能被4整
12、除的位置即1245048+4来分配和strua.i,同时N的最大值为4byte,所 以整个结构的长度为4byte的最小整数倍16byte4)当对齐参数设置为2byte时上例结果为:Le n:161245040,1245042,1245050,1245052Strua.c1245040Strua.c分配后,向后找一第一个能被2整除的位置来存放strua.d依次类推5)Ibyte对齐时:上例结果为:Le n:151245040,1245041,1245049,1245051此时,N=min (sizeof (成员),1),取N=1,由于1可以整除任何整数,所以各个成员依次分配,没 有间空,如下图所
13、示:1byte8byte2byte4bytecdsi6)当结构体成员为数组时,并不是将整个数组当成一个成员来对待,而是将数组的每个元素当一个成 员来分配,其他分配规则不变,如将上例的结构体改为:struct Acharc; 1bytedouble d; 8byteshort s; 2bytechar szBuf5;对齐参数设置为8byte,贝U,运行结果如下:Len:241245032,1245040,1245048,12450508bytec补0ds1szBuf补0Strua的s分配后,接下来分配Strua的数组szBuf5,这里要单独分配它的每个元素,由于是 char类 型,所以N=min
14、(1,8),取N=1,所以数组szBuf5的元素依次分配没有间隙。7)当结构中有成员不是一个完整的类型单元,如int或short型,而是该类型的一段时,即位段时,如struct Ainta1:5;inta2:9;charc;intb:4;short s;对于位段成员,存储是按其类型分配空间的,如int型就分配4个连续的存储单元,如果是相邻的同类型的段位成员就连续存放,共用存储单元,此处如a1,a2将公用一个4字节的存储单元,当该类型的长度不够用时,就另起一个该类型长度的存储空间。有位段时的对齐规则是这样:同类型的、 相邻的可连续在一个类型的存储空间中存放的位段成员作为一个该类型的成员变量来对待
15、,不是同类 型的、相邻的位段成员,分别当作一个单独得该类型的成员来对待,分配一个完整的类型空间,其长 度为该类型的长度,其他成员的分配规则不变,仍然按照前述的对齐规则进行。对于struct A,VC+6.0 中n设置为8时,sizeof(A)=16,内存分布:又如:struct Binta:5;intb:7;intc:6;intd:9;char e:2;intx;Vc+6.0的对齐参数设置为8、16、4字节对齐时,sizeof(A)=12内存分布为:(灰色部分未使用)4hyteabcdec空x当对齐参数设置为2字节时:(灰色部分未使用)sizeof(A)=102byteabdex又如in te
16、l的笔试题:#includestdafx.h ”#i nclude<iostream.h>struct bitinta:3;intb:2;int c:3;;intmain (i ntargc, char* argv)bit s;char *c =(char* )&s;*c =0x99;cout << s.a <<e ndl<<s.b<<e ndlvvs.cvve ndl;return 0;Output:?运行的结果是1-1-4结构bit的成员在内存中由低地址到高地址顺序存放,执行*c=0x99;后成员的内存分布情况为:a bc
17、C 10011001&s如(微8)当结构体成员是结构体类型时,那么该过程是个递归过程,且把该成员作为一个整体来对待, 软笔试题):struct example1short a ;long b;struct example2char c;example1 struct1;short e;int main (i nt argc, char* argv)example2 e2;int d=(un sig ned in t)&e2.struct1-( un sig ned int)&e2.c;prin tf("%d,%d,%dn",sizeof(exampl
18、e1),sizeof(example2),d); return 0;8byte对齐时,结果为:8, 16, 4内存分布为:4byteexample2c补0example1a补0be补0因为example1的对齐参数为4,分配完c后要接着分配struct1,这时的对齐参数为 min(struct1的对齐 参数,指定对齐参数),开始分配structl,在structl的成员分配过程中又是按照前述的规则来分配的。关于结构体内存对齐内存对齐”应该是编译器的 管辖范围”。编译器为程序中的每个 数据单元”安排在适当的位置上。但是 C语言的一个 特点就是太灵活,太强大,它允许你干预内存对齐”。如果你想了解更
19、加底层的秘密,内存对齐”对你就不应该再透明了。一、内存对齐的原因大部分的参考资料都是如是说的:1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处 取某些特定类型的数据,否则抛出硬件异常。2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需 要作两次内存访问;而对齐的内存访问仅需要一次访问。二、对齐规则每个特定平台上的编译器都有自己的默认对齐系数”也叫对齐模数)。程序员可以通过预编译命令#pragmapack(n) ,n=1,2,4,8,16来改变这一系数,其中的 n就是你要指定的 对齐系数
20、”。对齐步骤:1、 数据成员对齐规则:结构 (struct)(或联合(union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐 按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。2、 结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将 按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。3、 结合1、2颗推断:当#pragma pack 的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生 任何效果。备注:数组成员按长度按数组类型长度计
21、算,如char t9,在第1步中数据自身长度按1算,累加结构体时长度为9;第2步中,找最大数据长度时,如果结构体T有复杂类型成员 A的,该A成员的长度为该复杂类型成员A的最大成员长度。三、试验我们通过一系列例子的详细说明来证明这个规则吧!我试验用的编译器包括GCC 3.4.2 和VC6.0的C编译器,平台为 Windows XP + Sp2。我们将用典型的struct对齐来说明。首先我们定义一个struct :#pragma pack(n) /* n = 1,2, 4, 8, 16 */struct test_t int a;char b;short c;char d;#pragma pack
22、(n)首先我们首先确认在试验平台上的各个类型的 size ,经验证两个编译器的输出均为: sizeof(char) = 1sizeof(short) = 2sizeof(int) = 4的值。我们的试验过程如下:通过 #pragma pack(n) 改变 “对齐系数 ”,然后察看 sizeof(struct test_t)1 、 1 字节对齐 (#pragma pack(1)输出结果: sizeof(struct test_t) = 8 两个编译器输出一致 分析过程:1) 成员数据对齐#pragma pack(1)struct test_t int a; /*长度 4 < 1按1 对齐;
23、起始 offset=0 0%1=0;存放位置区间 0,3 */char b; /*长度 1 = 1按1 对齐;起始 offset=4 4%1=0;存放位置区间 4 */short c; /*长度 2 > 1按1 对齐;起始 offset=5 5%1=0;存放位置区间 5,6 */char d; /*长度 1 = 1按1 对齐;起始 offset=7 7%1=0;存放位置区间 7 */;#pragma pack()成员总大小 =82) 整体对齐整体对齐系数 = min(max(int,short,char), 1) = 1整体大小 (size)=$( 成员总大小 ) 按 $( 整体对齐系数
24、 ) 圆整 = 8 /* 8%1=0 */ 注 1 2 、 2 字节对齐 (#pragma pack(2)输出结果: sizeof(struct test_t) = 10 两个编译器输出一致 分析过程:1) 成员数据对齐#pragma pack(2)struct test_t int a; /*长度 4 > 2按2 对齐;起始 offset=0 0%2=0;存放位置区间 0,3 */char b; /*长度 1 <2 按1 对齐;起始 offset=4 4%1=0;存放位置区间 4 */short c; /*长度 2 =2 按2 对齐;起始 offset=6 6%2=0;存放位置区
25、间 6,7 */char d; /*长度 1 <2 按1 对齐;起始 offset=8 8%1=0;存放位置区间 8 */;#pragma pack()成员总大小 =92) 整体对齐整体对齐系数 = min(max(int,short,char), 2) = 2整体大小 (size)=$( 成员总大小 ) 按 $( 整体对齐系数 ) 圆整3 、 4 字节对齐 (#pragma pack(4)输出结果: sizeof(struct test_t) = 12 两个编译器输出一致分析过程:1) 成员数据对齐#pragma pack(4)struct test_t = 10 /* 10%2=0
26、*/int a; /*长度 4= 4按4 对齐;起始 offset=0 0%4=0char b; /*长度1 < 4按1 对齐;起始offset=4 4%1=0short c; /*长度2 < 4按2 对齐;起始offset=6 6%2=0char d; /*长度1 < 4按1 对齐;起始offset=8 8%1=0#pragma pack()成员总大小=92) 整体对齐;存放位置区间 0,3 */;存放位置区间 4 */;存放位置区间 6,7 */;存放位置区间 8 */整体对齐系数 = min(max(int,short,char), 4) = 4整体大小 (size)=
27、$( 成员总大小 ) 按 $( 整体对齐系数 ) 圆整4 、 8 字节对齐 (#pragma pack(8) 输出结果: sizeof(struct test_t) = 12 分析过程:两个编译器输出一致= 12 /* 12%4=0 */1) 成员数据对齐#pragma pack(8)struct test_t int a; /*长度 4 < 8按4 对齐;起始 offset=0 0%4=0char b; /*长度按1 对齐;起始offset=4 4%1=0short c; /*长度按2 对齐;起始offset=6 6%2=0char d; /*长度按1 对齐;起始offset=8 8%
28、1=0;存放位置区间 0,3 */;存放位置区间 4 */;存放位置区间 6,7 */;存放位置区间 8 */;#pragma pack()成员总大小 =92) 整体对齐整体对齐系数 = min(max(int,short,char), 8) = 4整体大小 (size)=$( 成员总大小 ) 按 $( 整体对齐系数 ) 圆整 = 12 /* 12%4=0 */5 、 16 字节对齐 (#pragma pack(16)两个编译器输出一致 输出结果: sizeof(struct test_t) = 12 分析过程:1) 成员数据对齐#pragma pack(16) struct test_t i
29、nt a; /*长度 4 < 16按4 对齐;起始 offset=0 0%4=0存放位置区间 0,3 */char b; /*长度1 < 16按1 对齐;起始offset=4 4%1=0存放位置区间4 */short c; /*长度2 < 16按2 对齐;起始offset=6 6%2=0存放位置区间6,7 */char d; /*长度1 < 16按1 对齐;起始offset=8 8%1=0存放位置区间8 */;#pragma pack()成员总大小 =9 2) 整体对齐整体对齐系数 = min(max(int,short,char), 16) = 4整体大小 (size
30、)=$( 成员总大小 ) 按 $( 整体对齐系数 ) 圆整 = 12 /* 12%4=0 */记录类型的内存分配!Packed Record 和 Record 的不同之处! typeMyRec=Recordvar1:integer;var2,var3,var4,var5,var6,var7,var8:shortint;var9:integer;var10:shortint;var11:integer;var12,var13:shortint;end;ShowMessage(intTostr(SizeOf(MyRec);结果显示为 18 ,而按我想象应为 16 。请高手讲解一下 Delphi5.
31、0 中变量内存空间分配机制,因为我有一个数组 MyArray:Array1.1000000 of MyRec;需要考虑节省内存问题,另外不要说我懒不爱看书,我手头所有关于Delphi 的书都没有提到这个问题。回答:显示的结果应该为 28 ,而不是 18 !按道理应该是 22 。用 Packed 的结果就是 22 。拟定义的数组比较大,应该用 packed record !原因如下:在 Windows 中内存的分配一次是 4 个字节的。而 Packed 按字节进行内存的申请和分配,这样速度要慢一些,因 为需要额外的时间来进行指针的定位。因此如果不用 Packed 的话, Delphi 将按一次
32、4 个字节的方式申请内存,因 此如果一个变量没有 4 个字节宽的话也要占 4 个字节!这样就浪费了。按上面的例子来说:var1:integer;/integer 刚好 4 个字节!var2-var5 占用 4 个字节, Var6-Var8 占用 4 个字节,浪费了一个字节。var9:integer/ 占用 4 个字节;var10: 占用 4 个字节;浪费 3 个字节var11: 占用 4 个字节;var12,var13 占用 4 个字节;浪费 2 个字节所以,如果不用 packed 的话,那么一共浪费 6 个字节!所以原来 22 个字节的记录需要 28 个字节的内存空间!*回复人: eDRIV
33、E(eDRIVE)(2001-3-2 17:45:00)得 0 分这是因为在 32 位的环境中,所有变量分配的内存都进行“边界对齐”造成的。这样做可以对速度有优化作用;但是 单个定义的变量至少会占用 32 位,即 4 个字节。所以会有长度误差,你可以用 packed 关键字取消这种优化。深入的分析,内存空间(不是内存地址)在计算机中划分为无数与总线宽度一致的单位,单位之间相接的地方称为 “边界”;总线在对内存进行访问时,每次访问周期只能读写一个单位( 32bit ),如果一个变量横跨“边界”的话, 则读或写这个变量就得用两个访问周期,而“边界对齐”时,只需一个访问周期,速度当然会有所优化。但是
34、因为格式比较整齐,所以电脑读取这个类型的数据的时候速度比较快。而 Packed Record 对数据进行了压缩,节省了内存空间,当然他的速度也变的慢了。type/ Declare anTDefaultRecordname1floater :name2intend;/ Declare aTPackedRecordname1floatername2intend;vardefaultRecpackedRecunpacked recordRecordstring4;single;char;Integer;packed record= Packed Record string4;single;char;
35、Integer;TDefaultRecord;TPackedRecord;end;ShowMessage('Defaultrecord size'+IntToStr(SizeOf(defaultRec);ShowMessage('Packedrecord size'+IntToStr(SizeOf(packedRec);beginDefaultrecordsize20Packed record size14不过,对于现在的操作系统来, packed Record 节省的那些空间已不用考虑他了。除了做 DLL (不用 packed 容 易造成内存混乱)和做硬件编程
36、时(比如串口)编程时必须用到packed Record ,其它情况都可以用 RecordC 的结构体与 Delphi 中的记录类型Object Pascal 的指针一、类型指针的定义。对于指向特定类型的指针,在 C 中是这样定义的:int *ptr;char *ptr;与之等价的 Object Pascal 是如何定义的呢?varptr: 5teger;ptr: Achar;其实也就是符号的差别而已。二、无类型指针的定义。 C 中有 void * 类型,也就是可以指向任何类型数据的指针。 Object Pasca l 为其定义了一个专门的类型: Pointer 。于是,ptr : Pointe
37、r;就与 C 中的void *ptr;等价了。三、指针的解除引用。要解除指针引用(即取出指针所指区域的值),C 的语法是 (*ptr) , ObjectPascal 则是 ptrA 。四、取地址(指针赋值)。取某对象的地址并将其赋值给指针变量,C 的语法是ptr= &Object;Object Pascal 则是ptr:= Object;也只是符号的差别而已。五、指针运算。在 C 中,可以对指针进行移动的运算,如:chara20;char*ptr=a;ptr+;ptr+=2;当执行 ptr+; 时,编译器会产生让 ptr 前进 sizeof(char) 步长的代码,之后, ptr 将指
38、向 a1 。 ptr+=2; 这句使得 ptr 前进两个 sizeof(char) 大小的步长。 同样, 我们来看一下 Object Pascal 中如何 实现:vara : array1.20of Char;ptr : PChar; /PChar 可以看作 ACharbegin/但 Pascal 不是这样的,因此 str 前要加上取地址的运算符ptr:=a;Inc(ptr);/ 这句等价于 C 的 ptr+;Inc(ptr,2); / 这句等价于 C 的 ptr+=2;end;六、动态内存分配。 C 中,使用 malloc() 库函数分配内存, free() 函数释放内存。如这样的代码:in
39、t *ptr, *ptr2;int i;ptr = (int*) malloc(sizeof(int) * 20);ptr2 = ptr;for(i=0; i<20;i+)*ptr = i;ptr+;free(ptr2);Object Pascal 中,动态分配内存的函数是 GetMem() ,与之对应的释放函数为 FreeMem() (传 统 Pascal 中获取内存的函数是 New() 和 Dispose() ,但 New() 只能获得对象的单个实体的内存大小, 无法取得连续的存放多个对象的内存块) 。因此,与上面那段 C 的代码等价的 Object Pascal 的代码为:varp
40、tr, ptr2 : 9nteger;i: integer;beginGetMem(ptr,sizeof(integer) * 20);/ 这句等价于 C 的 ptr = (int*) malloc(sizeof(int) * 20);ptr2 := ptr; / 保留原始指针位置for i := 0 to 19 dobeginptrA:= i;Inc(ptr);end;FreeMem(ptr2);end;对于以上这个例子(无论是 C 版本的,还是 Object Pascal 版本的),都要注意一个问题,就是 分配内存的单位是字节( BYTE ),因此在使用 GetMem 时,其第二个参数如果想当然的写成 20 ,那 么就会出问题了(内存访问越界)。因为 GetMem(ptr, 20); 实际只分配了 20 个字节的内存空间,而 一个整形的大小是四个字节, 那么访问第五个之后的所有元素都是非法的了 (对于 malloc
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 初三寒假总结
- 二零二五年度抵冲货款跨境电商金融结算合同3篇
- 二零二五年度房屋租赁担保合同范本(含租赁合同登记备案)3篇
- Unit 6 My clothes,my style welcome to the unit 说课稿2024-2025学年牛津译林版英语七年级上册
- 浙江省金华市婺城区2024-2025学年九年级上学期期末数学试卷(含答案)
- 二零二五年度彩钢房租赁与临时办公解决方案协议3篇
- 2024-2025学年云南省昆明市官渡区八年级(上)期末英语试卷(含答案)
- 二零二五年度企业间电子合同范本及操作手册2篇
- Unit 3 The world meets China Project 说课稿 -2023-2024学年高二英语外研版(2019)选择性必修第四册
- Unit 5读写课第一课时说课稿 - 2024-2025学年外研版(2024)七年级英语上册
- 液化气供应站安全管理制度和营业制度
- 停车场施工施工组织设计方案
- GB/T 21385-2008金属密封球阀
- GB/T 18994-2003电子工业用气体高纯氯
- 超分子化学简介课件
- 文言文阅读训练:《三国志-武帝纪》(附答案解析与译文)
- (完整版)招聘面试方案设计与研究毕业论文设计
- 调休单、加班申请单
- 肉制品生产企业名录296家
- 规划设计收费标准
- 山区道路安全驾驶教案
评论
0/150
提交评论