《STL源码剖析》(二)——空间配置器

2023-11-16

一、 为什么要有空间配置器:

1、小块内存带来的内存碎片问题

单从内存分配的角度来讲,由于频繁分配、释放小块内存容易在堆中造成外碎片(极端情况下:堆中空闲的总量满足一个要求,但是这些空闲的块都不连续,导致任何一个单独的空闲的块都无法满足请求)

2、小块内存频繁申请释放带来的性能问题:

(1)开辟空间的时候,分配器会找一块空闲给用户,找空闲块需要时间,尤其是在外碎片比较多的情况。如果分配器找不到,就要考虑假碎片现象(释放的小块内存没有合并),这时候就要将这些已经释放的空闲块合并,这也需要时间。
(2)malloc在开辟空间的时候,这些空间会带有一些附加的信息,这样也会造成空间的利用率降低,尤其频繁申请小块内存时。

为了解决上述问题,STL提出了内存池的概念,内存池最基本的思想就是一次向heap申请一块很大的内存(内存池),如果申请小块内存就直接到内存池中去要,有效解决上述问题。

二、 STL的空间配置器(SGI版)

STL的空间配置器分两级,一级空间配置器(__malloc_alloc_template)和二级空间配置器(__default_alloc_template)。在STL中,默认分配的内存大于128这季节,调用一级空间配置器申请内存;如果小于等于128字节则认为是小块内存,去内存池中申请内存。一级空间配置器比较简单,直接封装了malloc()和free()处理。

1、一级空间配置器在这里插入图片描述

(1)malloc()申请内存。
(2)malloc()失败后,调用oom_malloc():
a 如果客户端没有设置内存不足处理机制,则抛出bad_alloc。
b 设置了内存不足机制,调用内存处理机制,直到获取到一块足够内存。内存处理机制设置不当会存在死循环问题。

typedef void(*MALLOCALLOC)();           //将void (*)()   重命名成MALLOCALLOC
template<int inst>
class _MallocAllocTemplate
{
private:
       static void* _OomMalloc(size_t);       //malloc失败的时候调用的函数
       static MALLOCALLOC _MallocAllocOomHandler;         //函数指针,内存不足的时候的处理机制
public:
       static void* _Allocate(size_t n)                        //分配空间n个字节的空间
       {
              void *result=0;
              result = malloc(n);
              if (0 == result)                    //若果malloc失败,则就调OOM_malloc
                     _OomMalloc(n);
              return result;
       }
       static void _DeAllocate(void *p)                //释放这块空间
       {
              free(p);
       }
       static MALLOCALLOC _SetMallocHandler(MALLOCALLOC f)    //这是一个函数,参数是一个函数指针,返回值也是一个函数指针
       {
              MALLOCALLOC old = _MallocAllocOomHandler;
              _MallocAllocOomHandler = f;              //将内存分配失败的句柄设置为f(让它指向一个内存失败了,让系统去释放其他地方空间的函数)
              return old;
       }
};
template<int inst>
void(* _MallocAllocTemplate<inst>::_MallocAllocOomHandler)()=0;    //默认不设置内存不足处理机制
template<int inst>
void* _MallocAllocTemplate<inst>::_OomMalloc(size_t n)
{
       MALLOCALLOC _MyMallocHandler;     //定义一个函数指针
       void *result;               
       while (1)
       {
              _MyMallocHandler = _MallocAllocOomHandler;
              if (0 == _MyMallocHandler)                  //没有设置内存不足处理机制
                     throw std::bad_alloc();                  //则抛出异常
              (*_MyMallocHandler)();                 //调用内存不足处理的函数,申请释放其他地方的内存
              if (result = malloc(n))                //重新申请内存
                     break;
       }
       return result;                              //申请成功时,则返回这块内存的地址
}
typedef _MallocAllocTemplate<0> malloc_alloc;

2、二级空间配置器

