首页 文章

如何在Delphi中做C参数数组指针算法

提问于
浏览
2

在C中,您可以发送允许在数组上进行指针算术的参数 . 我需要能够在Delphi 7项目中做到这一点 . 我尝试这样做,但接收程序咳嗽 . 如果数组指针递增,c ^ [0]不应该在新的增量位置?

第一个过程调用makect(),但首先通过递增指针将指针移动到数组中更高的内存位置 . 但是,第二个过程,当设置为数组指针位置0时,不喜欢它 . (当然可能还有其他错误,但我想知道我是否正确这样做) .

此处列出的类型是为了清晰

type
  Pflt = ^flt;
  flt = double;

  Pflt_arr = ^flt_arr;
  flt_arr = array of flt;

  Pint_arr = ^int_arr;
  int_arr = array of integer;

构造函数

constructor TRefT.Create(const length:integer);
begin
  len := length;
  SetLength(_ip, 2 + (1 shl trunc(ln(length / 4.0) / ln(2.0) + 0.5) shr 1) );
  _ip[0] := 0;
  SetLength(_w, length shr 1);
end;


procedure TRefT.CF(buff: pflt_arr);
begin
  rdft(len, 1, buff, @_ip, @_w);
end;

呼叫程序

procedure TRefT.rdft(n:integer; isgn:integer; a:Pflt_arr; ip:Pint_arr; w:Pflt_arr);
var nw, nc: integer;
xi: flt;
begin
 nw := ip^[0];
 nc := ip^[1];
 if n > (nc shl 2) then
 begin
  nc := n shr 2;
  inc(w, nw);       <--attempt at pointer arithmetic
  makect(nc, ip, w); <-- C++ version is makect(nc, ip, w + nw);
 end;
end;

接收程序(增加数组);

procedure TRefT.makect(nc:integer; ip:Pint_arr; c:Pflt_arr);
var j, nch: integer;
  delta: flt;
begin
ip^[1] := nc;
if nc > 1 then
 begin
    nch := nc shr 1;
    delta := arctan(1.0) / nch;
    c^[0] := cos(delta * nch);  <-- coughs here!
    c^[nch] := 0.5 * c^[0];
    for j := 1 to nch do
     begin
        c^[j] := 0.5 * cos(delta * j);
        c^[nc - j] := 0.5 * sin(delta * j);
     end;
 end;
end;

