【问题标题】:How to do C++ parameter array pointer arithmetic in Delphi如何在Delphi中进行C++参数数组指针运算
【发布时间】:2014-02-17 04:34:43
【问题描述】:

在 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;

【问题讨论】:

  • 我已经删除了我之前的答案,因为它不再相关。但是,您需要显示更多代码。 IE。数组w 在传递到rdft 之前是如何创建的。
  • PS:你想要多个flt_arr 数组,还是只传递一个flt_arr 到rdft?

标签: c++ arrays delphi pointers delphi-7


【解决方案1】:

您的代码不正确。你有一个额外的、错误的间接级别。您需要的是指向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_array 或 Pint_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 2 和 shr 2 运算更改为算术运算。

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

最后一条评论是,很遗憾你被这么旧版本的 Delphi 困住了。现代版本具有$POINTERMATH 编译器选项。这允许 C 风格的指针算术和对标量变量的普通指针的索引。这样的移植任务是一个巨大的福音。

【讨论】:

  • 好的,我明白你在说什么了。我改变了我的类型(和构造函数)。范围检查默认关闭,但我在编译时遇到子范围边界错误。添加 {$R-} 似乎没有什么不同。我应该做些什么来关闭它吗?
  • @Michelle Gah,子范围界限。那时你有一个像这样的常量表达式:nc := ip^[1],编译器说不,因为它知道1 越界。给我一分钟再更新。
  • 我宁愿使用[0..SomethingSuitablyBig] 作为数组边界,而不是禁用一段代码的范围检查。 SomethingSuitablyBig 取决于具体情况,可以上升到 MaxInt div SizeOf(element) - 1,但通常像 65535 这样的东西在大多数情况下都可以。
  • @Rudy 这取决于。如果你想在指针上使用inc,0..0 会更简单。 0..65535 如果您出于明显的原因离开范围检查,那将毫无用处。
  • 如果这个问题只是关于如何修复数组指针运算,为什么不直接做makect(nc, ip, @w^[nw]); 并跳过指针运算。
【解决方案2】:

注意:此答案并非试图回答手头的问题。 David 做到了,并提出了处理指针算术的方法。

我不知道您有多少与此处介绍的代码相似的代码。使用指针可能很麻烦,有时在类型转换时会导致简单的错误。

更直接的 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 的建议,否则这种更像 pascal/Delphi 的方式更容易使用。

【讨论】:

  • 我大体上同意,但是看看所有需要的补充!我正在讨论看看这是否不能在更新版本的 Delphi 中完成,以便可以使用 $POINTERMATH。不过,我提高了您的回答,因为您的贡献非常有帮助。
  • @Michelle:您选择哪种解决方案取决于您是否可以控制代码,以及您是否只能与 Delphi 或其他语言交互。一般推荐使用假数组法。但前提是 double * 真的指向一个数组(或为零)。仅对于 Delphi 和 C++Builder,上面显示的纯 Delphi 方法要容易得多。
猜你喜欢
  • 2023-03-12
  • 2010-11-26
  • 2013-08-24
  • 2012-05-05
  • 2011-03-30
  • 2014-04-26
  • 1970-01-01
  • 2013-08-05
  • 1970-01-01
相关资源
最近更新 更多