二级空间配置器使用内存池+自由链表的形式避免了小块内存带来的碎片化,提高了分配效率,提高利用率。假设分配8个字节大小的空间,他会去内存池分配多个8个字节大小的内存块,多余的挂在自由链表上。下一次需要8个字节,就可以去自由链表上去取。如果回收8个字节,则直接挂在自由链表。
为了方便管理,二级空间配置器在分配的时候都是以8的倍数对齐。这样只需维护16个free_list就可以了,free_list的16个节点分别管理大小为8,…128字节大小的内存即可。
自由链表的结点类型:

union _Obj
{
       union _Obj* _M_free_list_link;
       char _M_client_data[1];    /* The client sees this.        */
};

内存池模型:
在这里插入图片描述
为了将自由链表的结点串起来,又不引入额外的指针,自由链表下面挂的最小的内存块是8个字节,足够存放一个地址,因此可以在这些内存块中存放其他内存块的地址,即将内存块连接起来。
二级空间配置器的类:

enum { _ALIGN = 8 };              //按照基准值8的倍数进行内存操作
enum { _MAXBYTES = 128 };        //自由链表中最大的块的大小是128
enum { _NFREELISTS = 16 };       //自由链表的长度,等于_MAXBYTES/_ALIGN
template <bool threads, int inst>  //非模板类型参数
class _DefaultAllocTemplate
{
       union _Obj                      //自由链表结点的类型
       {
              _Obj* _freeListLink;         //指向自由链表结点的指针
              char _clientData[1];          //this client sees
       };
private:
       static char* _startFree;             //内存池的头指针
       static char* _endFree;               //内存池的尾指针
       static size_t _heapSize;              //记录内存池已经向系统申请了多大的内存
       static _Obj* volatile _freeList[_NFREELISTS];    //自由链表
private:
       static size_t _GetFreeListIndex(size_t bytes)   //得到这个字节对应在自由链表中应取的位置
       {
              return (bytes +(size_t) _ALIGN - 1) / (size_t)_ALIGN - 1;     
       }
       static size_t _GetRoundUp(size_t bytes)        //对这个字节向上取成8的倍数
       {
              return (bytes + (size_t)_ALIGN - 1)&(~(_ALIGN-1));     //将n向上取成8的倍数
       }
       static void* _Refill(size_t n);          //在自由链表中申请内存,n表示要的内存的大小
       static char* _chunkAlloc(size_t size,int& nobjs);    //在内存池中申请内存nobjs个对象,每个对象size个大小
public:
       static void* Allocate(size_t n);      //n要大于0
       static void DeAllocate(void *p,size_t n);        //n要不等于0
};

假设申请n(<= 128)个字节:
1、将n上调至8的倍数,在自由链表的相应结点下面找,如果该结点下面挂有未使用的内存,摘下来返回这块空间的地址。否则调用refill()去内存池申请内存。
2、向内存池申请的时候多申请几个,STL默认一次申请nobjs=20个,将多余的挂在自由链表上,这样能够提高效率。
进入refill函数后,先调用chunk_alloc(n,nobjs)函数去内存池申请内存,如果成功,返回refill函数。
如果nobjs=1,表示内存池只够分配一个,这时返回这个地址,否则表示nobjs大于1,将多余的内存块挂到自由链表上。
3、进入chunk_alloc(n,nobjs)有三种情况:
3.1剩余空间足够,直接分配好返回。
3.2剩余空间够n,但不够n*nobjs,此时nobjs=剩余空间/n,返回。
3.3内存池剩余空间不够一个n,向heap申请内存,在申请前将内存池中的剩余内存挂到自由链表上,之后向heap申请。
3.3.1申请成功,调一次chunk——alloc()重新分配。
3.3.2不成功,去自由链表中查看是否有大于n的空间,有的话返还给内存池,调用chunk_alloc重新分配。
3.3.3没有的话,调用1级分配器,是否有内存不足处理机制。
在这里插入图片描述

