SDL(01-10)
SDL中的函数需要先初始化SDL才能用 :
//Initialize SDL
if( SDL_Init( SDL_INIT_VIDEO ) < )
{
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
}
1. 这里SDL_INIT_VIDEO是SDL库中的flag, 因为教程里面只用到了有关于video的部分, 所以只初始化了这一部分.
2. 这里还要注意初始化失败SDL_Init()返回的是-1.
3. SDL_GetError()在之后的教程中很常用, 只要是SDL库中的函数未实现预期的结果, 几乎都可以用这个函数来获取错误信息. 光从这个接口来看的话, SDL库内部应该是用专门用一个指针维护了一块内存, 每次出现错误时, 更新内存中的字符串为最新的错误信息, 而GetError()的作用就是返回这个指针..
初始化之后创建窗口:
//Create window
window = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
if( window == NULL )
{
printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
}
1. 这儿window是SDL_Window*类型, Window不出意外应该是个结构体, 代表的就是程序窗口.
2. SDL_CreateWindow()接收6个参数, 第一个是窗口的标题; 接下来两个参数定义的是窗口左上角的位置, 如果输入0, 0 的话, 窗口左上角将和屏幕的左上角重合. 这里还要注意这里的坐标系中, 原点是左上角, x轴向右增长, y轴向下增长; 再下面两个是宽和高, 宽就是x轴方向的长度, 高就是y方向的长度. 最后一个是flag, SDL_WINDOW_SHOWN保证窗口一旦创建即可见.
3. 如果有错的话返回空指针, 之后会发现很多函数都是这样, 同样, 这里也用到了SDL_GetError, 其实只要是SDL库中的, 都是靠这个函数打印错误...
创建窗口之后, 就要创建窗口的surface:
screenSurface = SDL_GetWindowSurface( window );
1. 这个screenSurface是SDL_Surface*类型的, SDL_Surface我个人的理解是, 可以当成是一张画布.
2. 同时要知道这张画布比较特殊, 因为它是绑定在该window上的, 你所有对于其他surface的操作, 最终都要弄到这个surface上才有效果.
我们暂时忽略一些操作函数不讲, 先来看看最后那些函数.
//Update the surface
SDL_UpdateWindowSurface( window );
1. 这里一个很重要的概念在于, 你对window所绑定的那个surface做了改变之后, 并不会立即反映在屏幕上, 必须要使用SDL_UpdateWindowSurface(window)来更新该window他才能把你所画在画布上的显示到桌面上.
//Destroy window
SDL_DestroyWindow( window );
1. window其实是一种资源, 当你不使用的时候需要释放掉, 这就是释放窗口的方法.
2. 另外值得一提的是 : 之后你会看到, surface也是需要释放的, 该窗口绑定的那个surface已经被上面这个函数释放掉了, 所以我们不需要释放, 但是对于其他非绑定surface, 需要我们自己释放.
//Quit SDL subsystems
SDL_Quit();
1. 这里只需要记住, 通常来讲要求初始化的最后都要求显式退出.
接下来介绍一些操作函数 :
//Load splash image
gHelloWorld = SDL_LoadBMP( "02_getting_an_image_on_the_screen/hello_world.bmp" );
if( gHelloWorld == NULL )
{
printf( "Unable to load image %s! SDL Error: %s\n", "02_getting_an_image_on_the_screen/hello_world.bmp", SDL_GetError() );
success = false;
}
1. gHelloWorld也是SDL_Surface* 类型, 这里SDL_LoadBMP()函数接收一个const char * 的指针, 要求该指针指向的字符串表示图片的路径, 如果成功的话, 将返回一个已经加载好图片的SDL_Surface指针, 这里我们就是用这个gHelloWorld(之前初始化为NULL了)来保存这个指针.
2. 如果不是绝对路径的话, 这个路径的起点一般是你的可执行文件所在的目录, 当然vs中刻意修改过, 这个路径与 vcxproj 文件所在路径相同.
//Deallocate surface
SDL_FreeSurface( gHelloWorld );
gHelloWorld = NULL;
1. 就像我之前所说的, 除了那个window绑定的SDL_Surface指针会在window销毁时随之销毁, 其他的都需要我们手动销毁.
//Apply the image
SDL_BlitSurface( gHelloWorld, NULL, gScreenSurface, NULL );
1. SDL_BlitSurface()把这个已经加载好图片的SDL_Surface传输到窗口绑定的Surface上去.
2. 该函数的第一个参数表示源surface, 第三个参数表示目标surface, 当然都是指针而不是surface本身...(暂时不用考虑第二和第四个参数)
3. 就像之前所说的, 完成这一步之后还需要SDL_UpdateWindowSurface()才可以在屏幕上显示. 关于这一点, 是因为目前的大多数渲染系统都是双缓冲的, 显示在桌面的是前缓冲, 而你用SDL_BlitSurface传输的数据其实传输到了后缓冲上, 而这个SDL_UpdateWindowSurface()就是起到了交换缓冲的作用. 双缓冲的好处就在于你从屏幕上看不到图像的绘制过程, 使得图像看起来是在瞬间完成的(实际上是在后缓冲完成后被直接交换到屏幕上).
然后来介绍一下SDL中的事件 :
while( SDL_PollEvent( &e ) != )
{
//User requests quit
if( e.type == SDL_QUIT )
{
quit = true;
}
}
1. e是SDL_Event类型, 也就是事件, 比如你移动鼠标, 按下键盘都是事件, 每次操作都将产生SDL_Event类型的结构体存放在事件池中.
2. SDL_PollEvent(&e)接收一个SDL_Event的指针, 然后把最近发生的事件放到这个指针所指向的空间中, 这里就是放到e里面.
3. 这里的SDL_QUI是当用户点击窗口右上角的×的时候触发的事件的类型...
//User presses a key
else if( e.type == SDL_KEYDOWN )
{
//Select surfaces based on key press
switch( e.key.keysym.sym )
{
case SDLK_UP:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_UP ];
break; case SDLK_DOWN:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_DOWN ];
break; case SDLK_LEFT:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_LEFT ];
break; case SDLK_RIGHT:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_RIGHT ];
break; default:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_DEFAULT ];
break;
}
1. 这里可以看到另外一种事件类型SDL_KEYDOWN, 虽然教程上说只要按下键盘上的键就能触发, 但是我实践发现只有类似上, 下, 左, 右, esc, ctrl, alt 等等之类的功能键会触发...
2. e.key里面是一些关于这个键事件的信息, e.key.keysym是关于那个键被按下的信息, e.key.keysym.sym 是关于被按下的键的SDL Keycode的信息, 比如代码中给出了上下左右的Keycode.
我们上面对于图片的传输是直接按位进行传输, 效率比较低(当然加载一张图片看不出来), 这里预先改变图片的格式再来进行传输从而达到加速的效果 :
//Convert surface to screen format
optimizedSurface = SDL_ConvertSurface( loadedSurface, gScreenSurface->format, NULL );
if( optimizedSurface == NULL )
{
printf( "Unable to optimize image %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
} //Get rid of old loaded surface
SDL_FreeSurface( loadedSurface );
1. 要理解这种优化我们需要介绍一些背景知识 : 我们所加载的bmp(bitmap)格式的图片通常来讲是24bit的, 然后现代的显示器通常都不是24bit, 例如对于32bit的显示器来讲, 那么我们每次调用上面所提到的SDL_BlitSurface()函数的时候都需要进行一次格式转换, 所以更好的方式是一旦一张图片加载成功就直接先将它格式转换为显示器相符的格式.
2. 这里的SDL_ConvertSurface()就是这个作用, 他接受三个参数, 第一个是源Surface, 第二个是显示器的格式, 这里gScreenSurface和Window是绑定的, 同时在SDL_Surface的结构体内部正好有格式, 所以它代表的就是显示器格式, 第三个参数我也不懂.
3. 另外一点值得注意的是, 这个SDL_ConvertSurface()返回的是转换好格式的Surface的指针, 但这是一个新的Surface, 所以我们现在可以释放掉之前那个Surface的资源了.
图片传输还有最后一点, 那就是图片的缩放, 在SDL2中有专门的函数来实现这一目的 :
//Apply the image stretched
SDL_Rect stretchRect;
stretchRect.x = ;
stretchRect.y = ;
stretchRect.w = SCREEN_WIDTH;
stretchRect.h = SCREEN_HEIGHT;
SDL_BlitScaled( gStretchedSurface, NULL, gScreenSurface, &stretchRect );
1. 首先我们看到的是SDL_Rect类型, 如果按照我的理解可以把这个类型看作是一个长方形的框架, 其内部有四个属性, 其中x, y代表这个框架在屏幕上的x坐标和y坐标(当然这里的坐标系统和前面一模一样), 之后两个参数就是框架的宽和高(长方形框架), 之后我们会经常碰到这种类型.
2. SDL_BlitScaled()有四个参数, 第一个代表源surface也就是你加载了图片资源的那个Surface, 第三个是目的Surface, 最后一个参数是框架的指针. 接下来SDL_BlitScaled()完成的工作就是按照最后一个参数所指的框架的位置以及大小的格式来将源Surface中的资源缩放到目标Surface, 所以完成之后目标surface中的那张图位置和大小与框架相同, 内容不变.
到这里你知道你有没有觉得SDL支持的图片格式有点单一, 这里引出了SDL的拓展库SDL_image.(要到网上额外下载的) :
//Initialize PNG loading
int imgFlags = IMG_INIT_PNG;
if( !( IMG_Init( imgFlags ) & imgFlags ) )
{
printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
success = false;
}
1. 类似于SDL, SDL_image也需要加载, 上面是假设我们只需要PNG的部分, 然后加载函数IMG_Init将返回一个加载结果的flag, 这个flag中如何对于PNG的那一位为0, 则说明PNG加载失败.
2. 因为SDL_image并不在SDL的基本库中, 所以它的报错函数有所不同, 但是使用方法是想通的, IMG_GetError()(之前那个是SDL_GetError()).
3. 这里不知道为什么虽然只加载了png, 但是我实际发现jpg, gif(但是没有动图效果)也是可以用的.
//Load image at specified path
SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
1. 这就是SDL_image中加载图片的函数, 其实和SDL_LoadBMP()用法以及作用都是相同的, 只是扩展了可以加载的图片类型而已.
2. 另外完成这一步之后, 仍然可以使用SDL_ConvertSurface(loadSurface, gScreenSurface->format, NULL) 来预先改变图片格式来进行优化加速.
我们上面一直在用Surface, 但是SDL2提供了一套纹理渲染API, 也就是texture rendering, surface和texture有如下几点区别 (区别收集自网络):
1. 一般来说, surface的加载顺序是 :文件 -> 内存 -> 显示,而texture的加载顺序是:文件-> 内存 -> 显存 -> 显示.
2. 从上过程可以看出, surface都是存在系统内存中, 而texture存在显存中, 所以texture的加载比surface多了一步, 但也只有texture可以享受硬件加速的好处.
当然要使用texture就要用来到渲染器 :
gRenderer = SDL_CreateRenderer( gWindow, -, SDL_RENDERER_ACCELERATED );
1. 这个函数就是用来生成一个与窗口绑定的渲染器, 然后返回指向该渲染器的指针, 所以gRenderer是SDL_Renderer*类型.
2. 渲染器可以理解为画笔, 一般在SDL初始化完成创建好窗口之后进行初始化(也就是生成绑定窗口的surface的位置).
接下来是如何将一张加载好的图片渲染到texture上:
newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
if( newTexture == NULL )
{
printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
} //Get rid of old loaded surface
SDL_FreeSurface( loadedSurface );
1. 虽然说texture好处多多, 但是从上述过程可以看出, 如果要加载图片最开始还是要经过内存的, 而这里使用的就是先用一个临时的surface来加载图片.
2. 加载好之后将surface渲染为texture的方法就是调用SDL_CreateTextureFromSurface(gRenderer, loadedSurface)函数, 这个函数的第一个参数就是渲染器了, 第二个参数则是已经加载好图片的surface, 返回值是一个指向texture的指针.
3. 因为我们最好要显示直接靠texture就可以了, 所以临时的surface在texture生成之后就可以释放了, 同时也因为这一点, 我想应该是没必要使用SDL_ConvertSurface()改变图片格式来优化了.
//Free loaded image
SDL_DestroyTexture( gTexture );
gTexture = NULL; //Destroy window
SDL_DestroyRenderer( gRenderer );
SDL_DestroyWindow( gWindow );
gWindow = NULL;
gRenderer = NULL;
1. 当然, 在程序结束的时候, 要记住显式的释放texture和renderer(不同于绑定在窗口上的surface不用显示释放).
然后是texture的显示 :
//Initialize renderer color
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF ); //Clear screen
SDL_RenderClear( gRenderer );
1. 之后的教程在显示部分都会使用较新的renderer渲染texture的形式, 所以之前的surface搭配SDL_BlitSurface()的形式会比较少.
2. SDL_SetRenderDrawColor()其实就是设置画笔(gRenderer就是这里的画笔), 后面三个参数分别是RGBA.
3. SDL_RenderClear()使用画笔目前的颜色清屏, 在本例中的效果就是屏幕编程白色(但是现在还不会显示出来).
//Render texture to screen
SDL_RenderCopy( gRenderer, gTexture, NULL, NULL ); //Update screen
SDL_RenderPresent( gRenderer );
1. 清屏之后为了把Texture加载上去, 我们就要用到这个SDL_RenderCopy()的函数, 这个函数提供四个参数, 第一个是渲染器, 第二个是要加载的texture, 第三个不清楚, 第四个在后面会提到, 其实我们之前也用过, 如果你记得我之前提过的SDL_Rect, 在改变图片的大小那里使用到的, SDL_BlitScaled函数的最后一个参数, 其实就是用来提供渲染的位置以及大小的, 这里设置为NULL的效果就是让图片填满整个窗口.
2. 最后这个函数作用其实和SDL_UpdateWindowSurface()的作用相同, 只不过这个函数用于renderer, texture这套api, 我之前说SDL_RenderClear()显示不出来就是因为还没有调用该函数.
这套texture API 还提供了一些基本的图形绘制函数:
//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer ); //Render red filled quad
SDL_Rect fillRect = { SCREEN_WIDTH / , SCREEN_HEIGHT / , SCREEN_WIDTH / , SCREEN_HEIGHT / };
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0x00, 0x00, 0xFF );
SDL_RenderFillRect( gRenderer, &fillRect );
1. 前两行是先设置背景色, 之前以及提过这里就不再赘述.
2. 这里我们又用到了SDL_Rect, 里面的四个参数分别是x, y, 宽, 高, 主要定义图形的位置和大小, 之前也说过.
3. 这里又使用了SDL_SetRenderDrawColor()来设置颜色, 因为之前设置背景色的时候已经使用了白色, 如果这里不设置的话, 画出了也是白色的, 那就看不出来了.
4. SDL_RenderFillRect()参数很简单, 效果的话就是填充这个长方形里面为当前的画笔颜色. 记住这里是填充, 而不只是用该颜色画出边框.
//Render green outlined quad
SDL_Rect outlineRect = { SCREEN_WIDTH / , SCREEN_HEIGHT / , SCREEN_WIDTH * / , SCREEN_HEIGHT * / };
SDL_SetRenderDrawColor( gRenderer, 0x00, 0xFF, 0x00, 0xFF );
SDL_RenderDrawRect( gRenderer, &outlineRect );
1. 这里和上面唯一的区别就在于最后一个函数是SDL_RenderDrawRect(), 这个是画而不是填充, 效果是只画出边框, 里面还是背景色, 要特别遇上边那个函数区别开来.
//Draw blue horizontal line
SDL_SetRenderDrawColor( gRenderer, 0x00, 0x00, 0xFF, 0xFF );
SDL_RenderDrawLine( gRenderer, , SCREEN_HEIGHT / , SCREEN_WIDTH, SCREEN_HEIGHT / );
1. 这个函数的效果从名字就可以看出来, 就是画线.
2. 前两个参数给出了画笔的起点位置, 后两个参数是画笔终点的位置, 这条线会把这两个点连起来.
SDL_RenderDrawPoint( gRenderer, SCREEN_WIDTH / , i );
1. 除了画线当然还可以画点, 后两个参数是点的左边.
当然你不是任何时候都希望在整个窗口中上只显示一张图片, 所以下面引入了viewport的概念:
//Top left corner viewport
SDL_Rect topLeftViewport;
topLeftViewport.x = ;
topLeftViewport.y = ;
topLeftViewport.w = SCREEN_WIDTH / ;
topLeftViewport.h = SCREEN_HEIGHT / ;
SDL_RenderSetViewport( gRenderer, &topLeftViewport ); //Render texture to screen
SDL_RenderCopy( gRenderer, gTexture, NULL, NULL );
1. 前面几行换汤不换药, SDL_Rect而已.
2. SDL_RenderSetViewport()函数的效果简单来说就是把你的render的操作对象暂时变成你在SDL_Rect中设置的那一块区域, 使用后如果你在后面再使用SDL_RenderCopy(), 虽然最后一个参数是NULL, 但是他并不会默认全屏显示该图片, 而是按照你viewport中设置的的位置和大小进行显示.
最后是Color Keying, 翻译过来叫抠图, 意思就是把指定的颜色吸收掉, 露出图层的下一层的颜色, 比如你想在一个背景是蓝色的天空上弄一个太阳的图片, 但是无奈太阳图片中有黑色的背景, 为了让太阳自然地挂在蓝色的天空上, 使得只有太阳而没有黑色的背景, 就必须使用color Keying 技术.
//Color key image
SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, , 0xFF, 0xFF ) );
1. SDL_SetColorKey()函数有三个参数, 第一个表示加载有图片的源surface(所以surface虽然不直接在窗口显示上使用, 但是在该库的使用过程中还是挺实用的), 第二个表示我们是否激活抠图(渣英语, 这里不太理解), 最后一个是我们需要抠掉的像素点.
2. 要想创造出我们需要的扣掉的像素点可以使用 SDL_MapRGB(), 该函数的第一个参数是这个这些像素点所处的格式, 因为图片加载在loadedSurface中, 所以自然是由loadedSurface来提供, 后面三个组成RGB, 这里代表的是青色, 那个图中所有青色的部分都会变得透明.(所以其实这里假设的是背景是青色的)
3. 函数产生的更改是直接在loadedSurface上进行的, 所以不会产生额外的Surface, 只需要调用SDL_CreateTextureFromSurface()来把这个surface渲染成texture就好.
最后要提醒的是如果真的要在蓝天上挂一个太阳, 那么应该先用SDL_RenderCopy(gRenderer, texture, NULL, &renderQuad); 渲染蓝天, 再渲染太阳, 如果顺序反了那么太阳就会被蓝天盖住, 也就显示不出来了.
SDL(01-10)的更多相关文章
- Cheatsheet: 2016 10.01 ~ 10.31
Docker Introduction to Docker Monitoring Database MongoDB: The Good, The Bad, and The Ugly Web 4 Key ...
- Cheatsheet: 2015 10.01 ~ 10.31
.NET Publishing your ASP.NET App to Linux in 5 minutes with Docker Integrating AngularJS with ASP.NE ...
- Cheatsheet: 2013 10.01 ~ 10.08
Other 20 Tips for becoming a better programmer Top 10 Movies for Programmers .NET RaptorDB - The Key ...
- @雅礼集训01/10 - T1@ matrix
目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个矩阵.求它的所有子矩阵中本质不同的行的个数之和. inp ...
- Cheatsheet: 2014 10.01 ~ 10.30
.NET ASP.NET Web Api: Unwrapping HTTP Error Results and Model State Dictionaries Client-Side HTTP 20 ...
- 2016/01/10 C++ Primer 小记 —— 命令行编译环境配置
OK!第一篇博文!自贺一下! 今日看了此书的前几页.嗯,说得挺全,基础易懂. 之前学过c++,但没用过命令行编译. 本人用的VS里的编译器,文件名是cl.exe,在VC目录下. 虽然有了编译器,但并不 ...
- A.01.10—模块的输出—PWM高端输出
PWM高端输出比低端输出用得多,如上次提到的卤素灯的控制均是采用高端输出的. PWM高端输出与PWM低端输出的差异就像固态高端输出与固态低端输出的差异类似,从线路失效后对用户的影响来看:高端输出为控制 ...
- 2019.01.10 bzoj1095: [ZJOI2007]Hide 捉迷藏(动态点分治)
传送门 蒟蒻真正意义上做的第一道动态点分治! 题意:给一棵最开始所有点都是黑点的树,支持把点的颜色变成从黑/白色变成白/黑色,问当前状态树上两个最远黑点的距离. 思路: 首先考虑不带修改一次点分治怎么 ...
- 批量创建10个用户stu01-stu10
1.批量创建10个用户stu01-stu10,并且设置随机8位密码,要求不能用shell循环(例如:for,while等),只能用命令及管道实现. ##方法1: [root@server tmp]# ...
- 洛谷P1141 01迷宫
题目描述 有一个仅由数字0与1组成的n×n格迷宫.若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上. 你的任务是:对于给定的迷宫, ...
随机推荐
- 嵌入式平台下的ldd
x86平台有ldd可以很方便的查看对库的依赖关系,但在嵌入式linux环境中没有这个命令,替而代之是 CC=$(CROSS_COMPILE)gcc LDD=$(CROSS_COMPILE)readel ...
- POJ 3252 Round Numbers(数位dp&记忆化搜索)
题目链接:[kuangbin带你飞]专题十五 数位DP E - Round Numbers 题意 给定区间.求转化为二进制后当中0比1多或相等的数字的个数. 思路 将数字转化为二进制进行数位dp,由于 ...
- hdu 1558 Segment set (并查集)
Segment set Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- [Angular] Organizing Your Exports with Barrels
From: import {LoadUserThreadsEffectService} from "./store/effects/load-user-threads.service&quo ...
- 【codeforces 750C】New Year and Rating(做法2)
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- WPF应用最小化至系统托盘运行
原文:WPF应用最小化至系统托盘运行 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/lordwish/article/details/5178889 ...
- Android学习Scroller(三)——控件平移划过屏幕 (Scroller简单使用)
MainActivity例如以下: package cc.cn; import android.os.Bundle; import android.view.View; import android. ...
- NOIP模拟 乘积 - 状压dp + 分组背包
题目大意: 给出n和k,求从小于等于n的数中取出不超过k个,其乘积是无平方因子数的方案数.无平方因子数:不能被质数的平方整除. 题目分析: 10(枚举\(n\le8\)),40(简单状压\(n\le1 ...
- SQLite的查询优化
SQLite是个典型的嵌入式DBMS,它有很多优点,它是轻量级的,在编译之后很小,其中一个原因就是在查询优化方面比较简单,它只是运用索引机制来进行优化的,经过对SQLite的查询优化的分析以及对源代码 ...
- 寄存器,cache、伙伴系统、内存碎片、段式页式存储管理
cache.伙伴系统.内存碎片.段式页式存储管理 目录 分层的存储管理 cache 局部性原理 置换算法 写回策略 linux环境下的cache 连续内存分配与内存碎片 内部碎片与外部碎片 动态分区分 ...