DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: steven7890
今日帖子: 8
在线用户: 17
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 glwang (glwang) ★☆☆☆☆ -
盒子活跃会员
2021/2/18 11:15:53
标题:
求指点:为啥无法删除Delphi动态数组中的指定元素? 浏览:361
加入我的收藏
楼主: 代码来自CSDN的帖子:https://bbs.csdn.net/topics/320093566

错误现象:实际内存中无法删除指定的动态数组元素;

编译器:Delphi 10.3.3

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Math;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure DynArrayDelete(var A; elSize: Longint; index, Count: Integer);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
type
  Ttemp = record
    A: Integer;
    b: string;
    c: Integer;
  end;
var
  temps: array of Ttemp;
  i: Integer;
begin
  setlength(temps, 10);
  for i := 0 to 9 do
  begin
    temps[i].A := i;
    temps[i].b := IntToStr(i);
    temps[i].c := i * 2;
  end;
  DynArrayDelete(temps, SizeOf(temps), 5, 1); // 实际上却并没有删除temps[5]
  for i := 0 to High(temps) do
  begin
    ShowMessageFmt('%d,%s,%d', [temps[i].A, temps[i].b, temps[i].c]);
  end;
  // ShowMessage(IntToStr(Length(temps)));
  // ShowMessageFmt('%d,%s,%d',[temps[2].a,temps[2].b,temps[2].c]);

end;

procedure TForm1.DynArrayDelete(var A; elSize: Longint; index, Count: Integer);
var
  len, MaxDelete: Integer;
  P: PLongint; // 4   个字节的长整形指针
begin
  P := PLongint(A); // 取的   A   的地址
  if P = nil then
    Exit;
  len := PLongint(PChar(P) - 4)^; // 变量的长度   ,偏移量   -4
  if index >= len then // 要删除的位置超出范围,退出
    Exit;
  MaxDelete := len - index; // 最多删除的数量
  Count := Min(Count, MaxDelete); // 取得一个较小值
  if Count = 0 then // 不要求删除
    Exit;
  Dec(len, Count); // 移动到要删除的位置
  MoveMemory(PChar(P) + index * elSize, PChar(P) + (index + Count) * elSize,
    (len - index) * elSize); // 移动内存
  Dec(P); // 移出   “数组长度”位置
  Dec(P); // 移出“引用计数”   位置
  // 重新再分配调整内存,len   新的长度.   Sizeof(Longint)   *   2   =   2*Dec(P)
  ReallocMem(P, len * elSize + SizeOf(Longint) * 2);
  Inc(P); // 指向数组长度
  P^ := len; // new   length
  Inc(P); // 指向数组元素,开始的位置
  PLongint(A) := P;
end;

end.
此帖子包含附件:glwang_2021218111553.rar 大小:54.5K
----------------------------------------------
作者:
男 keymark (keymark) ▲▲△△△ -
注册会员
2021/2/18 11:23:09
1楼: 为啥不抄 Tlist ?(虽然是固定长度)
多问一句 动态数组是DynArrayDelete 这么玩的吗??????????
中文看着眼晕,
我的个人理解是
把要删除后面的
复制到删除这一行开始
然后大小 setlength  -1 ?
大约这样
ReallocMem 会破坏动态数组的约定吗?
----------------------------------------------
播客
作者:
男 glwang (glwang) ★☆☆☆☆ -
盒子活跃会员
2021/2/18 11:31:21
2楼: 为啥这个DynArrayDelete(temps, SizeOf(temps), 5, 1); // 实际上却并没有删除temps[5]呢??
----------------------------------------------
作者:
男 wenyue0811 (wenyue0811) ★☆☆☆☆ -
普通会员
2021/2/18 12:37:28
3楼: 首先: 我只看了个大标题.没怎么看代码.
然后: 我就大着胆子回答这个问题了.

按此在新窗口浏览图片

用 TLIST<T> 或 TList, 不是更好吗...

当然如果你用的版本是没有泛型的话那除外. 
当然会让执行文件变大些.但对现在的电脑内存, 网速能有多大影响?...
当然.........

当然个人觉得还是 TLIST<T> 好用... ^-^
----------------------------------------------


美国国务卿蓬佩奥回答大学生提问时说,“我曾担任美国中央情报局(CIA)的局长。我们撒谎、我们欺骗、我们偷窃。我们还有一门课程专门来教这些。这才是美国不断探索进取的荣耀
作者:
男 keymark (keymark) ▲▲△△△ -
注册会员
2021/2/18 15:01:10
4楼: 调试了下
  len := PLongint(PChar(P) - 4)^; // 变量的长度   ,偏移量   -4
  if index >= len then // 要删除的位置超出范围,退出
    Exit;