enum { _ALIGN = 8 };              //按照基准值8的倍数进行内存操作
enum { _MAXBYTES = 128 };        //自由链表中最大的块的大小是128
enum { _NFREELISTS = 16 };       //自由链表的长度,等于_MAXBYTES/_ALIGN
template <bool threads, int inst>  //非模板类型参数
class _DefaultAllocTemplate
{
       union _Obj                      //自由链表结点的类型
       {
              _Obj* _freeListLink;         //指向自由链表结点的指针
              char _clientData[1];          //this client sees
       };
private:
       static char* _startFree;             //内存池的头指针
       static char* _endFree;               //内存池的尾指针
       static size_t _heapSize;              //记录内存池已经向系统申请了多大的内存
       static _Obj* volatile _freeList[_NFREELISTS];    //自由链表
private:
       static size_t _GetFreeListIndex(size_t bytes)   //得到这个字节对应在自由链表中应取的位置
       {
              return (bytes +(size_t) _ALIGN - 1) / (size_t)_ALIGN - 1;     
       }
       static size_t _GetRoundUp(size_t bytes)        //对这个字节向上取成8的倍数
       {
              return (bytes + (size_t)_ALIGN - 1)&(~(_ALIGN-1));     //将n向上取成8的倍数
       }
       static void* _Refill(size_t n);          //在自由链表中申请内存,n表示要的内存的大小
       static char* _chunkAlloc(size_t size,int& nobjs);    //在内存池中申请内存nobjs个对象,每个对象size个大小
public:
       static void* Allocate(size_t n);      //n要大于0
       static void DeAllocate(void *p,size_t n);        //n要不等于0
};
template<bool threads,int inst>
char* _DefaultAllocTemplate<threads,inst>::_startFree = 0;        //内存池的头指针
template<bool threads, int inst>
char* _DefaultAllocTemplate<threads, inst>::_endFree=0;           //内存池的尾指针
template<bool threads, int inst>
size_t _DefaultAllocTemplate<threads, inst>::_heapSize = 0;              //记录内存池已经向系统申请了多大的内存
template<bool threads, int inst>
typename _DefaultAllocTemplate<threads, inst>::_Obj* volatile      //前面加typename表示后面是个类型
_DefaultAllocTemplate<threads, inst>::_freeList[_NFREELISTS] = {0};    //自由链表
 
template<bool threads, int inst>
void* _DefaultAllocTemplate<threads, inst>::Allocate(size_t n)    //分配空间
{
       void *ret;
       //先判断要分配的空间大小是不是大于128个字节
       if (n>_MAXBYTES)      //大于_MAXBYTES个字节则认为是大块内存,直接调用一级空间配置器
       {
              ret = malloc_alloc::_Allocate(n);
       }
       else       //否则就去自由链表中找
       {
              _Obj* volatile *myFreeList = _freeList+_GetFreeListIndex(n);  //让myFreeList指向自由链表中n向上取8的整数倍
              _Obj* result = *myFreeList;
              if (result == 0)  //这个结点下面没有挂内存,则就要去内存池中申请
              {
                     ret = _Refill(_GetRoundUp(n));      //到内存池中申请
              }
              else            //已经在自由链表上找到了内存
              {
                     *myFreeList= result->_freeListLink;      //把第二块空间的地址放到自由链表上
                     ret = result;
              }
       }
       return ret;
}
template<bool threads, int inst>
void _DefaultAllocTemplate<threads, inst>::DeAllocate(void *p, size_t n)   //回收空间
{
       //先判断这个字节的大小
       if (n > _MAXBYTES)  //如果n大于自由链表中结点所能挂的最大内存块,则就直接调用一级指针的释放函数
       {
              malloc_alloc::_DeAllocate(p);
       }
       else        //将这块内存回收到自由链表中
       {
              _Obj* q = (_Obj*)p;
              _Obj* volatile *myFreeList = _freeList + _GetFreeListIndex(n);
              q->_freeListLink = *myFreeList;
              *myFreeList = q;
       }
}
 
