关于内存分配,字节对齐??

mohuifu_2000 2004-03-01 05:49:36


typedef enum
{
UNKOWEN,
LOCK_ONCE,
MULTI_LOCK
}LOCK_MODE; //锁卡模式
typedef struct
{
LOCK_MODE lock_mode;
BOOL lock_flag;
UINT8 DEF_PIN1[9];
UINT8 CODED_PIN1[9];
UINT8 IMSI[11];

}LOCK_PARAM; //锁卡参数

为什么给LOCK_PARAM 分配的内存空间是40个字节?

兄弟们帮我分析分析?
...全文
454 10 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
joachern 2004-08-18
  • 打赏
  • 举报
回复
gz
MikeChen2003 2004-08-18
  • 打赏
  • 举报
回复
mark
louifox 2004-08-18
  • 打赏
  • 举报
回复
mark
onedayonehole 2004-08-18
  • 打赏
  • 举报
回复
非常感谢,看样子不懂的东西实在太多了
itmaster 2004-04-17
  • 打赏
  • 举报
回复
内存分配策略
关于内存分配,通常有2种比较常用的分配策略:可变大小分配策略、固定尺寸分配策略。

可变大小分配策略,关键就在用他们的通用性上,通过他们,用户可以向系统申请任意大小的内存空间,显然,这样的分配方式很灵活,应用也很广泛,但是他们也有自己致命的缺陷,不过,对于我们来说,影响最大的大约在2个方面:
1、为了满足用户要求和系统的要求,不得不做一些额外的工作,效率自然就会有所下降;
2、在程序运行期间,可能会有频繁的内存分配和释放动作,利用我们已有的数据结构和操作系统的知识,这样就会在内存中形成大量的、不连续的、不能够直接使用的内存碎片,在很多情况下,这对于我们的程序都是致命的。如果我们能够每隔一段时间就重新启动系统自然就没有问题,但是有的程序不能够中断,就算是能够中断,让用户每隔一段时间去重起系统也是不现实的(谁还敢用你做的东东?)

固定尺寸分配策略,这个策略的关键就在于固定,也就是说,当我们申请内存时,系统总是为我们返回一个固定大小(通常是2的指数倍)的内存空间,而不管我们实际需要内存的大小。和上面我们所说的通用分配策略相比,显得比较呆板,但是速度更快,不会产生太多的细小碎片。

一般情况下,可变大小分配策略和固定尺寸分配策略经常共同合作,例如,分配器会有一个分界线M,当申请的内存大小小于M的时候,就采用固定尺寸分配,当申请的大小大于M的时候就采用可变大小的分配。其实,在SGI STL里面就是采用的这种混合策略,它采用的分界线是128B,如果申请的内存大小超过了128B,就移交第一级配置器处理,如果小于128B,则采用内存池策略,维护一个16个free-lists的小额区块。
itmaster 2004-04-17
  • 打赏
  • 举报
回复
自上篇“程序中的内存分配浅谈”贴出后,很多网友都跟我探讨过这方面的问题,也有不少大侠悉心指出其中谬误,在此halk表示衷心感谢!这篇文章可以说是继续上一篇的话题来的,同时也是我自己不断深入学习的心得。虽因个人水平有限错误不可避免,但还是想写出来,希望能够与大家共同探讨!

引子:勘误
上篇中存在若干错误,此处予以纠正。
1. 程序退出后,windows系统将收回所有分配的物理存储器,并释放句柄表,因而进程中new的内存空间不会永久存在,而是随进程而消亡。感谢GUOJIAN朋友指出此错误!
2. 有关字对齐的约定,在C程序编译的时候有一个选项可以控制的。对于X86来说,CPU本身会对非对齐的字进行对齐处理;而Alpha系列CPU则不支持此特性,如果需要的时候必须用软件实现,会显著影响处理速度。因而在通常情况下还是遵照默认选项,选择字对齐为好。

