Как сделать арифметику указателя массива параметров C ++ в Delphi

В 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

Решение

Ваш код неверен. У вас есть дополнительный, ошибочный уровень косвенности. Здесь вам нужны указатели на статический массив 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 и индексировать обычный указатель на скалярные переменные. Огромное благо для таких задач портирования.

3

Другие решения

Примечание: этот ответ не является попыткой ответить на поставленный вопрос. Дэвид сделал это и представил способы обработки арифметики с указателями.

Я не знаю, сколько у вас кода похоже на то, что представлено здесь. Работа с указателями может быть громоздкой и иногда приводит к простым ошибкам при типизации.

Более простым решением 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;

Если у вас есть большие библиотеки с ++ для перевода, я бы посоветовал следовать рекомендациям Дэвида, в противном случае с этим более похожим на паскаль / Delphi способом проще работать.

1

По вопросам рекламы [email protected]