template<bool threads,int inst>
void* _DefaultAllocTemplate<threads, inst>::_Refill(size_t n)     //n表示要申请的字节个数
{
       int nobjs = 20;           //向内存池申请的时候一次性申请20个
       char* chunk = _chunkAlloc(n,nobjs);    //因为现在链表中没有,所以要想内存池中申请,多余的再挂到自由链表下面
       if (1 == nobjs)          //只分配到了一个对象
       {
              return chunk;
       }
       _Obj* ret = (_Obj*)chunk;                  //将申请的第一个对象作为返回值
       _Obj* volatile *myFreeList = _freeList+ _GetFreeListIndex(n);
       *myFreeList =(_Obj*)(chunk+n);             //将第二个对象的地址放到自由链表中
       _Obj* cur= *myFreeList;
       _Obj* next=0;
       cur->_freeListLink = 0;
       for (int i = 2; i < nobjs; ++i)             //将剩下的块挂到自由链表上
       {
              next= (_Obj*)(chunk + n*i);
              cur->_freeListLink = next;
              cur = next;
       }
       cur->_freeListLink = 0;
       return ret;
}
template<bool threads, int inst>
char* _DefaultAllocTemplate<threads, inst>::_chunkAlloc(size_t size, int& nobjs)  //向系统中申请内存
{
       char* result = 0;
       size_t totalBytes = size*nobjs;        //总共请求的内存大小
       size_t leftBytes = _endFree - _startFree;      //内存池剩余的大小
       if (leftBytes>=totalBytes)     //如果剩余的大小大于等于申请的大小,则返回这个这内存
       {
              result = _startFree;
              _startFree += totalBytes;
              return result;
       }
       else if (leftBytes>size)         //如果剩余的内存足够分配一个size,
       {
              nobjs=(int)(leftBytes/size);
              result = _startFree;
              _startFree +=(nobjs*size);
              return result;
       }
       else            //内存池中的内存已经不够一个size了
       {
              size_t NewBytes = 2 * totalBytes+_GetRoundUp(_heapSize>>4);       //内存池要开辟的新的容量
              if (leftBytes >0)  //剩余的内存挂到自由链表上
              {
                     _Obj* volatile *myFreeList = _freeList + _GetFreeListIndex(leftBytes);
                     ((_Obj*)_startFree)->_freeListLink = *myFreeList;
                     *myFreeList = (_Obj*)_startFree;
              }
              
              //开辟新的内存
              _startFree = (char*)malloc(NewBytes);
              if (0 == _startFree)                   //如果开辟失败
              {
                     //如果开辟失败的话,则表明系统已经没有内存了,这时候我们就要到自由链表中找一块比n还大的内存块,如果还没有的话,那就掉一级空间配置器
                     for (size_t i = size; i <(size_t)_MAXBYTES;i+=(size_t)_ALIGN)
                     {
                           _Obj* volatile *myFreeList = _freeList + _GetFreeListIndex(i);
                           _Obj* p =*myFreeList;
                           if (NULL != p)       //在自由链表找到一块内存块
                           {
                                  _startFree =(char*)p;                  
                                  //将这个内存块摘下来给内存池
                                  *myFreeList = p->_freeListLink;
                                  _endFree = _startFree + i;
                                  return _chunkAlloc(size, nobjs);  //内存池开辟好的话,就再调一次chunk分配内存
                           }
                     }
                     //要是再找不到的话,就调一级空间配置器,其中有内存不足处理机制,要是还不行的话,他会自动抛出异常
                     _endFree = NULL;
                     _startFree=(char*)malloc_alloc::_Allocate(NewBytes);
              }      
              //开辟成功的,就更新heapSize(记录总共向系统申请了多少内存),,更新_endFree
              _heapSize += NewBytes;
              _endFree = _startFree + NewBytes;
              return _chunkAlloc(size, nobjs);             //内存池开辟好的话,就再调一次chunk分配内存
       }
}
 
typedef _DefaultAllocTemplate<0,0>  default_alloc;

三、空间配置器其他问题