三.进程空间
Win32的可执行文件在加载后,系统将为它建立一个它自己的虚拟内存空间,即进程空间,其容量达4G(64位系统中将更大,但我想64位的程序应该不叫做Win32程序了吧?)。这4G的空间划分为了几个区域,对于win98和win2000是不尽相同的。
Window2000 Windows98
NULL指针分配分区 0x00000000~0x0000ffff 0x00000000~0x00000fff
MS-DOS/Win16兼容分区 无 0x00001000~0x003fffff
用户分区 0x00010000~0x7ffeffff 0x00400000~0x7fffffff
禁止访问分区(64K) 0x7fff0000~0x7fffffff 无
共享(MMF)分区 无 0x80000000~0xbfffffff
内核方式分区 0x80000000~0xffffffff 0xc0000000~0xffffffff
(上表资料来自:《Windows核心编程》)

NULL指针分区是NULL指针的地址范围。对这个区域的读写企图都将引发访问违规。
DOS/WIN16分区是98中专门用于16位的DOS和windows程序运行的空间,所有的16位程序将共享这个4M的空间。Win2000中不存在这个分区,16位程序也会拥有自己独立的虚拟地址空间。有的文章中称win2000中不能运行16位程序,是不确切的。
用户分区是进程的私有领域,Win2000中,程序的可执行代码和其它用户模块均加载在这里,内存映射文件也会加载在这里。Win98中的系统共享DLL和内存映射文件则加载在共享分区中。
禁止访问分区只有在win2000中有。这个分区是用户分区和内核分区之间的一个隔离带,目的是为了防止用户程序违规访问内核分区。
MMF分区只有win98中有,所有的内存映射文件和系统共享DLL将加载在这个地址。而2000中则将其加载到用户分区。
内核方式分区对用户的程序来说是禁止访问的,操作系统的代码与此。内核对象是否也驻留在此?我认为应该是的,因为应用程序不能直接访问内核对象。但是关于这点我没找到有关的叙述,不知哪位大侠有确切的依据?另外要说明的是,win98中对于内核分区本也应该提供保护的,但遗憾的是并没有做到,因而98中程序可以访问内核分区的地址空间。

四.进程空间中的内存分配
可能很多初学的朋友会疑惑:每个进程4G空间?那我们要多大的内存才成!
事实上,我们给予每个进程的4G空间都是虚拟的,或者说虚幻的,在没有对一段空间定义之前,它根本只有逻辑意义,实际上并不存在。
我们可以根据情况把一段虚拟空间指定为“保留”的或者“提交”的,这样这段地址空间就不能再做它用,直到被释放。二者的区别是“提交”的空间将分配给物理存储器(RAM、磁盘页文件),而“保留”的则不分配。事实上在2000中,“提交”的空间也不是马上分配给物理存储器的,而是在第一次访问的时候才分配。
进程中每个线程都有自己的堆栈,这是一段线程创建时保留下的地址区域。我们的“栈内存”即在此。至于“堆”内存,我个人认为在未用new定义时,堆应该就是未“保留”未“提交”的自由空间,new的功能是在这些自由空间中保留(并提交?)出一个地址范围。

itmaster 2004-04-17
  • 打赏
  • 举报
回复
一.存空间的对齐规则

首先看一段例子:
……
int i1;
char c1;
char c2;
int i2;

cout << "i1:" <<&i1 << "\n";
cout << "c1:" <<(void *)&c1 << "\n";
cout << "c2:" <<(void *)&c2 << "\n";
cout << "i2:" <<&i2 << "\n";
……
输出结果如下:
i1:0012FF4C
c1:0012FF48
c2:0012FF44
i2:0012FF40
是不是有些奇怪?
我们知道char类型的变量是只占用一个字节的,用sizeof(char)得到的结果也会是1。但在这里我们看到,c1和c2都被分配了4个字节的存储空间,尽管我们用c1、c2只能访问其中的一个!
Windows确实是这样做的。在为变量/对象分配内存的时候,总是以4字节对齐,无论你的变量类型是什么。也就是说,任何一个变量/对象的存储空间都是以4的整数倍的地址开始的。
为什么会这样?我不知道,大概这跟操作系统的位数有关吧!本人系统是Win2000,以上结果也是在2000中得到的。在DOS6.22/7.0下是什么情况?我想应该是两字节(16位)对齐,但是没试过。如果哪位大虾感兴趣的话可以试一试,别忘了把结果跟我说一下!^_^

