WINDOWS堆栈区别[转]

WINDOWS堆栈区别[转]
WINDOWS堆栈区别[转]

WINDOWS堆栈区别[转]

堆和栈的区别(转贴)

非本人作也!因非常经典,所以收归旗下,与众人阅之!原作者不祥!

堆和栈的区别

一、预备知识—程序的内存分配

一个由C/C++编译的程序占用的内存分为以下几个部分

1、栈区(STACK)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(HEAP)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(STATIC)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束后有系统释放

4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放

5、程序代码区—存放函数体的二进制代码。

二、例子程序

这是一个前辈写的,非常详细

//MAIN.CPP

INT A = 0; 全局初始化区

CHAR *P1; 全局未初始化区

MAIN()

{

INT B; 栈

CHAR S[] = "ABC"; 栈

CHAR *P2; 栈

CHAR *P3 = "123456"; 123456\0在常量区,P3在栈上。

STATIC INT C =0;全局(静态)初始化区

P1 = (CHAR *)MALLOC(10);

P2 = (CHAR *)MALLOC(20);

分配得来得10和20字节的区域就在堆区。

STRCPY(P1, "123456"); 123456\0放在常量区,编译器可能会将它与P3所指向的"123456"优化成一个地方。

}

二、堆和栈的理论知识

2.1申请方式

STACK:

由系统自动分配。例如,声明在函数中一个局部变量INT B; 系统自动在栈中为B开辟空间

HEAP:

需要程序员自己申请,并指明大小,在C中MALLOC函数

如P1 = (CHAR *)MALLOC(10);

在C++中用NEW运算符

如P2 = (CHAR *)MALLOC(10);

但是注意P1、P2本身是在栈中的。

2.2

申请后系统的响应

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,

会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的DELETE语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

2.3申请大小的限制

栈:在WINDOWS下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示OVERFLOW。因此,能从栈获得的空间较小。堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

2.4申请效率的比较:

栈由系统自动分配,速度较快。但程序员是无法控制的。

堆是由NEW分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.

另外,在WINDOWS下,最好的方式是用VIRTUALALLOC分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。

2.5堆和栈中的存储内容

栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。

当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

2.6存取效率的比较

CHAR S1[] = "AAAAAAAAAAAAAAA";

CHAR *S2 = "BBBBBBBBBBBBBBBBB";

AAAAAAAAAAA是在运行时刻赋值的;

而BBBBBBBBBBB是在编译时就确定的;

但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。

比如:

#INCLUDE

VOID MAIN()

{

CHAR A = 1;

CHAR C[] = "1234567890";

CHAR *P ="1234567890";

A = C[1];

A = P[1];

RETURN;

}

对应的汇编代码

10: A = C[1];

00401067 8A 4D F1 MOV CL,BYTE PTR [EBP-0FH]

0040106A 88 4D FC MOV BYTE PTR [EBP-4],CL

11: A = P[1];

0040106D 8B 55 EC MOV EDX,DWORD PTR [EBP-14H]

00401070 8A 42 01 MOV AL,BYTE PTR [EDX+1]

00401073 88 45 FC MOV BYTE PTR [EBP-4],AL

第一种在读取时直接就把字符串中的元素读到寄存器CL中,而第二种则要先把指针值读到EDX中,在根据EDX 读取字符,显然慢了。

2.7小结:

堆和栈的区别可以用如下的比喻来看出:

使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。

使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。WINDOWS进程中的内存结构

在阅读本文之前,如果你连堆栈是什么多不知道的话,请先阅读文章后面的基础知识。

接触过编程的人都知道,高级语言都能通过变量名来访问内存中的数据。那么这些变量在内存中是如何存放的呢?程序又是如何使用这些变量的呢?下面就会对此进行深入的讨论。下文中的C语言代码如没有特别声明,默认都使用VC编译的RELEASE版。

首先,来了解一下 C 语言的变量是如何在内存分部的。C 语言有全局变量(GLOBAL)、本地变量(LOCAL),静态变量(STATIC)、寄存器变量(REGEISTER)。每种变量都有不同的分配方式。先来看下面这段代码:

#INCLUDE

INT G1=0, G2=0, G3=0;

INT MAIN()

{

STATIC INT S1=0, S2=0, S3=0;

INT V1=0, V2=0, V3=0;

//打印出各个变量的内存地址

PRINTF("0X%08X\N",&V1); //打印各本地变量的内存地址

PRINTF("0X%08X\N",&V2);

PRINTF("0X%08X\N\N",&V3);

PRINTF("0X%08X\N",&G1); //打印各全局变量的内存地址

PRINTF("0X%08X\N",&G2);

PRINTF("0X%08X\N\N",&G3);

PRINTF("0X%08X\N",&S1); //打印各静态变量的内存地址

PRINTF("0X%08X\N",&S2);

PRINTF("0X%08X\N\N",&S3);

RETURN 0;

}

编译后的执行结果是:

0X0012FF78

0X0012FF7C

0X0012FF80

0X004068D0

0X004068D4

0X004068D8

0X004068DC

0X004068E0

0X004068E4

输出的结果就是变量的内存地址。其中V1,V2,V3是本地变量,G1,G2,G3是全局变量,S1,S2,S3是静态变量。你可以看到这些变量在内存是连续分布的,但是本地变量和全局变量分配的内存地址差了十万八千里,而全局变量和静态变量分配的内存是连续的。这是因为本地变量和全局/静态变量是分配在不同类型的内存区域中的结果。对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(STACK)”和“堆(HEAP)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。

├———————┤低端内存区域

│……│

├———————┤

│动态数据区│

├———————┤

│……│

├———————┤

│代码区│

├———————┤

│静态数据区│

├———————┤

│……│

├———————┤高端内存区域

堆栈是一个先进后出的数据结构,栈顶地址总是小于等于栈的基地址。我们可以先了解一下函数调用的过程,以便对堆栈在程序中的作用有更深入的了解。不同的语言有不同的函数调用规定,这些因素有参数的压入规则和堆栈的平衡。WINDOWS API的调用规则和ANSI C的函数调用规则是不一样的,前者由被调函数调整堆栈,后者由调用者调整堆栈。两者通过“__STDCALL”和“__CDECL”前缀区分。先看下面这段代码:

#INCLUDE

VOID __STDCALL FUNC(INT PARAM1,INT PARAM2,INT PARAM3)

{

INT VAR1=PARAM1;

INT VAR2=PARAM2;

INT VAR3=PARAM3;

PRINTF("0X%08X\N",?M1); //打印出各个变量的内存地址

PRINTF("0X%08X\N",?M2);

PRINTF("0X%08X\N\N",?M3);

PRINTF("0X%08X\N",&VAR1);

PRINTF("0X%08X\N",&VAR2);

PRINTF("0X%08X\N\N",&VAR3);

RETURN;

}

INT MAIN()

{

FUNC(1,2,3);

RETURN 0;

}

编译后的执行结果是:

0X0012FF78

0X0012FF7C

0X0012FF80

0X0012FF68

0X0012FF6C

0X0012FF70

├———————┤<—函数执行时的栈顶(ESP)、低端内存区域

│……│

├———————┤

│VAR 1 │

├———————┤

│VAR 2 │

├———————┤

│VAR 3 │

├———————┤

│RET │

├———————┤<—“__CDECL”函数返回后的栈顶(ESP)

│PARAMETER 1 │

├———————┤

│PARAMETER 2 │

├———————┤

│PARAMETER 3 │

├———————┤<—“__STDCALL”函数返回后的栈顶(ESP)

│……│

├———————┤<—栈底(基地址EBP)、高端内存区域

上图就是函数调用过程中堆栈的样子了。首先,三个参数以从又到左的次序压入堆栈,先压“PARAM3”,再压

“PARAM2”,最后压入“PARAM1”;然后压入函数的返回地址(RET),接着跳转到函数地址接着执行(这里要补充一点,介绍UNIX下的缓冲溢出原理的文章中都提到在压入RET后,继续压入当前EBP,然后用当前ESP 代替EBP。然而,有一篇介绍WINDOWS下函数调用的文章中说,在WINDOWS下的函数调用也有这一步骤,但根据我的实际调试,并未发现这一步,这还可以从PARAM3和VAR1之间只有4字节的间隙这点看出来);第三步,将栈顶(ESP)减去一个数,为本地变量分配内存空间,上例中是减去12字节(ESP=ESP-3*4,每个INT 变量占用4个字节);接着就初始化本地变量的内存空间。由于“__STDCALL”调用由被调函数调整堆栈,所以在函数返回前要恢复堆栈,先回收本地变量占用的内存(ESP=ESP+3*4),然后取出返回地址,填入EIP寄存器,回收先前压入参数占用的内存(ESP=ESP+3*4),继续执行调用者的代码。参见下列汇编代码:

;--------------FUNC 函数的汇编代码-------------------

:00401000 83EC0C SUB ESP, 0000000C //创建本地变量的内存空间

:00401003 8B442410 MOV EAX, DWORD PTR [ESP+10]

:00401007 8B4C2414 MOV ECX, DWORD PTR [ESP+14]

:0040100B 8B542418 MOV EDX, DWORD PTR [ESP+18]

:0040100F 89442400 MOV DWORD PTR [ESP], EAX

:00401013 8D442410 LEA EAX, DWORD PTR [ESP+10]

:00401017 894C2404 MOV DWORD PTR [ESP+04], ECX

……………………(省略若干代码)

:00401075 83C43C ADD ESP, 0000003C ;恢复堆栈,回收本地变量的内存空间

:00401078 C3 RET 000C ;函数返回,恢复参数占用的内存空间

;如果是“__CDECL”的话,这里是“RET”,堆栈将由调用者恢复

;-------------------函数结束-------------------------

;--------------主程序调用FUNC函数的代码--------------

:00401080 6A03 PUSH 00000003 //压入参数PARAM3

:00401082 6A02 PUSH 00000002 //压入参数PARAM2