在这exit了。。按此在新窗口浏览图片  len = 1 显然你取值错位了 
Unit1.pas.66: len := Length(A);
005FD44D 8B45F8          mov eax,[ebp-$08]
005FD450 8B00          mov eax,[eax]
005FD452 8945E4          mov [ebp-$1c],eax
005FD455 837DE400         cmp dword ptr [ebp-$1c],$00
005FD459 740B          jz $005fd466
005FD45B 8B45E4          mov eax,[ebp-$1c]
005FD45E 83E804          sub eax,$04
005FD461 8B00          mov eax,[eax]
005FD463 8945E4          mov [ebp-$1c],eax
005FD466 8B45E4          mov eax,[ebp-$1c]
005FD469 8945F0          mov [ebp-$10],eax 
Unit1.pas.67: len := PLongint(integer(P)-8)^; // 变量的长度   ,偏移量   -4 //试了下-8也不对
005FD46C 8B45E8          mov eax,[ebp-$18]
005FD46F 83E808          sub eax,$08
005FD472 8B00          mov eax,[eax]
005FD474 8945F0          mov [ebp-$10],eax
----------------------------------------------
播客
作者:
男 keymark (keymark) ▲▲△△△ -
注册会员
2021/2/18 15:34:10
5楼: Unit1.pas.47: i:=length(temps);
005FD2E5 8B45F8          mov eax,[ebp-$08]
ebp-08 = eax 数组地址 02D08768
005FD2E8 8945EC          mov [ebp-$14],eax
eax 给 ebp-14
005FD2EB 837DEC00         cmp dword ptr [ebp-$14],$00 判断数组nil否
ebp -14 =0?
005FD2EF 740B          jz $005fd2fc
005FD2F1 8B45EC          mov eax,[ebp-$14]  不是nil 继续
ebp-14 给回eax
005FD2F4 83E804          sub eax,$04          +4 得出长度指针
eax +4
005FD2F7 8B00          mov eax,[eax]          反转得出长度
反转指针得值
005FD2F9 8945EC          mov [ebp-$14],eax      长度给ebp-14
eax 给 ebp-14
005FD2FC 8B45EC          mov eax,[ebp-$14]       长度给eax  jz $005fd2fc   =0直接跳这里
005FD2FF 8945F4          mov [ebp-$0c],eax        长度给i
把长度 拉出来 给ebp-0c 也就是i?

如果错了请指教。

看到链接
https://www.cnblogs.com/findumars/p/5087555.html
----------------------------------------------
播客
作者:
男 keymark (keymark) ▲▲△△△ -
注册会员
2021/2/18 16:55:08
6楼: 好了不陪你玩了
  len := PLongint(Longint(P)-4)^; 这样能拿到长度
  CopyMemory(@temps[5],@temps[6],4*sizeof(Ttemp)); 这样能复制 6 到5 一共复制4个
最后一个咋办?
SetLength(temps,9); //大小减一
当然这样 不如直接操作大小来的快。(一堆sao操作)
 按此在新窗口浏览图片
https://www.cnblogs.com/del/archive/2010/04/12/1710070.html
https://www.cnblogs.com/del/archive/2009/11/11/1600876.html
还是万一好使

DynArrayDelete有几个致命得问题,  4*sizeof(Ttemp)!!!!!!
----------------------------------------------
播客
作者:
男 keymark (keymark) ▲▲△△△ -
注册会员
2021/2/18 20:44:26
7楼: procedure DynArraySetZero(var A;Count: Integer);{inline;}
var
  P: PINT_PTR;
  i: integer;
begin
  P := PINT_PTR(A);
//  i := PINT_PTR(NativeInt(p)-sizeof(NativeInt))^;
  Dec(P); //长度
  P^ := Count;
//  Dec(P);//引用计数
//  P^ := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
  type
  Ttemp = record
    A: Integer;
    b: string;
    c: Integer;
  end;
var
  temps: array of Ttemp;
  i: Integer;
  P: PINT_PTR;
begin
  setlength(temps, 10);
  for i := 0 to 9 do
  begin
    temps[i].A := i;
    temps[i].b := IntToStr(i);
    temps[i].c := i * 2;
  end;
  CopyMemory(@temps[5],@temps[6],4*sizeof(Ttemp));
  DynArraySetZero(temps,10-1);
  for i := 0 to High(temps) do
  begin
    ShowMessageFmt('%d,%s,%d', [temps[i].A, temps[i].b, temps[i].c]);
  end;
end;
按此在新窗口浏览图片 好吧这么帖子懒得看了。
----------------------------------------------
播客
作者:
男 dalas (dalas) ▲▲▲▲▲ -
普通会员
2021/2/18 22:57:10
8楼: @6楼,如果 temps 是字符串数组,要怎么搞?
----------------------------------------------
-
作者:
男 keymark (keymark) ▲▲△△△ -
注册会员
2021/2/18 23:00:49
9楼: 按此在新窗口浏览图片
那么你要测试下上面得方法会不会破坏字符串[X]引用计数。
如果破坏了 循环赋值(好像非常耗时想想都阔怕)吧。。。老老实实得
----------------------------------------------
播客
作者:
男 dalas (dalas) ▲▲▲▲▲ -
普通会员
2021/2/18 23:09:29
10楼: 我目前的实现方法是这样的,循环赋值,你6楼的方法用在数值型数组正好,字符串因为不定长,容易出问题,关键是后面SizeOf怎么统计大小。

function DelArrayItem(var Values:TArray<String>;Index:Integer):Boolean;
var
  iIndex:Integer;
begin
  Result:=False;
  if (Index<Low(Values)) then Exit;
  for iIndex := Index to High(Values)-1 do
    Values[iIndex]:=Values[iIndex+1];
  SetLength(Values, Length(Values)-1);
  Result:=True;
end;



测试代码:
var
  aa:TArray<String>;
  i: Integer;
  str:String;
begin
  Setlength(aa,3);
  aa[0]:='1';
  aa[1]:='22';
  aa[2]:='333';
  DelArrayItem(aa,1);
  str:='';
  for i := Low(aa) to High(aa) do
    str:=str+aa[i];
  ShowMessage(str);
----------------------------------------------
-
作者:
男 earthsbest (全能中间件) ▲▲▲△△ -
注册会员
2021/2/20 17:23:08
11楼:  var A:=[1,2,3,4];
 delete(A,1,2); //A 将变成 [1,4]
----------------------------------------------
Delphi4Linux Delphi三层/FireDAC 技术群:734515869 http://www.cnblogs.com/rtcmw
作者:
男 glwang (glwang) ★☆☆☆☆ -
盒子活跃会员
2021/2/22 10:11:41
12楼: 感谢各位大佬的帮助!
----------------------------------------------
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v2.1 版权所有 页面执行52.73438毫秒 RSS