下面我们稍微做一下改动,将四个变量声明到一个结构中:
struct test
{
int i1;
char c1;
char c2;
int i2;
};
然后在main()中执行以下语句:

test s;
cout << "s.i1:" <<&s.i1 << "\n";
cout << "s.c1:" <<(void *)&s.c1 << "\n";
cout << "s.c2:" <<(void *)&s.c2 << "\n";
cout << "s.i2:" <<&s.i2 << "\n";
结果如下:
s.i1:0012FF50
s.c1:0012FF54
s.c2:0012FF55
s.i2:0012FF58
晕了~~~~~~~~~~~~~~~~这里的c1怎么就给分配了一个字节的空间?上面不是说总是4字节对齐吗?怎么这里又不舍的给那三个字节了?再看c2,更晕了!@#$%^&*怎么给它老人家三个字节空间?
看来在结构中,结构成员的内存分配不总是4字节对齐的了,而是跟变量本身的大小有关了。例如sizeof(char)=1, 所以char 型变量以1字节对齐;sizeof(short)=2,所以short型变量以2字节对齐等等。经过实验证明,在类中也是这样做的。
但是要注意,对于一个结构实例/类实例而言,它们是作为一个对象而存在的,所以为他们分配的空间还是要以4字节对齐。

二.栈内存与堆内存