:00401084 6A01 PUSH 00000001 //压入参数PARAM1

:00401086 E875FFFFFF CALL 00401000 //调用FUNC函数

;如果是“__CDECL”的话,将在这里恢复堆栈,“ADD ESP, 0000000C”

聪明的读者看到这里,差不多就明白缓冲溢出的原理了。先来看下面的代码:

#INCLUDE

#INCLUDE

VOID __STDCALL FUNC()

{

CHAR LPBUFF[8]="\0";

STRCAT(LPBUFF,"AAAAAAAAAAA");

RETURN;

}

INT MAIN()

{

FUNC();

RETURN 0;

}

编译后执行一下回怎么样?哈,“"0X00414141"指令引用的"0X00000000"内存。该内存不能为"READ"。”,“非法操作”喽!"41"就是"A"的16进制的ASCII码了,那明显就是STRCAT这句出的问题了。"LPBUFF"

的大小只有8字节,算进结尾的\0,那STRCAT最多只能写入7个"A",但程序实际写入了11个"A"外加1个\0。再来看看上面那幅图,多出来的4个字节正好覆盖了RET的所在的内存空间,导致函数返回到一个错误的内存地址,执行了错误的指令。如果能精心构造这个字符串,使它分成三部分,前一部份仅仅是填充的无意义数据以达到溢出的目的,接着是一个覆盖RET的数据,紧接着是一段SHELLCODE,那只要着个RET地址能指向这段SHELLCODE的第一个指令,那函数返回时就能执行SHELLCODE了。但是软件的不同版本和不同的运行环境都可能影响这段SHELLCODE在内存中的位置,那么要构造这个RET是十分困难的。一般都在RET和SHELLCODE之间填充大量的NOP指令,使得EXPLOIT有更强的通用性。

├———————┤<—低端内存区域

│……│

├———————┤<—由EXPLOIT填入数据的开始

││

│BUFFER │<—填入无用的数据

││

├———————┤

│RET │<—指向SHELLCODE,或NOP指令的范围

├———————┤

│NOP │

│……│<—填入的NOP指令,是RET可指向的范围

│NOP │

├———————┤

││

│SHELLCODE │

││

├———————┤<—由EXPLOIT填入数据的结束

│……│

├———————┤<—高端内存区域

WINDOWS下的动态数据除了可存放在栈中,还可以存放在堆中。了解C++的朋友都知道,C++可以使用NEW 关键字来动态分配内存。来看下面的C++代码:

#INCLUDE

#INCLUDE

#INCLUDE

VOID FUNC()

{

CHAR *BUFFER=NEW CHAR[128];

CHAR BUFFLOCAL[128];

STATIC CHAR BUFFSTATIC[128];

PRINTF("0X%08X\N",BUFFER); //打印堆中变量的内存地址

PRINTF("0X%08X\N",BUFFLOCAL); //打印本地变量的内存地址

PRINTF("0X%08X\N",BUFFSTATIC); //打印静态变量的内存地址

}

VOID MAIN()

{

FUNC();

RETURN;

}

程序执行结果为:

0X004107D0

0X0012FF04

0X004068C0

可以发现用NEW关键字分配的内存即不在栈中,也不在静态数据区。VC编译器是通过WINDOWS下的“堆(HEAP)”来实现NEW关键字的内存动态分配。在讲“堆”之前,先来了解一下和“堆”有关的几个API函数:HEAPALLOC 在堆中申请内存空间

HEAPCREATE 创建一个新的堆对象

HEAPDESTROY 销毁一个堆对象

HEAPFREE 释放申请的内存

HEAPWALK 枚举堆对象的所有内存块

GETPROCESSHEAP 取得进程的默认堆对象

GETPROCESSHEAPS 取得进程所有的堆对象

LOCALALLOC

GLOBALALLOC

当进程初始化时,系统会自动为进程创建一个默认堆,这个堆默认所占内存的大小为1M。堆对象由系统进行管理,它在内存中以链式结构存在。通过下面的代码可以通过堆动态申请内存空间:

HANDLE HHEAP=GETPROCESSHEAP();

CHAR *BUFF=HEAPALLOC(HHEAP,0,8);

其中HHEAP是堆对象的句柄,BUFF是指向申请的内存空间的地址。那这个HHEAP究竟是什么呢?它的值有什么意义吗?看看下面这段代码吧:

#PRAGMA COMMENT(LINKER,"/ENTRY:MAIN") //定义程序的入口

#INCLUDE

_CRTIMP INT (__CDECL *PRINTF)(CONST CHAR *, ...); //定义STL函数PRINTF

/*---------------------------------------------------------------------------

写到这里,我们顺便来复习一下前面所讲的知识:

(*注)PRINTF函数是C语言的标准函数库中函数,VC的标准函数库由MSVCRT.DLL模块实现。

由函数定义可见,PRINTF的参数个数是可变的,函数内部无法预先知道调用者压入的参数个数,函数只能通过分析第一个参数字符串的格式来获得压入参数的信息,由于这里参数的个数是动态的,所以必须由调用者来平衡堆栈,这里便使用了__CDECL调用规则。BTW,WINDOWS系统的API函数基本上是__STDCALL调用形式,只有一个API例外,那就是WSPRINTF,它使用__CDECL调用规则,同PRINTF函数一样,这是由于它的参数个数是可变的缘故。

---------------------------------------------------------------------------*/

VOID MAIN()

{

HANDLE HHEAP=GETPROCESSHEAP();

CHAR *BUFF=HEAPALLOC(HHEAP,0,0X10);

CHAR *BUFF2=HEAPALLOC(HHEAP,0,0X10);

HMODULE HMSVCRT=LOADLIBRARY("MSVCRT.DLL");

PRINTF=(VOID *)GETPROCADDRESS(HMSVCRT,"PRINTF");

PRINTF("0X%08X\N",HHEAP);

PRINTF("0X%08X\N",BUFF);

PRINTF("0X%08X\N\N",BUFF2);

}

执行结果为:

0X00130000

0X00133100

0X00133118

HHEAP的值怎么和那个BUFF的值那么接近呢?其实HHEAP这个句柄就是指向HEAP首部的地址。在进程的用户区存着一个叫PEB(进程环境块)的结构,这个结构中存放着一些有关进程的重要信息,其中在PEB首地址偏移0X18处存放的PROCESSHEAP就是进程默认堆的地址,而偏移0X90处存放了指向进程所有堆的地址列表的指针。WINDOWS有很多API都使用进程的默认堆来存放动态数据,如WINDOWS 2000下的所有ANSI 版本的函数都是在默认堆中申请内存来转换ANSI字符串到UNICODE字符串的。对一个堆的访问是顺序进行的,同一时刻只能有一个线程访问堆中的数据,当多个线程同时有访问要求时,只能排队等待,这样便造成程序执行效率下降。

最后来说说内存中的数据对齐。所位数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍,DWORD 数据的内存起始地址能被4除尽,WORD数据的内存起始地址能被2除尽,X86 CPU能直接访问对齐的数据,当他试图访问一个未对齐的数据时,会在内部进行一系列的调整,这些调整对于程序来说是透明的,但是会降低运行速度,所以编译器在编译程序时会尽量保证数据对齐。同样一段代码,我们来看看用VC、DEV-C++和LCC 三个不同编译器编译出来的程序的执行结果:

#INCLUDE

INT MAIN()

{

INT A;

CHAR B;

INT C;

PRINTF("0X%08X\N",&A);

PRINTF("0X%08X\N",&B);

PRINTF("0X%08X\N",&C);

RETURN 0;

}

这是用VC编译后的执行结果:

0X0012FF7C

0X0012FF7B

0X0012FF80

变量在内存中的顺序:B(1字节)-A(4字节)-C(4字节)。

这是用DEV-C++编译后的执行结果:

0X0022FF7C

0X0022FF7B

0X0022FF74

变量在内存中的顺序:C(4字节)-中间相隔3字节-B(占1字节)-A(4字节)。

这是用LCC编译后的执行结果:

0X0012FF6C

0X0012FF6B

0X0012FF64

变量在内存中的顺序:同上。

三个编译器都做到了数据对齐,但是后两个编译器显然没VC“聪明”,让一个CHAR占了4字节,浪费内存哦。

基础知识:

堆栈是一种简单的数据结构,是一种只允许在其一端进行插入或删除的线性表。允许插入或删除操作的一端称为栈顶,另一端称为栈底,对堆栈的插入和删除操作被称为入栈和出栈。有一组CPU指令可以实现对进程的内存实现堆栈访问。其中,POP指令实现出栈操作,PUSH指令实现入栈操作。CPU的ESP寄存器存放当前线程的栈顶指针,EBP寄存器中保存当前线程的栈底指针。CPU的EIP寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。

参考:《WINDOWS下的HEAP溢出及其利用》BY: ISNO

《WINDOWS核心编程》BY: JEFFREY RICHTER

摘要:讨论常见的堆性能问题以及如何防范它们。(共9 页)

前言

您是否是动态分配的C/C++ 对象忠实且幸运的用户?您是否在模块间的往返通信中频繁地使用了“自动化”?您的程序是否因堆分配而运行起来很慢?不仅仅您遇到这样的问题。几乎所有项目迟早都会遇到堆问题。大家都想说,“我的代码真正好,只是堆太慢”。那只是部分正确。更深入理解堆及其用法、以及会发生什么问题,是很有用的。

什么是堆?

(如果您已经知道什么是堆,可以跳到“什么是常见的堆性能问题?”部分)

在程序中,使用堆来动态分配和释放对象。在下列情况下,调用堆操作:

事先不知道程序所需对象的数量和大小。

对象太大而不适合堆栈分配程序。

堆使用了在运行时分配给代码和堆栈的内存之外的部分内存。下图给出了堆分配程序的不同层。GLOBALALLOC/GLOBALFREE:MICROSOFT WIN32 堆调用,这些调用直接与每个进程的默认堆进行对话。