1、空间配置器中的所有函数和变量都是静态的,所以程序结束后才会被释放。二级空间配置器中没有将申请的内存还给操作系统,知识将他们挂在自由链表,当程序结束时才会归还到操作系统。
2、由于没有将内存归还,所以会出现两种极端情况。
2.1不断开辟小块内存,最后整个heap挂在自由链表没有使用,再想要开辟一个大的内存失败。
2.2不断的开辟char,最后将整个heap全挂在自由链表第一个节点后面,再开辟一个6字节的内存,失败。
3、二级空间配置器造成内存碎片化问题,极端情况下一直申请char,浪费7/8的空间。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《STL源码剖析》(二)——空间配置器 的相关文章

  • 在 xaml 中编写嵌套类型时出现设计时错误

    我创建了一个用户控件 它接受枚举类型并将该枚举的值分配给该用户控件中的 ComboBox 控件 很简单 我在数据模板中使用此用户控件 当出现嵌套类型时 问题就来了 我使用这个符号来指定 EnumType x Type myNamespace
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 从经典 ASP 调用 .Net C# DLL 方法

    我正在开发一个经典的 asp 项目 该项目需要将字符串发送到 DLL DLL 会将其序列化并发送到 Zebra 热敏打印机 我已经构建了我的 DLL 并使用它注册了regasm其次是 代码库这使得 IIS 能够识别它 虽然我可以设置我的对象
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 结构体的内存大小不同?

    为什么第一种情况不是12 测试环境 最新版本的 gcc 和 clang 64 位 Linux struct desc int parts int nr sizeof desc Output 16 struct desc int parts
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • C# 动态/expando 对象的深度/嵌套/递归合并

    我需要在 C 中 合并 2 个动态对象 我在 stackexchange 上找到的所有内容仅涵盖非递归合并 但我正在寻找能够进行递归或深度合并的东西 非常类似于jQuery 的 extend obj1 obj2 http api jquer
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • C# 使用“?” if else 语句设置值这叫什么

    嘿 我刚刚看到以下声明 return name null name NA 我只是想知道这在 NET 中叫什么 是吗 代表即然后执行此操作 这是一个俗称的 条件运算符 三元运算符 http en wikipedia org wiki Tern
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐

  • Stem-and-Leaf Plot in R

    Data set faithful 272 2 Waiting time between eruptions and the duration of the eruption for the Old Faithful geyser gt d
  • flink源码阅读---Flink intervalJoin 使用和原理分析

    1 前言 Flink中基于DataStream的join 只能实现在同一个窗口的两个数据流进行join 但是在实际中常常会存在数据乱序或者延时的情况 导致两个流的数据进度不一致 就会出现数据跨窗口的情况 那么数据就无法在同一个窗口内join
  • 主成分分析PCA以及特征值和特征向量的意义

    定义 主成分分析 Principal Component Analysis PCA 是一种统计方法 通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量 转换后的这组变量叫主成分 PCA的思想是将n维特征映射到k维上 k
  • android TextUtils工具类的用法

    方法摘要 static CharSequence 返回一个字符序列 commaEllipsize CharSequence text TextPaint p float avail String oneMore String more Co
  • Windows小技巧7--Sublime Text 3使用总结

    Windows小技巧7 Sublime Text 3使用总结 Sublime Text 是一个代码编辑器 也是HTML和散文先进的文本编辑器 Sublime Text是由程序员Jon Skinner于2008年1月份所开发出来 它最初被设计
  • C++中函数返回引用,及问题

    目录 函数返回值 返回引用 C 基础知识 函数返回引用深度解析 关于函数调用返回引用错误并且每次调用不一致的分析与解决 将引用作为函数返回值的格式 好处和规则 实用经验 45 禁止函数返回局部变量的引用 1 返回的是一个引用类型 也就是返回
  • 整数溢出的漏洞危害和预防

    智能合约作为区块链2 0的代表技术 适应于区块链去中心化 分布式的特点 具有独立运行 不可篡改的优良特性 可用于实现包含金融工具在内的各类分布式应用 开发者可以自行定义交易逻辑并开发代码发布到链上 合约代码在矿工节点的虚拟机环境 如EVM
  • vue踩坑填坑(一):引入模块组件

    在webpack vue开发中 如果在一个vue文件中引入另外一个封装的模块组件的vue文件 则有以下两种方式 首先想要在以下代码中引入一个封装好的输入框组件input text vue
  • cf服务器维护会不会掉分,《cf》枪王排位长时间不打会不会掉分? 枪王排位扣分机制介绍...

    川北在线核心提示 原标题 cf 枪王排位长时间不打会不会掉分 枪王排位扣分机制介绍 CF枪王排位大师以上不打会掉分么 很多小伙伴都在问枪王排位长时间不打会不会掉分 为此牛游戏小编为大家带来cf枪王排位扣分机制介绍 一起来看看枪王排位长时间不
  • Basic Level 1014 福尔摩斯的约会 (20分)

    题目 大侦探福尔摩斯接到一张奇怪的字条 我们约会吧 3485djDkxh4hhGE 2984akDfkkkkggEdsb s hgsfdk d Hyscvnm 大侦探很快就明白了 字条上奇怪的乱码实际上就是约会的时间星期四 14 04 因为
  • JDK8 HashMap put() 方法源码分析

    文章目录 一 前置知识 红黑树定义 二 构造方法 HashMap HashMap int initialCapacity float loadFactor tableSizeFor int cap 计算hashmap初始容量 三 put 方
  • 入门级题解7. 整数反转

    给你一个 32 位的有符号整数 x 返回将 x 中的数字部分反转后的结果 如果反转后整数超过 32 位的有符号整数的范围 231 231 1 就返回 0 假设环境不允许存储 64 位整数 有符号或无符号 思路 反转 想到链表反转 又看到是整
  • android studio对数据库进行,Android Studio 学习(四) 数据库

    文件存储 写数据 String data Data ti save FileOutputStream out null BufferedWriter writer null try out openFileOutput data Conte
  • C++毕业设计基于QT实现的超市收银管理系统源代码+数据库

    C 毕业设计基于QT实现的超市收银管理系统源代码 数据库 编译使用 编译完成后 需要拷贝 file目录下的数据库 POP db文件到可执行程序目录下 登录界面 主界面 会员管理 完整代码下载地址 基于QT实现的超市收银管理系统源代码 数据库
  • ctf.show 通关秘籍

    文章目录 CTF show 1 web签到题 2 web2 3 web3 CTF show 1 web签到题 访问web签到题的地址 发现页面只有 where is flag 字样 使用Fn F12进入调试模式 或者页面空白处点击右键查看网
  • mysql默认的数据库和表_MySQL 自带4个默认数据库

    默认数据库分类 information schema performance schema mysql test informance schema 保存了MySQl服务所有数据库的信息 具体MySQL服务有多少个数据库 各个数据库有哪些表
  • Mrosoft visual c++6.0打开文件未响应,快速解决。【最新办法,初学者都会】

    1 下载filetool的vc6 0的辅助工具 下载地址 http download microsoft com download vc60ent s1 6 0 w9xnt4 en us filetool exe 快速下载filetool
  • 为SQL Server Always On可用性组配置域控制器和Active Directory

    In this series for SQL Server Always On availability groups we are covering end to end configurations for SQL Server 201
  • echarts横向个性化柱状图

    先看一下效果图 横向柱状图 顶部小圈是一个图片 下面我们就来看看如何实现 1 第一步 先把柱状图中需要插入的图片 转换成base64格式 百度搜一下 可以搜到在线工具直接转换 2 html中定义一个div 用来盛放柱状图 div style
  • 《STL源码剖析》(二)——空间配置器

    一 为什么要有空间配置器 1 小块内存带来的内存碎片问题 单从内存分配的角度来讲 由于频繁分配 释放小块内存容易在堆中造成外碎片 极端情况下 堆中空闲的总量满足一个要求 但是这些空闲的块都不连续 导致任何一个单独的空闲的块都无法满足请求 2