静态数组, 在声明时就分配好内存了, 譬如:


var
  arr1: array[..] of Char;
  arr2: array[..] of Integer;
begin
  ShowMessageFmt('数组大小分别是: %d、%d', [SizeOf(arr1), SizeOf(arr2)]);
  {数组大小分别是: 512、1024}
end;

对静态数组指针, 虽然在声明之处并没有分配内存, 但这个指针应该分配多少内存是有定数的.

这种情况, 我们应该用 New 和 Dispose 来分配与释放内存. 譬如:


type
  TArr1 = array[..] of Char;
  TArr2 = array[..] of Integer;
var
  arr1: ^TArr1;
  arr2: ^TArr2;
begin
  New(arr1);
  New(arr2);   arr1^ := '万一的 Delphi 博客';
  ShowMessageFmt('%s%s', [arr1^[], arr1^[]]); {万一}
//  ShowMessageFmt('%s%s', [arr1[0], arr1[1]]); {这样也可以}   arr2[Low(arr2^)] := Low(Integer); {第一个元素赋最小值}
  arr2[High(arr2^)] := MaxInt;      {第一个元素赋最大值}
  ShowMessageFmt('%d, %d', [arr2[], arr2[]]); {-2147483648, 2147483647}   Dispose(arr1);
  Dispose(arr2);
end; //变通一下, 再做一遍这个例子:
type
  TArr1 = array[..] of Char;
  TArr2 = array[..] of Integer;
  PArr1 = ^TArr1;
  PArr2 = ^TArr2;
var
  arr1: PArr1;
  arr2: PArr2;
begin
  New(arr1);
  New(arr2);   arr1^ := '万一的 Delphi 博客';
  ShowMessageFmt('%s%s', [arr1[], arr1[]]);   arr2[Low(arr2^)] := Low(Integer);
  arr2[High(arr2^)] := MaxInt;
  ShowMessageFmt('%d, %d', [arr2[], arr2[]]); {-2147483648, 2147483647}   Dispose(arr1);
  Dispose(arr2);
end;

给已知大小的指针分配内存应该用 New, 上面的例子是关于静态数组指针的, 后面要提到的结构体(记录)的指针也是如此.

New 的本质也函数调用 GetMem, 但不需要我们指定大小了.

但这对动态数组就不合适了, 不过给动态数组分配内存 SetLength 应该足够了, 譬如:


var
  arr: array of Integer;
begin
  SetLength(arr, );   arr[] := Random();
  arr[] := Random();
  arr[] := Random();   ShowMessageFmt('%d,%d,%d', [arr[],arr[],arr[]]); {0,3,86}
end;

那怎么给动态数组的指针分配内存呢? 其实动态数组变量本身就是个指针, 就不要绕来绕去再给它弄指针了.

不过有一个理念还是满重要的, 那就是我们可以把一个无类型指针转换为动态数组类型, 譬如:


type
  TArr = array of Integer;
var
  p: Pointer;
begin
  GetMem(p, * SizeOf(Integer)); {分配能容纳 3 个 Integer 的空间}   {这和 3 个元素的 TArr 的大小是一样的, 但使用时需要进行类型转换}
  TArr(p)[] := Random();
  TArr(p)[] := Random();
  TArr(p)[] := Random();   ShowMessageFmt('%d,%d,%d', [TArr(p)[], TArr(p)[], TArr(p)[]]); {0,3,86}   FreeMem(p);
end;

这里用到了 GetMem 和 FreeMem, 对分配无类型指针这是比较常用的; 对其他类型的指针它可以, 但不见得是最好的方案, 譬如:


//获取窗口标题(显然不如用前面说过的 StrAlloc 更好)
var
  p: Pointer;
begin
  GetMem(p, );
  GetWindowText(Handle, p, );
  ShowMessage(PChar(p)); {Form1}
  FreeMem(p);
end;

应该提倡用 GetMemory 和 FreeMemory 代替 GetMem、FreeMem, 譬如:


var
  p: Pointer;
begin
  p := GetMemory();
  GetWindowText(Handle, p, );
  ShowMessage(PChar(p)); {Form1}
  FreeMemory(p);