LOCALALLOC/LOCALFREE:WIN32 堆调用(为了与MICROSOFT WINDOWS NT 兼容),这些调用直接与每个进程的默认堆进行对话。

COM 的IMALLOC 分配程序(或COTASKMEMALLOC / COTASKMEMFREE):函数使用每个进程的默认堆。自动化程序使用“组件对象模型(COM)”的分配程序,而申请的程序使用每个进程堆。

C/C++ 运行时(CRT) 分配程序:提供了MALLOC() 和FREE() 以及NEW 和DELETE 操作符。如MICROSOFT VISUAL BASIC 和JAVA 等语言也提供了新的操作符并使用垃圾收集来代替堆。CRT 创建自己的私有堆,驻留在WIN32 堆的顶部。

WINDOWS NT 中,WIN32 堆是WINDOWS NT 运行时分配程序周围的薄层。所有API 转发它们的请求给NTDLL。

WINDOWS NT 运行时分配程序提供WINDOWS NT 内的核心堆分配程序。它由具有128 个大小从8 到1,024 字节的空闲列表的前端分配程序组成。后端分配程序使用虚拟内存来保留和提交页。

在图表的底部是“虚拟内存分配程序”,操作系统使用它来保留和提交页。所有分配程序使用虚拟内存进行数据的存取。

分配和释放块不就那么简单吗?为何花费这么长时间?

堆实现的注意事项

传统上,操作系统和运行时库是与堆的实现共存的。在一个进程的开始,操作系统创建一个默认堆,叫做“进程堆”。如果没有其他堆可使用,则块的分配使用“进程堆”。语言运行时也能在进程内创建单独的堆。(例如,C 运行时创建它自己的堆。)除这些专用的堆外,应用程序或许多已载入的动态链接库(DLL) 之一可以创建和使用单独的堆。WIN32 提供一整套API 来创建和使用私有堆。有关堆函数(英文)的详尽指导,请参见MSDN。当应用程序或DLL 创建私有堆时,这些堆存在于进程空间,并且在进程内是可访问的。从给定堆分配的数据将在同一个堆上释放。(不能从一个堆分配而在另一个堆释放。)

在所有虚拟内存系统中,堆驻留在操作系统的“虚拟内存管理器”的顶部。语言运行时堆也驻留在虚拟内存顶部。某些情况下,这些堆是操作系统堆中的层,而语言运行时堆则通过大块的分配来执行自己的内存管理。不使用操作系统堆,而使用虚拟内存函数更利于堆的分配和块的使用。

典型的堆实现由前、后端分配程序组成。前端分配程序维持固定大小块的空闲列表。对于一次分配调用,堆尝试从前端列表找到一个自由块。如果失败,堆被迫从后端(保留和提交虚拟内存)分配一个大块来满足请求。通用的实现有每块分配的开销,这将耗费执行周期,也减少了可使用的存储空间。