2 回答

  • 3

    您的代码不正确 . 你有一个额外的,错误的间接水平 . 你需要的是指向 Double 的静态数组的指针,而不是指向 Double 的动态数组的指针 .

    请记住,动态数组是作为指向数组第一个元素的指针实现的 . 因此,就间接而言,您的类型等同于指向标量的指针 .

    一种方法是声明这样的类型

    type
      Pflt_arr = ^Tflt_arr;
      Tflt_arr = array [0..0] of flt;
    
      Pint_arr = ^Tint_arr;
      Tint_arr = array [0..0] of Integer;
    

    并确保禁用此代码的更改检查 .

    这将允许你写:

    a^[i]
    

    a 的类型为 Pflt_array 时 .

    如果你写的话还有更多:

    inc(a, n);
    

    那么它会将地址 a 递增 n*sizeof(a^) ,这是 n*sizeof(Tflt_array) ,这是 n*sizeof(flt)*Length(a^)) ,这是 n*sizeof(flt) ,这正是你想要的 .

    当您将常量表达式作为索引提供时,这会中断 . 根据这一行:

    nc := ip^[1];
    

    这里编译器会反对 1 不在 0..0 范围内 . 所以你不能两种方式 .

    在这种情况下,您似乎需要破解 ip 的前两个元素 . 您可以这样做:

    type
      Phuge_int_arr = ^Thuge_int_arr; 
      Thuge_int_arr = array [0..(MaxInt div sizeof(Integer))-1] of Integer;
    

    然后写:

    nc := Phuge_int_arr(ip)^[1];
    

    感觉有点乱 .


    另一种方法是编写如下类型:

    type
      Pflt_arr = ^Tflt_arr;
      Tflt_arr = array [0..(MaxInt div sizeof(flt))-1] of flt;
    

    这适用于所有索引方案,并允许您启用范围检查 . 但它使指针递增更加困难 . 现在你必须写:

    inc(Pflt(a), n);
    

    总的来说,后一种方法可能是两种罪恶中较小的一种 .


    声明实际存储的代码仍应使用动态数组, SetLength 等 . 当需要 Pflt_arrayPint_array 强制转换动态数组时:

    Pflt_array(dyn_array)
    

    这是有效的,因为动态数组是作为指向数组第一个元素的指针实现的 .


    使用 0..0 变体,您的代码如下所示:

    type
      Pflt = ^flt;
      flt = Double;
    
      Pflt_arr = ^Tflt_arr;
      Tflt_arr = array [0..0] of flt;
    
      Pint_arr = ^Tint_arr;
      Tint_arr = array [0..0] of Integer;
    
      Phuge_int_arr = ^Thuge_int_arr; 
      Thuge_int_arr = array [0..(MaxInt div sizeof(Integer))-1] of Integer;
    
    ....
    
    constructor TRefT.Create(const length:integer);
    begin
      len := length;
      SetLength(_ip, 2 + (1 shl trunc(ln(length / 4.0) / ln(2.0) + 0.5) shr 1) );
      SetLength(_w, length shr 1);
    end;
    
    procedure TRefT.CF(buff: pflt_arr);
    begin
      rdft(len, 1, buff, Pint_arr(_ip), Pflt_arr(_w));
    end;
    
    procedure TRefT.rdft(n:integer; isgn:integer; a:Pflt_arr; ip:Pint_arr; w:Pflt_arr);
    var nw, nc: integer;
        xi: flt;
    begin
      nw := Phuge_int_arr(ip)^[0];
      nc := Phuge_int_arr(ip)^[1];
      if n > (nc shl 2) then
      begin
        nc := n shr 2;
        inc(w, nw);
        makect(nc, ip, w);
      end;
    end;
    
    procedure TRefT.makect(nc:integer; ip:Pint_arr; c:Pflt_arr);
    var j, nch: integer;
        delta: flt;
    begin
      Phuge_int_arr(ip)^[1] := nc;
      if nc > 1 then
      begin
        nch := nc shr 1;
        delta := arctan(1.0) / nch;
        c^[0] := cos(delta * nch);
        c^[nch] := 0.5 * c^[0];
        for j := 1 to nch do
        begin
          c^[j] := 0.5 * cos(delta * j);
          c^[nc - j] := 0.5 * sin(delta * j);
        end;
      end;
    end;
    

    或者使用 0..(MaxInt div sizeof(scalar))-1 的替代方案如下所示:

    type
      Pflt = ^flt;
      flt = Double;
    
      Pflt_arr = ^Tflt_arr;
      Tflt_arr = array [0..(MaxInt div sizeof(flt))-1] of flt;
    
      Pint_arr = ^Tint_arr;
      Tint_arr = array [0..(MaxInt div sizeof(Integer))-1] of Integer;
    
    ....
    
    constructor TRefT.Create(const length:integer);
    begin
      len := length;
      SetLength(_ip, 2 + (1 shl trunc(ln(length / 4.0) / ln(2.0) + 0.5) shr 1) );
      SetLength(_w, length shr 1);
    end;
    
    procedure TRefT.CF(buff: pflt_arr);
    begin
      rdft(len, 1, buff, Pint_arr(_ip), Pflt_arr(_w));
    end;
    
    procedure TRefT.rdft(n:integer; isgn:integer; a:Pflt_arr; ip:Pint_arr; w:Pflt_arr);
    var nw, nc: integer;
        xi: flt;
    begin
      nw := ip^[0];
      nc := ip^[1];
      if n > (nc shl 2) then
      begin
        nc := n shr 2;
        inc(Pflt(w), nw);
        makect(nc, ip, w);
      end;
    end;
    
    procedure TRefT.makect(nc:integer; ip:Pint_arr; c:Pflt_arr);
    var j, nch: integer;
        delta: flt;
    begin
      ip^[1] := nc;
      if nc > 1 then
      begin
        nch := nc shr 1;
        delta := arctan(1.0) / nch;
        c^[0] := cos(delta * nch);
        c^[nch] := 0.5 * c^[0];
        for j := 1 to nch do
        begin
          c^[j] := 0.5 * cos(delta * j);
          c^[nc - j] := 0.5 * sin(delta * j);
        end;
      end;
    end;
    

    随便挑选!


    为了清楚起见,您可以利用此机会在移植此代码时将 shl 2shr 2 操作更改为算术运算 .

    您可能不知道的选项根本就没有翻译 . 将原始.c文件编译为对象,并使用 $LINK 静态链接它们 .

    最后一个评论是,你很难接受这样一个旧版本的Delphi . 现代版本具有 $POINTERMATH 编译器选项 . 这允许C样式指针算术和指向标量变量的普通指针索引 . 这种移植任务的巨大好处 .

  • 1

    注意:这个答案不是试图回答手头的问题 . 大卫做到了这一点并提出了处理指针算术的方法 .

    我不知道你有多少代码与这里提供的代码类似 . 使用指针可能很麻烦,有时在类型转换时会导致简单的错误 .

    更直接的Delphi解决方案是使用动态数组,并使用开放数组声明方法 .

    您的示例的解决方案如下所示:

    Type
     TRefT = class
     private
       len : Integer;
       _ip : array of integer;
       _w  : array of double;
     public
       Constructor Create(const length : integer);
       procedure CF(const buff : array of double);  // Or var
       procedure rdft(       n    : integer;
                             isgn : integer;
                       const a    : array of double;  // Or var
                         var ip   : array of integer;
                         var w    : array of double);
       procedure makect(     nc : integer;
                             nw : integer; // c array index offset
                         var ip : array of integer;
                         var c  : array of double);
     end;
    

    constructor TRefT.Create(const length:integer);
    begin
      len := length;
      SetLength(_ip, 2 + (1 shl trunc(ln(length / 4.0) / ln(2.0) + 0.5) shr 1) );
      SetLength(_w, length shr 1);
    end;
    
    procedure TRefT.CF(const buff: array of double);
    begin
      rdft(len, 1, buff, _ip, _w);
    end;
    
    procedure TRefT.rdft(       n    : integer;
                                isgn : integer;
                          const a    : array of double;
                            var ip   : array of integer;
                            var w    : array of double);
    var
      nw, nc: integer;
    begin
      nw := ip[0];
      nc := ip[1];
      if n > (nc shl 2) then
      begin
        nc := n shr 2;
        makect(nc, nw, ip, w);
      end;
    end;
    
    procedure TRefT.makect(     nc : integer;
                                nw : integer;  // c array index offset
                            var ip : array of integer;
                            var c  : array of double);
    var
      j, nch: integer;
      delta: double;
    begin
      ip[1] := nc;
      if nc > 1 then
      begin
        nch := nc shr 1;
        delta := ArcTan(1.0) / nch;
        c[nw] := Cos(delta * nch);
        c[nch + nw] := 0.5 * c[nw];
        for j := 1 to nch do
        begin
          c[j + nw] := 0.5 * Cos(delta * j);
          c[nc - j + nw] := 0.5 * Sin(delta * j);
        end;
      end;
    end;
    

    如果您要翻译大型c库,我建议您遵循David的建议,否则这种更加方便/ Delphi的方式更容易使用 .

相关问题