end;

先总结下:
New 是给已知大小的指针分配内存;
GetMem 主要是给无类型指针分配内存;
尽量使用 GetMemory 来代替 GetMem.

还有个 AllocMem 和它们又有什么区别呢?

AllocMem 分配内存后会同时初始化(为空), GetMem 则不会, 先验证下:


var
  p1,p2: Pointer;
begin
  p1 := AllocMem();
  ShowMessage(PChar(p1)); {这里会显示为空}
  FreeMemory(p1);   p2 := GetMemory();
  ShowMessage(PChar(p2)); {这里会显示一些垃圾数据, 内容取决与在分配以前该地址的内容}
  FreeMemory(p2);
end;

关于 FreeMemory 与 FreeMem 的区别:
1、FreeMemory 会检查是否为 nil 再 FreeMem, 这有点类似: Free 与 Destroy;
2、FreeMem 还有个默认参数可以指定要释放的内存大小, 不指定就全部释放(没必要只释放一部分吧);
3、New 对应的 Dispose 也可以用 FreeMem 或 FreeMemory 代替.

尽量使用 FreeMemory 来释放 GetMem、GetMemory、AllocMem、ReallocMem、ReallocMemory 分配的内存.

ReallocMem、ReallocMemory 是在已分配的内存的基础上重新分配内存, 它俩差不多 ReallocMemory 比 ReallocMem 多一个 nil 判断, 尽量使用 ReallocMemory 吧. 譬如:


type
  TArr = array[..MaxListSize] of Char;
  PArr = ^TArr;
var
  arr: PArr;
  i: Integer;
begin
  arr := GetMemory();
  for i := to do arr[i] := Chr(+i);
  ShowMessage(PChar(arr)); {ABCDE}   arr := ReallocMemory(arr, );
  ShowMessage(PChar(arr)); {ABCDE}
  for i := to do arr[i] := Chr(+i);
  ShowMessage(PChar(arr)); {ABCDEFGHIJKLMNOPQRSTUVWXYZ}
end;

注意上面这个例子中 TArr 类型, 它被定义成一个足够大的数组; 这种数组留出了足够的可能性, 但一般不会全部用到.
我们一般只使用这种数组的指针, 否则一初始化将会内存不足而当机.
即便是使用其指针, 也不能用 New 一次行初始化; 应该用 GetMem、GetMemory、AllocMem、ReallocMem、ReallocMemory 等用多少申请多少.
需要注意的是, 重新分配内存也可能是越分越少; 如果越分越大应该可以保证以前数据的存在.
这在 VCL 中 TList 类用到的理念.

如果你在心里上接受不了那么大一个数组(其实没事, 一个指针才多大? 我们只使用其指针), 也可以这样:


type
  TArr = array[..] of Char;
  PArr = ^TArr;
var
  arr: PArr;
  i: Integer;
begin
  arr := GetMemory();
  for i := to do arr[i] := Chr(+i);
  ShowMessage(PChar(arr)); {ABCDE}   arr := ReallocMemory(arr, );
  ShowMessage(PChar(arr)); {ABCDE}
  for i := to do arr[i] := Chr(+i);
  ShowMessage(PChar(arr)); {ABCDEFGHIJKLMNOPQRSTUVWXYZ}
end;

这好像又让人费解, 只有一个元素的数组能干什么?
应该这样理解: 仅仅这一个元素就足够指示数据的起始点和数据元素的大小和规律了.

另外的 SysGetMem、SysFreeMem、SysAllocMem、SysReallocMem 四个函数, 应该是上面这些函数的底层实现, 在使用 Delphi 默认内存管理器的情况下, 我们还是不要直接使用它们.