KNOWLEDGE BASE 文章Q10758,“用CALLOC() 和MALLOC() 管理内存”(搜索文章编号), 包含了有关这些主题的更多背景知识。另外,有关堆实现和设计的详细讨论也可在下列著作中找到:“DYNAMIC STORAGE ALLOCATION: A SURVEY AND CRITICAL REVIEW”,作者PAUL R. WILSON、MARK S. JOHNSTONE、MICHAEL NEELY 和DAVID BOLES;“INTERNATIONAL WORKSHOP ON MEMORY MANAGEMENT”, 作者KINROSS, SCOTLAND, UK, 1995 年9 月( https://www.360docs.net/doc/261518188.html,/USERS/OOPS/PAPERS.HTML)(英文)。

WINDOWS NT 的实现(WINDOWS NT 版本4.0 和更新版本)使用了127 个大小从8 到1,024 字节的8 字节对齐块空闲列表和一个“大块”列表。“大块”列表(空闲列表[0])保存大于1,024 字节的块。空闲列表容纳了用双向链表链接在一起的对象。默认情况下,“进程堆”执行收集操作。(收集是将相邻空闲块合并成一个大块的操作。)收集耗费了额外的周期,但减少了堆块的内部碎片。

单一全局锁保护堆,防止多线程式的使用。(请参见“SERVER PERFORMANCE AND SCALABILITY KILLERS”中的第一个注意事项, GEORGE REILLY 所著,在“MSDN ONLINE WEB WORKSHOP”上(站点:https://www.360docs.net/doc/261518188.html,/WORKSHOP/SERVER/IIS/TENCOM.ASP(英文)。)单一全局锁本质上是用来保护堆数据结构,防止跨多线程的随机存取。若堆操作太频繁,单一全局锁会对性能有不利的影响。

什么是常见的堆性能问题?

以下是您使用堆时会遇到的最常见问题:

分配操作造成的速度减慢。光分配就耗费很长时间。最可能导致运行速度减慢原因是空闲列表没有块,所以运行时分配程序代码会耗费周期寻找较大的空闲块,或从后端分配程序分配新块。

释放操作造成的速度减慢。释放操作耗费较多周期,主要是启用了收集操作。收集期间,每个释放操作“查找”它的相邻块,取出它们并构造成较大块,然后再把此较大块插入空闲列表。在查找期间,内存可能会随机碰到,从而导致高速缓存不能命中,性能降低。

堆竞争造成的速度减慢。当两个或多个线程同时访问数据,而且一个线程继续进行之前必须等待另一个线程完成时就发生竞争。竞争总是导致麻烦;这也是目前多处理器系统遇到的最大问题。当大量使用内存块的应用程序或DLL 以多线程方式运行(或运行于多处理器系统上)时将导致速度减慢。单一锁定的使用—常用的解决方案—意味着使用堆的所有操作是序列化的。当等待锁定时序列化会引起线程切换上下文。可以想象交叉路口闪烁的红灯处走走停停导致的速度减慢。

竞争通常会导致线程和进程的上下文切换。上下文切换的开销是很大的,但开销更大的是数据从处理器高速缓存中丢失,以及后来线程复活时的数据重建。

堆破坏造成的速度减慢。造成堆破坏的原因是应用程序对堆块的不正确使用。通常情形包括释放已释放的堆块或使用已释放的堆块,以及块的越界重写等明显问题。(破坏不在本文讨论范围之内。有关内存重写和泄漏等其他

细节,请参见MICROSOFT VISUAL C++(R) 调试文档。)

频繁的分配和重分配造成的速度减慢。这是使用脚本语言时非常普遍的现象。如字符串被反复分配,随重分配增长和释放。不要这样做,如果可能,尽量分配大字符串和使用缓冲区。另一种方法就是尽量少用连接操作。

竞争是在分配和释放操作中导致速度减慢的问题。理想情况下,希望使用没有竞争和快速分配/释放的堆。可惜,现在还没有这样的通用堆,也许将来会有。

在所有的服务器系统中(如IIS、MSPROXY、DATABASESTACKS、网络服务器、EXCHANGE 和其他), 堆锁定实在是个大瓶颈。处理器数越多,竞争就越会恶化。

尽量减少堆的使用

现在您明白使用堆时存在的问题了,难道您不想拥有能解决这些问题的超级魔棒吗?我可希望有。但没有魔法能使堆运行加快—因此不要期望在产品出货之前的最后一星期能够大为改观。如果提前规划堆策略,情况将会大大好转。调整使用堆的方法,减少对堆的操作是提高性能的良方。

如何减少使用堆操作?通过利用数据结构内的位置可减少堆操作的次数。请考虑下列实例:

STRUCT OBJECTA {

// OBJECTA 的数据

}

STRUCT OBJECTB {

// OBJECTB 的数据

}

// 同时使用OBJECTA 和OBJECTB

//

// 使用指针

//

STRUCT OBJECTB {

STRUCT OBJECTA * POBJA;

// OBJECTB 的数据

}

//

// 使用嵌入

//

STRUCT OBJECTB {

STRUCT OBJECTA POBJA;

// OBJECTB 的数据

}

//

// 集合–在另一对象内使用OBJECTA 和OBJECTB

//

STRUCT OBJECTX {

STRUCT OBJECTA OBJA;

STRUCT OBJECTB OBJB;

}

避免使用指针关联两个数据结构。如果使用指针关联两个数据结构,前面实例中的对象 A 和 B 将被分别分配和释放。这会增加额外开销—我们要避免这种做法。

把带指针的子对象嵌入父对象。当对象中有指针时,则意味着对象中有动态元素(百分之八十)和没有引用的新位置。嵌入增加了位置从而减少了进一步分配/释放的需求。这将提高应用程序的性能。

合并小对象形成大对象(聚合)。聚合减少分配和释放的块的数量。如果有几个开发者,各自开发设计的不同部分,则最终会有许多小对象需要合并。集成的挑战就是要找到正确的聚合边界。

内联缓冲区能够满足百分之八十的需要(AKA 80-20 规则)。个别情况下,需要内存缓冲区来保存字符串/二进制数据,但事先不知道总字节数。估计并内联一个大小能满足百分之八十需要的缓冲区。对剩余的百分之二十,可以分配一个新的缓冲区和指向这个缓冲区的指针。这样,就减少分配和释放调用并增加数据的位置空间,从根本上提高代码的性能。

在块中分配对象(块化)。块化是以组的方式一次分配多个对象的方法。如果对列表的项连续跟踪,例如对一个{名称,值} 对的列表,有两种选择:选择一是为每一个“名称-值”对分配一个节点;选择二是分配一个能容纳(如五个)“名称-值”对的结构。例如,一般情况下,如果存储四对,就可减少节点的数量,如果需要额外的空间数量,则使用附加的链表指针。

块化是友好的处理器高速缓存,特别是对于L1-高速缓存,因为它提供了增加的位置—不用说对于块分配,很多数据块会在同一个虚拟页中。

正确使用_AMBLKSIZ。C 运行时(CRT) 有它的自定义前端分配程序,该分配程序从后端(WIN32 堆)分配大小为_AMBLKSIZ 的块。将_AMBLKSIZ 设置为较高的值能潜在地减少对后端的调用次数。这只对广泛使用CRT 的程序适用。

使用上述技术将获得的好处会因对象类型、大小及工作量而有所不同。但总能在性能和可升缩性方面有所收获。另一方面,代码会有点特殊,但如果经过深思熟虑,代码还是很容易管理的。

其他提高性能的技术

下面是一些提高速度的技术:

使用WINDOWS NT5 堆

由于几个同事的努力和辛勤工作,1998 年初MICROSOFT WINDOWS(R) 2000 中有了几个重大改进:

改进了堆代码内的锁定。堆代码对每堆一个锁。全局锁保护堆数据结构,防止多线程式的使用。但不幸的是,在高通信量的情况下,堆仍受困于全局锁,导致高竞争和低性能。WINDOWS 2000 中,锁内代码的临界区将竞争的可能性减到最小,从而提高了可伸缩性。

使用“LOOKASIDE”列表。堆数据结构对块的所有空闲项使用了大小在8 到1,024 字节(以8-字节递增)的快速高速缓存。快速高速缓存最初保护在全局锁内。现在,使用LOOKASIDE 列表来访问这些快速高速缓存空闲列表。这些列表不要求锁定,而是使用64 位的互锁操作,因此提高了性能。

内部数据结构算法也得到改进。

这些改进避免了对分配高速缓存的需求,但不排除其他的优化。使用WINDOWS NT5 堆评估您的代码;它对小于1,024 字节(1 KB) 的块(来自前端分配程序的块)是最佳的。GLOBALALLOC() 和LOCALALLOC() 建立在同一堆上,是存取每个进程堆的通用机制。如果希望获得高的局部性能,则使用HEAP(R) API 来存取每个进程堆,或为分配操作创建自己的堆。如果需要对大块操作,也可以直接使用VIRTUALALLOC() / VIRTUALFREE() 操作。

上述改进已在WINDOWS 2000 BETA 2 和WINDOWS NT 4.0 SP4 中使用。改进后,堆锁的竞争率显著降低。这使所有WIN32 堆的直接用户受益。CRT 堆建立于WIN32 堆的顶部,但它使用自己的小块堆,因而不能从WINDOWS NT 改进中受益。(VISUAL C++ 版本6.0 也有改进的堆分配程序。)

使用分配高速缓存

分配高速缓存允许高速缓存分配的块,以便将来重用。这能够减少对进程堆(或全局堆)的分配/释放调用的次数,也允许最大限度的重用曾经分配的块。另外,分配高速缓存允许收集统计信息,以便较好地理解对象在较高层次上的使用。

典型地,自定义堆分配程序在进程堆的顶部实现。自定义堆分配程序与系统堆的行为很相似。主要的差别是它在进程堆的顶部为分配的对象提供高速缓存。高速缓存设计成一套固定大小(如32 字节、64 字节、128 字节等)。这一个很好的策略,但这种自定义堆分配程序丢失与分配和释放的对象相关的“语义信息”。

与自定义堆分配程序相反,“分配高速缓存”作为每类分配高速缓存来实现。除能够提供自定义堆分配程序的所有好处之外,它们还能够保留大量语义信息。每个分配高速缓存处理程序与一个目标二进制对象关联。它能够使用一套参数进行初始化,这些参数表示并发级别、对象大小和保持在空闲列表中的元素的数量等。分配高速缓存处理程序对象维持自己的私有空闲实体池(不超过指定的阀值)并使用私有保护锁。合在一起,分配高速缓存和私有锁减少了与主系统堆的通信量,因而提供了增加的并发、最大限度的重用和较高的可伸缩性。

需要使用清理程序来定期检查所有分配高速缓存处理程序的活动情况并回收未用的资源。如果发现没有活动,将释放分配对象的池,从而提高性能。

可以审核每个分配/释放活动。第一级信息包括对象、分配和释放调用的总数。通过查看它们的统计信息可以得出各个对象之间的语义关系。利用以上介绍的许多技术之一,这种关系可以用来减少内存分配。

分配高速缓存也起到了调试助手的作用,帮助您跟踪没有完全清除的对象数量。通过查看动态堆栈返回踪迹和除没有清除的对象之外的签名,甚至能够找到确切的失败的调用者。

MP 堆

MP 堆是对多处理器友好的分布式分配的程序包,在WIN32 SDK(WINDOWS NT 4.0 和更新版本)中可以得到。最初由JVERT 实现,此处堆抽象建立在WIN32 堆程序包的顶部。MP 堆创建多个WIN32 堆,并试图将分配调用分布到不同堆,以减少在所有单一锁上的竞争。

本程序包是好的步骤—一种改进的MP-友好的自定义堆分配程序。但是,它不提供语义信息和缺乏统计功能。通常将MP 堆作为SDK 库来使用。如果使用这个SDK 创建可重用组件,您将大大受益。但是,如果在每个DLL 中建立这个SDK 库,将增加工作设置。

重新思考算法和数据结构

要在多处理器机器上伸缩,则算法、实现、数据结构和硬件必须动态伸缩。请看最经常分配和释放的数据结构。试问,“我能用不同的数据结构完成此工作吗?”例如,如果在应用程序初始化时加载了只读项的列表,这个列表不必是线性链接的列表。如果是动态分配的数组就非常好。动态分配的数组将减少内存中的堆块和碎片,从而增强性能。

减少需要的小对象的数量减少堆分配程序的负载。例如,我们在服务器的关键处理路径上使用五个不同的对象,每个对象单独分配和释放。一起高速缓存这些对象,把堆调用从五个减少到一个,显著减少了堆的负载,特别当每秒钟处理1,000 个以上的请求时。

如果大量使用“AUTOMATION”结构,请考虑从主线代码中删除“AUTOMATION BSTR”,或至少避免重复的BSTR 操作。(BSTR 连接导致过多的重分配和分配/释放操作。)

摘要

对所有平台往往都存在堆实现,因此有巨大的开销。每个单独代码都有特定的要求,但设计能采用本文讨论的基本理论来减少堆之间的相互作用。

评价您的代码中堆的使用。

改进您的代码,以使用较少的堆调用:分析关键路径和固定数据结构。

在实现自定义的包装程序之前使用量化堆调用成本的方法。

如果对性能不满意,请要求OS 组改进堆。更多这类请求意味着对改进堆的更多关注。

要求 C 运行时组针对OS 所提供的堆制作小巧的分配包装程序。随着OS 堆的改进,C 运行时堆调用的成本将减小。

操作系统(WINDOWS NT 家族)正在不断改进堆。请随时关注和利用这些改进。

MURALI KRISHNAN 是INTERNET INFORMATION SERVER (IIS) 组的首席软件设计工程师。从1.0 版本开始他就设计IIS,并成功发行了 1.0 版本到 4.0 版本。MURALI 组织并领导IIS 性能组三年(1995-1998), 从一开始就影响IIS 性能。他拥有威斯康星州MADISON 大学的M.S.和印度ANNA 大学的B.S.。工作之外,他喜欢阅读、打排球和家庭烹饪。

https://www.360docs.net/doc/261518188.html,/EXPERT/FAQ/FAQ_INDEX.ASP?ID=172835

我在学习对象的生存方式的时候见到一种是在堆栈(STACK)之中,如下

COBJECT OBJECT;

还有一种是在堆(HEAP)中如下

COBJECT* POBJECT=NEW COBJECT();

请问

(1)这两种方式有什么区别?

(2)堆栈与堆有什么区别??

---------------------------------------------------------------

1) ABOUT STACK, SYSTEM WILL ALLOCATE MEMORY TO THE INSTANCE OF OBJECT AUTOMATICALLY, AND TO THE

HEAP, YOU MUST ALLOCATE MEMORY TO THE INSTANCE OF OBJECT WITH NEW OR MALLOC MANUALLY.

2) WHEN FUNCTION ENDS, SYSTEM WILL AUTOMATICALLY FREE THE MEMORY AREA OF STACK, BUT TO THE

HEAP, YOU MUST FREE THE MEMORY AREA MANUALLY WITH FREE OR DELETE, ELSE IT WILL RESULT IN MEMORY

LEAK.

3)栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

4)堆上分配的内存可以有我们自己决定,使用非常灵活。

(完整版)配送中心管理制度

配送中心管理制度 第一章总则 配送中心是公司配送各种物资、周转储备、物资管理的重要场所。它的主要任务是:保管好库存物资,做到数量准确,质量完好,确保安全,收发迅速,降低费用,加速资金周转。 为了保障配送中心货品保管安全、提高仓库工作效率和物流对接规范,制定本管理制度,确保本公司的物资储运安全。 第二章适用范围 一、针对配送中心分拣总库、冷藏库等。 二、使用于配送中心所有员工。 第三章岗位设置与职责 一、配送中心包括采购部、分拣总库、冷藏库、司机班等;设经理、采购主管、仓库主管、司机(配送员)、勤杂工等岗位。 二、配送中心经理负责中心的全面管理,公司物资采购计划汇总及实施、供应商的开发与管理、物资的储备与保管、配送与运输、安全与防范、部门之间的协调等工作,保证按时、按量供应质优、价美的物品给客户。 三、采购主管负责按申购计划及时采购各种物资,并对所有采购物资的质量及其安全性、可靠性负责,及时了解和反馈市场行情,做到保质、比价、择优、安全、及时。