我们都知道,当我们想得到一块特定大小的存储区域的时候,可以用声明变量来获取,也可以用new操作符获取。例如我想获得一块存储4个整数的空间,以下方式都可以得到:
int iArray[4];
int pPointer = new int[4];
这两种方式是不相同的。声明变量/数组的方式,是在栈中分配内存。程序中的每一个函数都有自己的栈,用于为函数作用域内的变量/对象分配存储空间。当调用完此函数返回的时候,栈空间自动被收回,其中的内容也就全部无效了。而new是在堆中分配内存的,而且一经分配则永久保留,直到显式的以delete运算符来释放掉。如果不进行delete,则会造成内存泄露。
C函数库中的malloc/free也是在堆中分配内存的,与new/delete相比,二者在为对象分配空间的时候有所区别。关于这个此处不做过多解释,有兴趣的读者可以去查阅相关资料。
下面举一个例子,来演示堆内存和栈内存的使用。
char *GetMemory(void)
{
char p [] ="Hello world";
return p;
}
void Test(void)
{
char *str =NULL;
str =GetMemory();
printf(str)}
{
建立一个完整的程序,调用以上的Test函数,然后看看输出了什么?

Hello world

哈哈……成功!~~~~~~~~~~~~~~~
再运行一遍,乱码!!!
再来一遍?一半正确,一般是乱码!!!
有鬼呀~~~~~~~~~~~~~~

呵呵……是栈内存在作怪。
char p [] =="Hello world";是在栈中分配的存储区域,它在GetMemory返回时已经失效了,里面的内容可能任何值但是已经没有了意义。
将以上程序改动一下:
char *GetMemory(void)
{
char str[] = "Hello world";
int isize = strlen(str) + 1;
char *p = new char[isize];
memcpy(p, str, isize);
return p;
}
编译,运行……
Hello world
再来一遍?来十遍都不会错了!这里返回的是指向堆内存的指针,即使函数结束了也不会释放。当然,如果你在程序结束前不用delete释放掉它的话,它就永远在哪儿了 。这也应该叫做“常驻内存”吧?哈哈……

newbibe 2004-04-17
  • 打赏
  • 举报
回复
//win32

LOCK_MODE lock_mode; 4 //一个int
BOOL lock_flag; 4 //一个int

UINT8 DEF_PIN1[9]; 9
UINT8 CODED_PIN1[9]; 9
UINT8 IMSI[11]; * --这里就有个对齐的问题了

1
默认的是 8byte 对齐。
现在有 9+9+11 = 29 个byte,应该分配 8*4 个字节。

2
如果是 4byte对齐。
现在有 9+9+11 = 29 个byte,应该分配 8*4 个字节。

3
如果是 2byte对齐。
现在有 9+9+11 = 29 个byte,应该分配 15*2 个字节。(比29多的最少的字节数)

4
如果是 1byte对齐。
现在有 9+9+11 = 29 个byte,应该分配 29 个字节。



newbibe 2004-04-17
  • 打赏
  • 举报
回复
http://www01.softhouse.com.cn/linux/knowledge/tech/7092.html
newbibe 2004-04-17
  • 打赏
  • 举报
回复
C编译器对结构空间缺省的分配

  在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间;各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间

例如,下面的结构各成员空间分配情况:
struct test {
char x1;
short x2;
float x3;
char x4;
};
  
                 
结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然对界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大对界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。

结构中的位段
  所谓位段是以位为单位定义长度的结构体类型中的成员。编译器对结构中位段的分配遵从下面几点原则:
? 对于长度为0的位段,其下一个位段从下一个存储单元开始存放:
如:
struct T {
unsigned char a : 1;
unsigned char b : 2;
unsigned : 0;
unsigned c : 3;
};
  结构T的成员a和b在一个存储单元中,c则在另一个存储单元中。
  · 一个位段必须存储在同一存储单元中,不能跨两个单元:
如:
struct T {
unsigned char a : 4;
unsigned char b : 6;
};
  结构T的成员a在一个存储单元中,b则在另一个存储单元中。

更改C编译器的缺省分配策略
  一般地,可以通过下面的两种方法改变缺省的对界条件:
  · 使用伪指令#pragma pack ([n])
  · 在编译时使用命令行参数
#pragma pack ([n])伪指令允许你选择编译器为数据分配空间所采取的对界策略.

  在Microsfot Visual C++中,命令行参数/Zp[n]可以改变缺省对界条件;在Borland C++ Builder中,命令行参数-a[n]可以改变缺省对界条件。n的含义和#pragma pack中的n相同。
  例如,在使用了#pragma pack (1)伪指令后,test结构各成员的空间分配情况.

应用实例

  我们在日常编程工作中,特别是对一些网络事务的处理,经常会同其他人有着各种各样的协议:如我传给你20字节的头,前4个字节表示……等等。很多人都是通过指针偏移的方法来得到各种信息,这样做,不仅编程复杂,而且一旦协议有变化,程序修改起来也比较麻烦。在了解了编译器对结构空间的分配原则之后,我们完全可以利用这一特性定义自己的协议结构,通过访问结构的成员来获取各种信息。这样做,不仅简化了编程,而且即使协议发生变化,我们也只需修改协议结构的定义即可,其它程序无需修改,省时省力。下面以TCP协议首部为例,说明如何定义协议结构。

其协议结构定义如下:
struct TCPHEADER {
short SrcPort; // 16位源端口号
short DstPort; // 16位目的端口号
int SerialNo; // 32位序列号
int AckNo; // 32位确认号
unsigned char HaderLen : 4; // 4位首部长度
unsigned char Reserved1 : 4; // 保留6位中的4位
unsigned char Reserved2 : 2; // 保留6位中的2位
unsigned char URG : 1;
unsigned char ACK : 1;
unsigned char PSH : 1;
unsigned char RST : 1;
unsigned char SYN : 1;
unsigned char FIN : 1;
short WindowSize; // 16位窗口大小
short TcpChkSum; // 16位TCP检验和
short UrgentPointer; // 16位紧急指针
};
其协议结构还可以定义为如下的形式:
struct TCPHEADER {
short SrcPort; // 16位源端口号
short DstPort; // 16位目的端口号
int SerialNo; // 32位序列号
int AckNo; // 32位确认号
unsigned char HaderLen : 4; // 4位首部长度
unsigned char : 0; // 保留6位中的4位
unsigned char Reserved : 2; // 保留6位中的2位
unsigned char URG : 1;
unsigned char ACK : 1;
unsigned char PSH : 1;
unsigned char RST : 1;
unsigned char SYN : 1;
unsigned char FIN : 1;
short WindowSize; // 16位窗口大小
short TcpChkSum; // 16位TCP检验和
short UrgentPointer; // 16位紧急指针
};



转载 http://www.china-pub.com/computers/emook/1000/info.htm

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