Delphi 的内存操作函数(2): 给数组指针分配内存的更多相关文章

  1. Delphi 的内存操作函数(1): 给字符指针分配内存

    马上能想到的函数有: GetMem AllocMem ReallocMem FreeMem GetMemory ReallocMemory FreeMemory New Dispose NewStr ...

  2. Delphi 的内存操作函数(1): 给字符指针分配内存( 给字符指针(PChar、PWideChar、PAnsiChar)分配内存最佳的选择是StrAlloc。分配内存的时候会对字符串进行初始化)

    马上能想到的函数有: GetMem AllocMem ReallocMem FreeMem GetMemory ReallocMemory FreeMemory New Dispose NewStr ...

  3. Delphi中复制带有String的记录结构时不能使用Move之类的内存操作函数

    请看下面的代码: program TestRecord; {$APPTYPE CONSOLE} uses  SysUtils,  Math; type  TRecordA = record    Na ...

  4. C语言中内存操作函数

      一.malloc/calloc 名称: Malloc/calloc 功能: 动态内存分配函数 头文件: #include <stdlib.h> 函数原形: void *malloc(s ...

  5. c++ void,内存操作函数

    void的含义 void的字面意思是“无类型”, void * 则为“无类型指针”, void * 可以指向任何类型的数据 void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变 ...

  6. c#读写共享内存操作函数封装

    原文 c#读写共享内存操作函数封装 c#共享内存操作相对c++共享内存操作来说原理是一样,但是c#会显得有点复杂. 现把昨天封装的读写共享内存封装的函数记录下来,一方面希望给需要这块的有点帮助,另一方 ...

  7. 内存操作函数memmove,memcpy,memset

    通过字符串的学习,我们知道字符串操作函数的操作对象是字符串,并且它的结束标志是结束符\0,当然这个说的是不 受限制的字符串函数.然而当我们想要将一段内存的数据复制到另一块内存时,我们不能使用字符串操作 ...

  8. 【转】C内存操作函数

    一.malloc/calloc 名称: Malloc/calloc 功能:  动态内存分配函数 头文件: #include <stdlib.h> 函数原形: void *malloc(si ...

  9. 自己实现内存操作函数memset(),memcmp(),memcpy(),memmove()

    1.memset()内存设置函数(初始化) void *my_memset(void* dest, int c, size_t count) { assert(dest != NULL); char  ...

随机推荐

  1. I2C与EEPROM

    一.基本概念 RAM(Random Access Memory)的全名为随机存取记忆体,它相当于PC机上的移动存储,用来存储和保存数据的.它在任何时候都可以读写,RAM 通常是作为操作系统或其他正在运 ...

  2. LuoGu P4996 咕咕咕

    题目描述 小 F 是一个能鸽善鹉的同学,他经常把事情拖到最后一天才去做,导致他的某些日子总是非常匆忙. 比如,时间回溯到了 2018 年 11 月 3 日.小 F 望着自己的任务清单: 看 iG 夺冠 ...

  3. 适合前端学习JS的网站

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript

  4. yapi部署

    官方提供了两种安装方式,由于环境或者权限问题可能会遇到不少麻烦 最简单的安装方式: 第一种方式 npm install -g yapi-cli --registry https://registry. ...

  5. 分享一个学习的网站:每天会有大量AI相关的干货(论文分享,行业动态,相关竞赛经验分享等)http://www.deepsmart.ai/

    网址:http://www.deepsmart.ai/ 微信公众号如下:

  6. synchronized 是可重入锁吗?为什么?

    什么是可重入锁? 关于什么是可重入锁,我们先来看一段维基百科的定义. 若一个程序或子程序可以“在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(re ...

  7. vue-video-player集成videojs-contrib-hls实现.m3u8文件播放

    // 安装依赖 npm install vue-video-player --save npm install videojs-contrib-hls --save // 在main.js中全局引入 ...

  8. UOJ#276. 【清华集训2016】汽水 二分答案 点分治

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ276.html 题解 首先,读入的时候就将所有的 $w_i$ 减掉 $k$ . 于是我们要求的就是平均值最 ...

  9. P1525 关押罪犯 并查集

    题目描述 SS城现有两座监狱,一共关押着NN名罪犯,编号分别为1-N1−N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用“怨气值”(一个正整数值) ...

  10. Django——RESTful架构

    一.REST简述 来自维基百科的解释: 表现层状态转换(REST,英文:Representational State Transfer)是Roy Thomas Fielding博士于2000年在他的博 ...