四、仓库主管负责物料的收单、收料、检验、入库、发料、退料、储存、防护等工作,对所收发、保管的物品数量和质量负责,执行5S规范,做到分区储货、存量合理、账物相符、日清日结。 五、司机兼任配送员,负责配送、运输、交收物品及其清单,对所配送的物品数量及其完整性负责,配送车辆的维护管理工作,做到按单派货、安全快速。 六、仓库勤杂工负责物料装卸、搬运、包装、防护等工作,对所包装、装运的物品数量及其完整性负责,做到分类包装、分类装卸、轻拿轻放、安全作业。 七、财务部作为业务监督部门,负责定期盘点、核查配送中心的货物流转与储存情况,并主导报废物品的处理。 第三章作业行为规范 一、严格遵守公司和部门各项规章制度: 1、自觉遵守公司的《员工手册》及本部门的规章制度,服从领导,强调守时和效率,主动工作,服务好一线部门。 2、仓库主管申购汇总收单要全面及时、分门别类、做好与各部门的沟通,根据库存量对申购单进行分解处理,拟定合理的采购与配送计划,并将采购计划及时上报。 3、采购主管根据采购计划及时采购,坚持货比三家、质量保证、安全可靠、价格合理的采购原则;对长期定量的采购项目,寻找具备相应资质的供应商合作。 4、配送人员(司机)要对单作业,及时完成各配送线路/客户的食材及其物料的交接清点和单据签收汇报工作,明确配送路线,了解货物清单,控制好交接时点,安全驾驶,不违反交通规则,勤清洁保养车辆。

数据结构课程设计 数制转换 数组和栈

中北大学 数据结构与算法课程设计 说明书 学院、系:软件学院 专业:软件工程 学生姓名:xxx 学号:xxxx 设计题目:数制转换问题 起迄日期: 2013年12月9日- 2013年12月20日指导教师:xxx 2013 年12月 20 日

1、需求分析 任意给定一个M进制的数x ,请实现如下要求 1) 求出此数x的10进制值(用MD表示) 2) 实现对x向任意的一个非M进制的数的转换。 3) 用两种方法实现上述要求(用栈解决和用数组解决)。 2、概要设计 流程图 数组的流程图:

栈的流程图:

算法思想 1、用数组实现该问题: DtoM()函数和MtoD()函数是实现该问题的主要函数。DtoM()函数是实现十进制转换为其它进制的函数,它是将输入的十进制数x取首先对需要转换的进制M取余,然后再对其取整,接着通过递归调用DtoM()函数依次将得到的整数部分依次先取余后取整,并将所得的余数依次存入一个数组中,然后逆向取出数组中的元素,即得到转换后的结果。而MtoD()函数则是实现其他进制M转换为十进制,并将其转换为非M进制的数。M进制转十进制则是从该M进制数的最后一位开始算,依次列为第0、1、2…n位并分别乘以M的0、1、2…n次方,将得到的次方相加便得到对应的十进制数,再调用DtoM()函数将其转换为非M进制的数。 2、用栈实现该问题: 同样是利用DtoM()和MtoD()两个函数实现。两个函数的思想同利用数组实现时相同。只是栈具有后进先出的性质,故其用Pop()取数较数组的逆向取数方便些。 模块划分 1、用数组实现该问题: ⑴i,j,y,n,s,m,r,reminder,x是定义的全局变量,初始值都为0; ⑵DtoM(int g,int h)是实现十进制数转换为M进制数的函数; ⑶MtoD()是实现M(仅指二进制数和八进制数)进制数转换为十进制数的函数,并 在其中调用D2M(int g,int h)实现向非M进制数的转换; ⑷HtoD(int f)是实现十六进制数转换为十进制数的函数,并在其中调用D2M(int g,int h)实现向非十六进制数的转换; ⑸void main()是主函数,功能是给出测试的数据,并在特定条件下调用D2M()

物流仓储及配送实训报告

实训报告

物流仓储与配送实训总结报告 一、实训容 这次实训的容主要是物流仓储和配送管理的基本理论和简单的实践。 二、情况回顾 经过一个学期的对仓储和配送的学习,我们对仓储的定义,基本功能等各个工作流程有了一定的理论基础,为了以后能更好的适应工作和学习,学校给我们安排了为期两周的室实训,以便我们掌握一些基本操作。两周的实训转眼间就要结束了,回顾两周的实训,我们收获不少,但也看见了自己的一些不足。这两周的实训是培养我们的实践能力,学以致用。 三、操作作业 我们到顺达物流公司进行了简单的参观,对于顺达的出入库作业和货物的储存管理等做了简单的了解。 成品、出口品在出入库时根据定单或出口装箱单出库,一定要坚持“先进先出”原则,特别在出口方面。还有出口时,装箱单上的箱号和封号要填写正确,为了做到先进先出,可再填制一记录单用于记录每一货品每次入库的数量和同一生产日的数量,出库时根据“先进先出”进行销帐,并记录余数。货物的储存一般分为3个步骤,收货-入库-货物管理。在收货的时候,要确定收货数量,记好货物订单号,最后确定入库。货物少的商品要是没有地方摆放就摆放在地上。在确定没有任何问题以后,对商品进行编号。给货物编号是最麻烦的一步,虽然没有什么体力劳动,但是要给货物编号学要对货物的品名规格,包装单位等每一方面分的清清楚楚。随后把货架上的货物与统计单进行核对,所以核对货物是必

需做的。每天出货结束以后都要对货物进行盘点,确定该日的工作也没有差错,如果发现错误应该及时纠正。 一、仓储管理在物流中的地位 从某种意义上讲,仓储管理在物流管理中占据着核心的地位。从物流的发展史可以看出,物流的研究最初是从解决“牛鞭效应”开始的,即在多环节的流通过程中,由于每个环节对于需求的预测存在误差,因此随着流通环节增加,误差被放大,库存也就越来越偏离实际的最终需求,从而带来保管成本和市场风险的提高。解决这个问题的思路,从研究合理的安全库存开始,到改变流程,建立集中的配送中心,以致到改变生产方式,实行订单生产,将静态的库存管理转变为动态的JIT配送,实现降低库存数量和周期的目的。在这个过程中,尽管仓库越来越集中,每个仓库覆盖的服务围越来越大,仓库吞吐的物品越来越多,操作越来越复杂,但是仓储的周期越来越短,成本不断递减的趋势一直没有改变。从发达国家的统计数据来看,现代物流的发展历史就是库存成本在总物流成本中所占比重逐步降低的历史。 从许多微观案例来看,仓储管理已成为供应链管理的核心环节。这是因为仓储总是出现在物流各环节的接合部,例如采购与生产之间,生产的初加工与精加工之间,生产与销售之间,批发与零售之间,不同运输方式转换之间等等。仓储是物流各环节之间存在不均衡性的表现,仓储也正是解决这种不均衡性的手段。仓储环节集中了上下游流程整合的所有矛盾,仓储管理就是在实现物流流程的整合。如果借用运筹学的语言来描述仓储管理在物流中的地位,可以说就是在运输条件为约束力的情况下,寻求最优库存(包括布局)方案作为控制手段,使得物流达到总成本最低的目标。在许多具体的案例中,物流的整合、优化实际上归结

三种不同方法解决数制转换问题

/////////////////方法一 #include #define S 10 void zh(int N,int r) { int L[S],top; int x; top=-1; while(N) { L[++top]=N%r; N=N/r; while(top!=-1) { x=L[top--]; printf("%d",x); } } printf("\n"); } main() { int w,z; scanf("%d%d",&w,&z); zh(w,z); } ///////////////////////////方法二 #include #include #define maxsize 50 void conversion(int n,int r) { int ss[maxsize]={0}; int i=0; int j; while(n) { ss[i]=(n%r); i++; n=n/r; } for(j=0;j

}//数制转换 void main() { int n=37; int r=4; printf("十进制数%2d转换为%d进制数。\n",n,r); conversion(n,r); } /////////////////方法三 #include #include #define maxsize 5 typedef struct { int data[maxsize]; int top; }seqstack; void init_seqstack(seqstack *s) { s->top=-1; }//栈的初始化 int empty_seqstack(seqstack *s) { if(s->top==-1) return 1; else return 0; }//空栈的判断 int push_seqstack(seqstack *s,int x) { if(s->top==maxsize-1) return 0; else { (s)->data[++(s)->top]=x; return (1); } }//进栈 int pop_seqstack(seqstack *s,int *x)

物流库房管理制度

物流中心库房管理制度 一、目的 加强对存货的管理和控制,保证存货的安全完整,提高存货的运营效率,保证合理确认存货价值,防止并及时发现和纠正存货业务中的各种差错和舞弊,使库房管理规范化,保证物资财产不受损失,根据企业管理和财务管理的一般要求,结合本公司的具体情况,制定本制度。 二、范围 本实施细则适用于四川懿科网络信息有限公司所有库房。 三、术语 (一)库房管理——指围绕公司运营管理活动而制订的有关公司运营管理方面的实施细则、管理规定等规范性文件。 四、管理职责 (一)物流中心 1、物流中心是组织,衔接,调节,管理物流活动的一个较大的物流据点。 2、以库房为基础,在各物流环节方面提供延伸服务。 2、组织制订、修改本公司库房管理各项规章制度。 3、检查、考核各部门库管对规章制度的制订及执行情况。 (二)库房管理 1、严格执行“库房管理制度”,按规定程序办事。 2、定期清理,总结自己的工作。 五、管理内容 (一)库房物资入库 1、物资入库,交货人须办理交货手续,库房管理员应在场,核对,清点物资名称、数量是否一致,交接双方在入库单上签字。 2、各部门项目余料入库,交货人应告知库房管理员余料是何项目上退下来的,以便核算项目成本,交接双方在入库单上签字。 3、各部门甲供材料入库,交货人应告知库房管理员是何部门何项目申请领下来的材料,核对材料数量,规格是否和材料申请单上的一致,交接双方在入库单上签字。 4、各部门采购入库的材料,库房管理员应核对,清点材料数量,名称,规格,并分类堆放,交接双方在入库单上签字。 5、各部门各项目的工具入库,经手人应告知库房管理员此工具的来源,并与库房管理员办好相关手续,交接双方在入库单上签字。 (二)库房物资保管

用栈实现把十进制转化为八进制

#include #include #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 typedef struct { int *base; int *top; int stacksize; }sqstack; int InitStack(sqstack &s) { s.base=(int *)malloc(STACK_INIT_SIZE*sizeof(int)); if(!s.base) exit(-1); s.top=s.base; s.stacksize=STACK_INIT_SIZE; return 1; } int Pop(sqstack &s,int &e) { if(s.top==s.base) return 0; e=*--s.top; return 1; } int Push(sqstack &s,int e) { if(s.top-s.base==s.stacksize) { s.base=(int *)realloc(s.base,(s.stacksize+STACKINCREMENT)*sizeof(int)); if(!s.base) exit(-1); s.top=s.base+s.stacksize; s.stacksize+=STACKINCREMENT; } *s.top++=e; return 1; } int StackEmpty(sqstack s) { if(s.top==s.base) return 0; else return 1; }

仓储与配送实务

单元一:概述 1.简述仓储的定义及其在物流活动中的重要性。 答:仓储的定义为利用仓库及相关设施设备进行货品的进库、存贮、出库的作业。仓储 除了有保管作用以外,还具有:(1)对货物静茹下个环节前的质 量起保证作用;(2)保证社会再生产过程顺利进行的必要条件; (3)优化库存管理、加快商品周转的重要手段;(4)能够为货物 静茹市场做好准备。 2.说明如何进行产品类别和仓库分类。 答:产品类别有:(1)按产品原材料分类;(2)按产品用途分类; (3)按产品在流通中处的位置分类;(4)按产品其他特征分类, 如产地、形状、尺寸、颜色等。 仓库分类有:(1)按用途分类有自有仓库、营业仓库、公共仓 库、保税仓库;(2)按结构和构造分类有平房仓库、多层仓库、 高层货架仓库、散货仓库、罐式仓库;(3)按仓库功能分类有生 产仓库、储备仓库、集配型仓库、加工型仓库;(4)按技术处理 方式及保管方式分类有普通仓库、冷藏仓库、恒温仓库、危险品仓 库;(5)按仓库选址分类有港口仓库、内陆仓库、枢纽站仓库。 3.简要回答储位管理的储位分区和管理原则。 答:储位分区通常分为:(1)预备储区,是商品进出仓库的一个暂存区;(2)保管储区,是仓库中最大最重要的保管区,商品在此时间最长;(3)动管储区,主要任务是对储区货物的整理、整顿和对拣货单的处理。 管理原则是储存空间明确,商品数量合理,变动更新及时。 4.阐述堆码的重要性和堆码原则。 答:堆码的重要性体现在堆码可以与所采用的机械作业相匹配,对维护商品质量具有重要的意义,能够充分利用库房容积和提高装卸作业 效率。 堆码的原则主要有:1.尽量利用库位空间;2.仓库通道与堆垛之前保持适当宽度与距离,提高装卸效率;3.根据货物类别不同,正确利用不同的堆码方式堆垛;5.库位不紧张时,尽量避免货物堆码的覆盖和拥挤。 5.描述流通加工的重要性和分类。

各种进制之间转换方法

各进制转换方法(转载) 一、计算机中数的表示: 首先,要搞清楚下面3个概念 ?数码:表示数的符号 ?基:数码的个数 ?权:每一位所具有的值 请看例子: 数制十进制二进制八进制十六进制 数码0~9 0~1 0~7 0~15 基10 2 8 16 权10o,101,102,…2o,21,22,…8o,81,82,…16o,161,162,…特点逢十进一逢二进一逢八进一逢十六进一 十进制4956= 4*103+9*102 +5*101+6*10o 二进制1011=1*23+0*22 +1*21+1*2o 八进制4275=4*83+2*82 +7*81+5*8o 十六进制81AE=8*163+1*162 +10*161+14*16o

二、各种进制的转换问题 1.二、八、十六进制转换成十进制 2.十进制转换成二、八、十六进制 3.二进制、八进制的互相转换 4.二进制、十六进制的互相转换 1、二、八、十六进制转换成十进制 方法:数码乘以相应权之和 2、十进制转换成二、八、十六进制 方法:连续除以基,直至商为0,从低到高记录余数

3、二进制、八进制的互相转换 方法: ?二进制转换成八进制:从右向左,每3位一组(不足3位左补0),转换成八进制 ?八进制转换成二进制:用3位二进制数代替每一位八进制数 例(1101001)2=(001,101,001)2=(151)8 例 (246)8=(010,100,110)2=(10100110)2 4、二进制、十六进制的互相转换 方法: ?二进制转换成十六进制:从右向左,每4位一组(不足4位左补0),转换成十六进制 ?十六进制转换成二进制:用4位二进制数代替每一位十六进制数 例(11010101111101)2=(0011,0101,0111,1101)2=(357D)16 例 (4B9E)16=(0100,1011,1001,1110)2=(100101110011110)2 三、各种进制数的运算

配送中心仓库安全管理制度

配送中心仓库安全管理制度 一、目的 为保证仓库管理工作的正常化,提高物流服务质量,保障员工的身体健康和中心库物资的安全,特制定本制度: 二、范围 本制度适用于配送中心中心仓日常安全管理,以及公司所有进出中心仓人员管理。 三、职责 配送中心主任负责,全面监督执行仓库安全管理措施。 配送中心仓管员负责,本区域安全管理工作。 配送中心配送员负责,本区域安全管理工作。 配送中心其他人员负责,仓库安全的日常维护监督工作。 四、安全管理内容 1.人员管理: 必须严格遵守公司劳动纪律和各项安全操作规程。 积极响应上级的工作安排;加强员工间的交流与合作。 内外库区禁止吸烟。 无正常有效单据取货、拣货、包装、发货的一律视为偷盗行为,配送中心有最终处罚权。 库区工作人员,有权要求出入库的其他部门员工出入登记。 供应商或相应的物流配送人员,必须在接货人员或者仓管员带领下登记进入库区。进入库区后只能在相应的区域内、在接货方相应人员的监管下作业。严禁人员随意流动、出入、逗留,离开库区时登记离开时间。

库区值班工作人员对所有进入库区的非仓储质检人员应进行必要的询问,谢绝无关人员入内,但须给予准确的指引和帮助。 节假日或非正常工作时间原则上非仓储质检人员不得出入库区。 严禁在库区会客、聚众聊天、大声喧哗、打扑克等有损公司形象的行为。 仓库出现员工内部偷盗行为,必须立即对责任人予以开除。出现损失的必须要求赔偿。情节较重的,追究其刑事责任。 2消防安全规范 必须设立安全员岗位有仓库工作人员兼任,全面负责仓库的安全管理。 对各类安全防护设施、安全标志和消防器材的维护应承包到个人,并每周两次的进行清洁、检查,不得擅自拆除、挪做他用或随意移动位置。 每月按安全检查记录表上各项内容进行检查,并作记录。 检查发现消防器材有异常时,须及时上报配送中心并及时更换。 每位员工必须熟练使用灭火器,定期强化员工的消防意识,规范对消防器材的操作。 内外库区杜绝火种。仓库内严禁使用明火。确因工作需要动火,必须提出动火申请,经批准后方可动火。 3日常安全管理 专用设备的使用及维护: 在仓库内使用电器设备,必须先对该设备及其线路认真检查,保证设备状态完好和电线绝缘良好,方可使用。 在使用专用设备的过程中,应以人员、设备、货物、基础设施的安全为前提。 专用车辆行使过程中,除驾驶员外,不得有任何人员侧坐或攀爬于车辆上。 仓库主管定期组织人员对各种设备、基础设施进行维护、保养,以保证不影响正常作业。

栈的应用:数制转换

数制转换: 十进制数N和其它d进制数的转换是计算机实现计算的基本问题,其解决方法很多,其中一种简单方法基于以下原理. N=(N div d)*d+N mod d (其中:div为整除运算,mod 为求余运算.) 例如:10进制数1348转换为8进制为:2504.其运算过程如下: N N div d N mod d 1348 168 4 168 21 0 21 2 5 2 0 2 假设现要编制一个满足下列要求的程序:对于输入的任意一个非负十进制整数,打印输出与其相等的八进制数,由于上述计算过程是从低位到高位顺序产生八进制数的各个位数,而打印输出,一般来说应从高位到低位进行,恰好和计算过程相反.因此,若将计算过程中得到的八进制数的各位顺序进栈,则按出栈序列打印输出的即为与输入对应的八进制数. 算法描述如下: void conversion() { InitStack(s); scanf(" %d",N); while(N) { push(s,n%8) N=N/8; } while(!StackEmpty(s)) { Pop(S,e); printf("%d",e); } } 这个是利用栈的先进后出特性的最简单的例子.在这个例子中,栈操作的序列是直线式的,即先一味地进栈,然后一味的出栈.也许有人会提出疑问:用数组直接实现不也很简单吗?仔细分析上述算法不难看出,栈的引入简化了程序设计的问题,划分了不同的关注层次,使思考的范围缩小了.而用数组不仅掩盖了问题的本质,还要分散精力去考虑数组下标增减等细节. 完整代码实现: #include<iostream> #include<stack> using namespace std; void change(int n,int d)

仓储与配送知识点

一、名词解释 1.仓储管理:仓储管理就是对仓库及仓库内储存的物资所进行的管理,是仓储机构为了充分利用所拥有的仓储资源提供仓储服务所进行的计划、组织、控制和协调过程。 2.保税仓库:为国际贸易的需要,设置在一国国土之上,但在海关关境之外的仓库。 3.中转仓库:又称为转运仓库,处于货物运输系统的中间环节,存放那些待转运的货物。 4.通用仓库:用以储存一般没有特殊要求的工业品或农、副产品的仓库。 5.自动化仓库:是指通过计算机和相应的自动控制设备对仓库的作业和仓储管理进行自动控制和管理,并通过自动化系统进行仓库作业的现代化仓库。 6定位储存是指每一项商品都有固定的储位,商品在储存时不可互相窜位。 7.分类储存是指所有货物按一定特性加以分类,每一类货物固定其储存位置,同类货物不同品种要按一定的法则来安排储位。 8.随机储存是根据库存货物及储位使用情况,随机安排和使用储位,各种商品的储位是随机产生的。 9.货运记录:是表明承运单位负有责任事故,收货单位据此索赔的基本文件。 10.普通记录:是承运部门开具的一般性证明文件,不具备索赔效力,仅作为收货单位向有关部门交涉处理的依据。 11.期末盘点法:是指在会计计算期末统一清点所有商品数量的方法。 12.循环盘点法:是指在每天每周清点一小部分商品,一个循环周期将每种商品至少清点一次的方法。 13.安全库存:是指为了防止由于不确定因素(如突发性大量订货、供应商延期交货等)影响订货需求而准备的缓冲库存。 14.定期订货法:是按预先确定的订货时间间隔进行订货补充的库存管理方法。 15.定量订货法:是指当库存量下降到预定的库存数量(订货点)时,按经济订货批量为标准进行订货的一种库存管理方式。 16.商品养护:商品养护是商品在储运过程中所进行的保养和维护。 17.批量拣取:将多张订单集合成一批,按照商品品种类别加总后再进行拣货,然后依据不同客户或不同订单分类集中地拣货方式。 18.按单拣选:是针对每一份订单,作业员巡回于仓库中,按照订单所列商品及数量,将客户所订购的商品逐一取出,然后集中站在一起的拣货方式。 19、配送成本:配送过程中所支付的费用总和. 20、共同配送模式:多个企业联合组织实施的配送活动. 21、第三方配送模式:指交易双方把自己需要完成的配送业务委托给第三方来完成的一种配送运作模式。 二、解答题 1、仓库选址策略有哪些? 市场定位、制造定位、中间定位 2、仓储作用和功能有哪些? 作用:(1)搞好仓储活动是实现社会再生产过程顺利进行的必要条件 (2)搞好仓储活动是保持物资原有使用价值和合理使用物资的重要手段 (3)搞好仓储活动,是加快资金周转、节约流通费用、降低物流成本、提高经济效益的有效途径 (4)物资仓储活动是物资供销管理工作的重要组成部分 功能:储存、调节、保管、加工、集散、信息传感、现货交易功能 3.仓库的结构设计要考虑哪些因素?

二进制 各种转化

C语言中二进制十进制十六进制各是什么意思? 学按位要用到这些知识但又不懂! 匿名| 浏览1240 次问题未开放回答 推荐于2016-05-22 01:54:54 最佳答案 计算机中常用的数的进制主要有:二进制、八进制、十六进制,学习计算机要对其有所了解。2进制,用两个阿拉伯数字:0、1; 8进制,用八个阿拉伯数字:0、1、2、3、4、5、6、7; 10进制,用十个阿拉伯数字:0到9; 16进制就是逢16进1,但我们只有0~9这十个数字,所以我们用A,B,C,D,E,F这五个字母来分别表示10,11,12,13,14,15。字母不区分大小写。 以下简介各种进制之间的转换方法:

一、二进制转换十进制 例:二进制“1101100” 1101100 ←二进制数 6543210 ←排位方法 例如二进制换算十进制的算法: 1*26 + 1*25 + 0*24 + 1*23 + 1* 22 + 0*21 + 0*20 ↑↑ 说明:2代表进制,后面的数是次方(从右往左数,以0开始) =64+32+0+8+4+0+0 =108 二、二进制换算八进制 例:二进制的“10110111011” 换八进制时,从右到左,三位一组,不够补0,即成了: 010 110 111 011 然后每组中的3个数分别对应4、2、1的状态,然后将为状态为1的相加,如:010 = 2 110 = 4+2 = 6 111 = 4+2+1 = 7 011 = 2+1 = 3 结果为:2673

三、二进制转换十六进制 十六进制换二进制的方法也类似,只要每组4位,分别对应8、4、2、1就行了,如分解为:0101 1011 1011 运算为: 0101 = 4+1 = 5 1011 = 8+2+1 = 11(由于10为A,所以11即B) 1011 = 8+2+1 = 11(由于10为A,所以11即B) 结果为:5BB 四、二进制数转换为十进制数 二进制数第0位的权值是2的0次方,第1位的权值是2的1次方…… 所以,设有一个二进制数:0110 0100,转换为10进制为: 计算:0 * 20 + 0 * 21 + 1 * 22 + 0 * 23 + 0 * 24 + 1 * 25 + 1 * 26 + 0 * 27 = 100 五、八进制数转换为十进制数 八进制就是逢8进1。 八进制数采用0~7这八数来表达一个数。 八进制数第0位的权值为8的0次方,第1位权值为8的1次方,第2位权值为8的2次方…… 所以,设有一个八进制数:1507,转换为十进制为: 计算:7 * 80 + 0 * 81 + 5 * 82 + 1 * 83 = 839

数据结构实验报告 栈进制转换

数据结构试验报告栈的应用——进制转换程序

3.出栈程序 int Pop(Stack *s , int *e) { if(s->top == s->base) { return ERROR; } *e = * -- s->top; return OK; } 4.主函数与进制转化 void main() { int N; int a; int e; Stack s; InitStack(&s); Pop(&s , &e); Push(&s ,&e); InitStack(&s); printf("请输入十进制数:"); scanf("%d",&N); printf("要将N转化为几进制?"); scanf("%d",&a); while(N) { e=N%a; Push( &s , &e ); N = N / a ; } while(s.base!=s.top) { Pop(&s ,&e); printf("%d",e); } free(s.base); system("pause"); } 3.源程序 #include #include #include

#define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 #define OK 1 #define ERROR 0 #define OVERFLOW -1 #define TRUE 1 #define FALSE -1 typedefstruct{ int *base; int *top; intstacksize; }Stack; intInitStack(Stack *s) { s->base=(int *)malloc(STACK_INIT_SIZE * sizeof(int)); if(!s->base) { exit(OVERFLOW); } s->top=s->base; s->stacksize=STACK_INIT_SIZE; return OK; } int Push(Stack *s , int *e) { if(s->top - s->base >= STACK_INIT_SIZE) { s->base=(int *)realloc(s->base , (s->stacksize + STACKINCREMENT) * sizeof(int) ); if(!s->base) { exit(OVERFLOW); } s->top=s->base + STACKINCREMENT; } * s->top ++ = *e; return OK; } int Pop(Stack *s , int *e)

仓储与配送管理案例集

案例集 《仓储与配送管理》案例集 【案例一】:仓储的增值服务功能 作为仓储业来讲,除了经济利益和服务利益外,还必须提供其他的增值服务,以保持其竞争能力。这种情况对与配送中心、公共仓库和合同仓库的经营人以及私有仓储的经营人来说,是千真万确。仓库增值服务主要集中在包装或生产上。 最普通的增值服务于包装有关。在通常情况下,产品往往是以散装形式或无标签形式装运到仓库里来的。所以,这种存货基本上没有什么区别。一旦收到顾客的订单,配送中心的仓库管理就要按客户要求对产品进行定制和发放。如制造商把未贴标志的电池发送到仓库中,向仓库的作业人员提供了销售所需带有的商标牌号的包装材料。接到订货仓库作业人员按要求将标志图案贴到电池上,然后用定制的盒子将其包装上。所以即使该产品在仓库里存放时是没有区别的,但是零售商实际收到的是已经定制化了的产品和包装。由于支持个别零售商需求所需要的安全储备量较少,所以该配送中心可以减少其存货。与此同时,还可以相应的减少市场预测和配送计划的复杂性。 此外配送中心仓库可以通过优化包装来提高这种增值服务,以满足整个渠道的顾客需求。例如,仓库可以通过延伸包装和变换托盘来增值。这种做法可以使配送中心只处理一种统一的商品,与此同时,延期包装,以使包装需求专门化。另一个有关仓库增值的例子是在商品交付给零售商或顾客以前,解除保护性包装。在大型机械的情况下,这是一种有价值的服务,因为有时要零售商或顾客处理掉大量的包装是有困难的,因此解除或回收包装材料是提供的增值服务。 配送中心还可以通过改变包装特点来增值,诸如厂商将大片的防冻剂运到仓库,由配送中心对该商品进行瓶装,以满足各种牌号和包装尺寸的需要。这类延期包装是存货风险降到最低程度,减少了运输成本,并减少损坏(即相对于玻璃瓶包装的产品而言) 另一个增值服务是对诸如水果和蔬菜之类的产品进行温控。配送中心可以依赖储存温度,提前或延迟香蕉的成熟过程,这样产品可以按照市场的状况成熟。 提供增值的仓储服务,是配送中心经理对监督合同的履行承担特别的责任。尽管外部活动及其经营管理可以提高存货的有效性和作业的效率,但他们也要承担厂商控制范围外的责任。例如,仓库包装需要仓库经营人严格符合厂商内部所适应的质量标准。因此,仓库必须按相同的质量运行,并符合外部厂商的服务标准。 【思考题】: 仓储增值服务功能主要有哪些? 【案例二】:Fireside 轮胎公司的仓库选址决策 Fireside 轮胎公司是一家制造运动型多功能径向轮胎的公司,在汽车零件市场上销售产品,在全美国分销产品。Fireside 有三个轮胎生产工厂,分别位于宾夕法尼亚的阿伦镇、俄亥俄的托莱多和伊利诺伊的马科姆。一般情况下,Fireside 将轮胎从工厂运送到配送中心,但整车购买通常会直接从工厂运往顾客所在地。一个地区的所有运货都接受最低重量357 英担至400 英担的整车费率。 Fireside 的管理层非常关心配进中心的最经济位置,在亚特兰大的配送中心为北卡罗莱纳、南卡罗莱纳、佐治亚、佛罗里达、密西西比、阿拉巴马和东北田纳西组成的东北区域提供服务。但Fireside 的管理人员担心亚特兰大不是物流上最为理想的选择。

仓储配送中心管理规定

仓储配送中心管理规定 一、目的 为规范仓库进、出、存作业程序,加强对仓库的管理,通过制定仓库作业规定及奖惩制度,指导和规范仓库人员日常作业行为,并通过奖惩的措施起到激励和考核人员的作用。 二、范围 仓库工作人员 三、职责 仓库电脑员负责货品的出入库单据的录入,账目的调整及日常数据的维护仓库管理员负责货品装卸、存储、盘点等工作; 配送员负责货品的装卸及根据调货单按时按量送到门店或客户 加工对生鲜的包装及散称的分包 四、验收及保管 1 货品的验收入库仓库管理制度 1.1 货品到公司后库管员依据清单上所列的名称、数量进行核对、清点,经使用部门或请购人员及检验人员对质量检验合格后,方可入库。 1.2 对入库货品核对、清点后,库管员及时填写入库单,经使用人、货管科主管签字后,库管员、财务科各持一联做帐,采购人员持一联做请款报销凭证。 1.3 库管要严格把关,有以下情况时可拒绝验收或入库。 a)未经总经理或部门主管批准的采购。 b)与合同计划或请购单不相符的采购货品。 c)与要求不符合的采购货品。 1.4 因生产急需或其他原因不能形成入库的货品,库管员要到现场核对验收,并及时补填"入库单"。 2 货品保管仓库管理制度 2.1 货品入库后,需按不同类别、性能、特点和用途分类分区码放,做到"二齐、三清、四号定位"。 a)二齐:货品摆放整齐、库容干净整齐。

b)三清:材料清、数量清、规格标识清。 c)四号定位:按区、按排、按架、按位定位。 2.2 库管员对常用或每日有变动的货品要随时盘点,若发现误差须及时找出原因并更正 2.3 库存信息及时呈报。须对数量、文字、表格仔细核对,确保报表数据的准确性和可靠性。 3 货品的领发仓库管理制度 3.1 库管员凭领料人的领料单如实领发,若领料单上主管或总经理未签字、字据不清或被涂改的,库管员有权拒绝发放货品。 3.2 库管员根据进货时间必须遵守"先进先出"的仓库管理制度原则。 3.3 领料人员所需货品无库存,库管员应及时通知使用者,使用者按要求填写请购单,经总经理批准后交采购人员及时采购。 3.4 任何人不办理领料手续不得以任何名义从库内拿走货品,不得在货架或货位中乱翻乱动,库管员有权制止和纠正其行为。 3.5 以旧换新的货品一律交旧领新;领用的各种工具均要上工具卡,并由领用人和总经理签字。 4 货品退库仓库管理制度 4.1 由于生产计划更改引起领用的货品剩余时,应及时退库并办理退库手续。 4.2 废品货品退库,库管员根据"废品损失报告单"进行查验后,入库并做好记录和标识。 五、规定 1 仓储管理规定 1.1 货品收货及入库 1.1.1 需严格按照”仓库单据作业管理流程”中有关“收货确认单”的流程进行作业。 1.1.2 采购将“送货”给到仓库后,仓库人员需要将货物放到仓库内部,不允许放在仓库外,尤其不能隔夜放在仓库外,下班后必须将货物检查放在仓库内部。 1.1.3 收货时需要求采购人员给到“送货单”,没有时需要追查,直到拿到单据为止,并填写入库单。仓库人员负追查和保管单据的责任。 1.1.4 所有产品入库确认必须仓库人员和采购共同确认。新产品尤其需要

各种进制之间转换方法

各进制转换方法(转载)一、计算机中数的表示: 首先,要搞清楚下面3个概念 ?数码:表示数的符号 ? 基:数码的个数 ?权:每一位所具有的值

、各种进制的转换问题 1. 二、八、十六进制转换成十进制 2. 十进制转换成二、八、十六进制 3. 二进制、八进制的互相转换 4. 二进制、十六进制的互相转换 1、二、八、十六进制转换成十进制 方法:数码乘以相应权之和 例(HloJ-l/25+lx24+l/23+0/22+ h2:+h20 -(59)10 例(136)8=lx82+3x8l+6x8°=(94)10 例(1F2^)1S=1X163+15X16S +2\16] + 10/16° = (7978)10 2、十进制转换成二、八、十六进制 方法:连续除以基,直至商为0,从低到高记录余数

例把十进制数159转换成八进制数 8| 19 8辽 (159)IO =(237)8 例把十进制数59转换成二进制数 (59)IO =(111O11)2 2 余余余余余余 8 159

例把十进制数459转换成十六进制数 u | 1| C| B (459)io=(1CB)ib ' 3、二进制、八进制的互相转换 方法: *二进制转换成八进制:从右向左,每3位一组(不足3位左补0),转换成八进制*八进制转换成二进制:用3位二进制数代替每一位八进制数 例(1101001)2=(001,101,001)2=(151)8 例(246)8=(010,100,110)2=(10100110)2 4、二进制、十六进制的互相转换 方法: 二进制转换成十六进制:从右向左,每4位一组(不足4位左补0),转换成十六进制 *十六进制转换成二进制:用4位二进制数代替每一位十六进制数 例(11010101111101)2=(0011,0101,0111,1101)2=(357D)16 例(4B9E)16=(0100,1011,1001,1110)2=(100101110011110)2 三、各种进制数的运算 方法:逢满进具体计算与平时十进制的计算类似,以十六进制为例: 加法:

仓库、配送中心、物流中心的区分,你会分别吗

仓库、配送中心、物流中心的区分,你会分别吗? 仓库是保管和保养物品的场所的总称。过去,物品在仓库内储存的时间往往较长或很长,物品的品种也很少,所以仓库的主要责能是保菅好物品和保养好物品。但现代的仓库,物品在仓库内的储存时间,往往较短或很短,以降低资金积压;物品的品种也大大增多,即拣选物品的量、速度和配送点的数量都有几何级数的增长。由量变到质变,部分仓库名称转变为配送中心和物流中心。 近年来,社会上许多原仓储企业、运输企业,甚至小型托运企业、送水送奶企业等均翻牌改称物流中心或配送中心。这种现象,本身没有原则的不妥和不可,但给物流客户的搜索带来了不便,在学术交流上带耒一定的不便。因此,甚少应在学术交流上应有一个基本的共织。在GB/T 18354—2001 物流术语中,对仓库、物流中心和配送中心己有明确的定义。但不知何故,在该标准中仓库放在“5物流技术装备与设施术语”中定义,物流中心放在“3基本概念术语”中定义,配送中心放在“4 物流作业术语”中定义,这在概念上造成混乱。 简单的区分:仓库是保管和保养物品的场所的总称;配送中心是储存众多物品、且将储存周期较短的众多物品配送给众多零售店(如专卖店、连销店、超市等)或最终客户的场所;而物流中心是储存众多物品、且将储存周期稍长的众多物品送达配送中心的场所。 配送中心的特点是:其位置处于物流的下游;一般储存物品的品种较多、存储周期短;为使零售店或最终客户不设库或少设库以及不设车队,具有强大的多客户、多品种、多频次少量的拣选和配送功能。因为多客户、多品种才能实现保菅、运输作业的规模化、共同化,节约费用。配送中心一般采用“门到门”的汽车运输,其作业范围较小(20~300公里),为本地区的最终客户服务。有时,配送中心还有流通加工的业务,如钢材的定尺加工,食品由大的运输包装改为小的零售包装,饲料由单一改饲料为复合饲料等,服务的延伸和增值。 物流中心的特点是:位置处于物流的中游,是制造厂仓库与配送中心的中间坏节,一般离制造厂仓库与配送中心较远,为使运输经济性,采用大容量汽车或铁路运输和少批次大量的出入库方式。 需要说明,仓库、物流中心、配送中心都是自营或代客户保管和运输物品的场所,也可以说是过去各部、各级储运公司,要绝对地区分是较困难的,有时它们的业务有明显的交叉性;所谓的多客户、多品种、多频次少量的拣选或大容量汽车或铁路运输和少批次大量的出入库方式等,也是相对而言。仓库已逐步地被物流中心和配送中心所替代,除季节性生产明显的储备粮库、棉花库、果品库、冷芷海产品库以及军需储备库等,仍以保管保养为主。 因为三者都有保管和保养物品的功能以及其它相同的功能,只是程度、强弱的不同,此外物流中心和配送中心是由仓库发展、派生而成。因此,有时我们说仓库,也包括物流中心和配送中心、是三者的统称。

数据结构 栈十进制转八进制的算法详解(已测试过)

实验目的 建立栈实现十进制转八进制 实验内容 编程序并上机调试运行。 建立栈实现十进制转八进制 1.编写程序 //十进制转八进制 #include #include #include #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 typedef struct { int *base; int *top; int stacksize; }sqstack; int initstack (sqstack *s) {s->base=(int *)malloc(STACK_INIT_SIZE * sizeof(int)); if(!s->base) exit(0); s->top=s->base; s->stacksize =STACK_INIT_SIZE ; return 0; }//构造一个空栈s int push(sqstack *s,int e) { if((s->top-s->base)>=s->stacksize){ s->base=(int*)realloc(s->base,(s->stacksize + STACKINCREMENT )*sizeof(int)); if(!(s->base)) exit(1);

s->top=s->base+s->stacksize; s->stacksize+=STACKINCREMENT; } *s->top++=e; return 0; }//插入新的元素e为新的栈顶元素 int stackempty (sqstack *s) {if(s->top==s->base) return 1; else return 0; }//若栈s为空栈,则返回1,否则返回0 int pop (sqstack *s,int *e) {if(s->top==s->base) return 1; *e=*--s->top; return 0; }//若栈不为空,则删除s的栈顶元素,用e返回其值,返回OK,否则返回ERROR void conversion (int n) { sqstack s; int e; initstack(&s); printf("请输入一个十进制数:\n"); scanf("%d",&n); while (n){ push(&s,n%8); n=n/8; } printf("\n"); printf("该数的八进制数为:\n"); while(!stackempty(&s)){ pop(&s,&e); printf("%d",e); }

相关文档
最新文档