Avatar billede linuxchristian Nybegynder
02. november 2005 - 17:49 Der er 6 kommentarer

Underlig fejl med dynamiske arrays i delphi

Jeg får fejlen "Invalid Pointer operation" nå jeg køre min kode. Programmer analysere en string og kan finde ud af udtryk så som 3+4+8*6, men den kan dog endnu ikke finde ud af /.

Nå jeg køre koden med 3+5 kan den godt finde ud af det. Hvis jeg skriver 3+5+8 kan den ikke, anden gang den køre koden kommer den med fejlen.
Hvad kan der være galt? Det er jo den samme kode programmet køre.

---------- Kode --------------
program Project3;

{$APPTYPE CONSOLE}
uses
  Stack, SysUtils;

var
sExpression: String;
iNumberOfNumbers, iRunTimes: Integer;
NumberStack: TNumStack;
OpStack: TOpStack;

const
        Operatorer= ['*', '/', '+', '-'];
        Tal=['0','1','2','3','4','5','6','7','8','9'];

{----------------------------------------
  Begyndelsen på CheckPriority Funktionen

  Funktion:
  Denne funktion modtager to char's, derefter
  undersøger den hvad for et af de to der har
  højest prioritet (* før +).
  Hvis den første operator er højest retunere
  funktionen true. Hvis den anden operator er højest
  eller hvis begge operatore har lige stor prioritet
  returner den false.

  Kaldes fra:
  OpHandle Funktionen
  ---------------------------------------}
function CheckPriority(var cPriorityCheck1, cPriorityCheck2: Char): boolean;
var
iPriority1, iPriority2: Integer;
bCheckPriority: boolean;
begin
bCheckPriority:=false;
// IF * OR / = 2 ELSE 1
if (cPriorityCheck1='*') OR (cPriorityCheck1='/') then iPriority1:=2 else iPriority1:=1;
if (cPriorityCheck2='*') OR (cPriorityCheck2='/') then iPriority2:=2 else iPriority2:=1;

// If c>d return true
if (iPriority1>iPriority2) then bCheckPriority:=true;
// If c<=d return false
if (iPriority2>=iPriority1) then bCheckPriority:=false;
Result:=bCheckPriority;
end;

{----------------------------------------
  Begyndelsen på OpHandle Funktionen

  Funktion:
  OpHandel funktionen kaldes fra main og fortæller
  case udtrykket om den skal udregne nu eller vente.
  Funktionen styre også operator stacken, ved at Pop'e
  op Push'e operatore ind i den.

  Kaldes fra:
  Main
  ---------------------------------------}
function OpHandle(sOpHandel: string): boolean;
var
cTempPeekValue : Char;
begin
if (OpStack.Empty) then
begin
  OpStack.Push(sExpression[iRunTimes]);
  Result:=False;
end
else
  begin
  cTempPeekValue:=OpStack.Peek;
    if CheckPriority(sExpression[iRunTimes], cTempPeekValue) then // Hvis sExpression[iRunTimes] har højere prioritet end cTempPeekValue.
    begin
      opstack.Push(sExpression[iRunTimes]); // Gem Operatoren væk på OpStacken til en anden gang.
      result:=False;
    end
    else
    begin // Lavere prioritet, udregn nu
      Result:=True;
    end;
  end;
end;

{----------------------------------------
  Begyndelsen på LargeNumbers Procedure

  Funktion:
  Hvis programmet møder tal med flere cifre
  gemmer den dem i første omgang bare som enkelte
  tal i NumberStacken.
  Funktionen kaldes når programmet møder en operator
  i strengen og hvis iNumberOfNumbers er større end 1.
  (Hvis iNumberOfNumbers er større end 1 har programmet
  mødt mere end et cifre siden sidste operator)

  Kaldes fra:
  Main (Case Strukturen)
  ---------------------------------------}
Procedure LargeNumbers;
var
  a, b, c : String;
  i : integer;
begin
  i:=0;
  while i<=(iNumberOfNumbers-2)  do
    begin
      a:=IntToStr(NumberStack.Pop);
      b:=IntToStr(NumberStack.Pop);
      c:=a+b;
      NumberStack.Push(StrToInt(c));
      i:=i+1;
    end;
end;

{----------------------------------------
  Begyndelsen på Calculate Procedure

  Funktion:

  Kaldes fra:
  Main (While-loop) og Slutning
  ---------------------------------------}
procedure Calculate;
begin
  Write('1. Length of Number array: ');
  WriteLn(NumberStack.MyLength);
  Write('1. Length of Op array: ');
  WriteLn(OpStack.MyLength);
  case OpStack.Pop of
      '*': begin
          NumberStack.push(NumberStack.Pop * NumberStack.Pop);
          end;
      '/': begin
          NumberStack.push(NumberStack.Pop div NumberStack.Pop);
          end;
      '-': begin
          NumberStack.push(NumberStack.Pop - NumberStack.Pop);
          end;
      '+': begin
          NumberStack.push(NumberStack.Pop + NumberStack.Pop);
          end;
  end;
  WriteLn(NumberStack.Peek);
  if (iRunTimes>0) then
  begin
    OpStack.Push(sExpression[iRunTimes]);
  end;
end;

{----------------------------------------
  Begyndelse på Main

  Funktion:
  Modtager udtrykket.
  Analysere det.
  Deler det op i operatore og tal.
  Foretager også nogle udregninger.
  ---------------------------------------}
begin

// Initialisere de to stakke
NumberStack:=TNumStack.Create;
OpStack:=TOpStack.Create;

// Modtager Udtrykker
Write('Expression:');
ReadLn(sExpression);

iRunTimes:=length(sExpression);

while iRunTimes>=1 do // Starter bagfra i ligningen og køre fremad.
begin
  if sExpression[iRunTimes] in Operatorer then
    begin
        if (iNumberOfNumbers>1) then // Hvis der har været flere cifret tal, så læg det tal sammen.
          begin
            LargeNumbers;
          end;
        iNumberOfNumbers:=0; // Koden har nu mødt en operator, derfor nul stilles iNumberOfNumbers.

        if (OpHandle(sExpression[iRunTimes]) = false) then  // Hvis den værdi programmet møder i sExpression[iRunTimes] er af højere prioritet end den operator der ligger øverst på OpStacken så returner OpHandel false, og værdien bliver smidt ind i toppen af OpStacken.
          begin
            // Operatoren bliver lagt ind i OpStacken i OpHandel funktionen.
          end
        else // Hvis programmet møder en operator der har lavere eller samme prioritet end den operator der ligger på OpStacken, så henter den de to øverste tal fra NumberStacken og bruger operatoren op dem.
          begin
            Calculate;
          end;
      iRunTimes:=iRunTimes-1; //Programmet er nået en længere ind i udtrykket
    end // Slut på if-sætning
  else begin
    if sExpression[iRunTimes] in Tal then
      begin
        NumberStack.Push(StrToInt(sExpression[iRunTimes]));
        iNumberOfNumbers:=iNumberOfNumbers+1;
        iRunTimes:=iRunTimes-1;
      end;
  end; // Slut på Else
end; // Slut på While-Loop

WriteLn(NumberStack.Peek);
WriteLn(OpStack.Peek);
Calculate;
WriteLn(NumberStack.Pop);
readLn;

End.

---------- Stack Class ---------
unit Stack;

interface

// NumberStack
type
  TNumStack = class
    NumStackArray : Array of integer;
    procedure Push(iPushvalue : Integer); // Bruges til at pushe en værdi ind i stacken
    Function Pop: Integer; // Henter den øverste værdi ud af stacken
    Function Peek: Integer; // Opgiver den øverste værdi i stacken
    procedure ClearStack; // Ryder hele stacken
    function Empty : boolean; // Ser om stacken er tom
    Constructor Create;
    function MyLength:Integer;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

// Operator Stack
type
  TOpStack = class
    OpStackArray : Array of char;
    procedure Push(cPushvalue : char); // Bruges til at pushe en værdi ind i stacken
    Function Pop : Char; // Henter den øverste værdi ud af stacken
    Function Peek : Char; // Opgiver den øverste værdi i stacken
    procedure ClearStack; // Ryder hele stacken
    function Empty : boolean; // Ser om stacken er tom
    Constructor Create;
    function MyLength:Integer;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

uses
  SysUtils, StrUtils;


{------------------------------------------
  Begyndelse af Number Stackens funktioner
------------------------------------------}

Constructor TNumStack.Create;
var t : integer;
begin
    SetLength(NumStackArray, 1);
    for t:=0 to Length(NumStackArray) do
      begin
      NumStackArray[t]:=0;
      end;
end;

// Sæt alle værdier i stacken til 0
procedure TNumStack.ClearStack;
var t : integer;
begin
for t:=0 to Length(NumStackArray) do
  begin
  NumStackArray[t]:=0;
  end;
end;

function TNumStack.MyLength:Integer;
begin
  Result:=Length(NumStackArray);
end;

// Ser om stacken er tom
function TNumStack.Empty : boolean;
var
i : integer;
Empty: Boolean;
begin
Empty:=True;
  for i:=0 to Length(NumStackArray) do
  begin
    if (Length(NumStackArray) > 1) then
    begin
      Empty:=False;
      break;
    end;
  end;
Result:=Empty;
end;

Function TNumStack.Pop: Integer;
var
i : integer;
begin
Result:=NumStackArray[0];
if (Length(NumStackArray) > 2) then
  begin
  for i:=0 to (Length(NumStackArray)-2) do
    begin
      NumStackArray[i]:=NumStackArray[i+1];
      NumStackArray[i+1]:=0;
    end;
  end
    else
    begin
      NumStackArray[0]:=0;
      NumStackArray[1]:=0;
    end;
SetLength(NumStackArray, (Length(NumStackArray)-1));
end;

// Push værdier til toppen af stacken
procedure TNumStack.Push(iPushValue : Integer);
var
i : integer;
Begin
  SetLength(NumStackArray, (Length(NumStackArray)+1));
  i:=Length(NumStackArray);
    while i>=1 do
    begin
      NumStackArray[i]:=NumStackArray[i-1];
      NumStackArray[i-1]:=0;
      i:=i-1;
    end;
  NumStackArray[0]:=iPushValue;
end;

// Læser den øverste værdi fra stacken uden at slette den.
Function TNumStack.Peek: Integer;
begin
  Result:=NumStackArray[0];
end;

{------------------------------------------
  Slutning på Number Stack funktioner
------------------------------------------}

{------------------------------------------
  Begyndelse af Operator Stackens funktioner
------------------------------------------}

Constructor TOpStack.Create;
var t : integer;
begin
    SetLength(OpStackArray, 1);
    for t:=0 to Length(OpStackArray) do
      begin
      OpStackArray[t]:='A';
      end;
end;

// Sæt alle værdier i stacken til 0
procedure TOpStack.ClearStack;
var t : integer;
begin
for t:=0 to Length(OpStackArray) do
  begin
  OpStackArray[t]:='A';
  end;
end;

// Ser om stacken er tom, Result er false hvis den ikke er tom.
function TOpStack.Empty : boolean;
var
i : integer;
Empty: Boolean;
begin
Empty:=True;
  for i:=0 to Length(OpStackArray) do
  begin
    if (Length(OpStackArray) > 1) then
    begin
      Empty:=False;
      break;
    end;
  end;
Result:=Empty;
end;

Function TOpStack.Pop : Char;
var
i : integer;
begin
Result:=OpStackArray[0];
if (Length(OpStackArray) > 2) then
begin
i:=0;
while i<=(Length(OpStackArray)-2) do
  begin
    OpStackArray[i]:=OpStackArray[i+1];
    OpStackArray[i+1]:='A';
    i:=i+1;
  end;
end
else
  begin
    OpStackArray[0]:='A';
    OpStackArray[1]:='A';
  end;
SetLength(OpStackArray, ((Length(OpStackArray))-1));
end;

// Push værdier til toppen af stacken
procedure TOpStack.Push(cPushValue : Char);
var
i : integer;
Begin
  SetLength(OpStackArray, ((Length(OpStackArray)+1)));
  i:=Length(OpStackArray);
    while i>=1 do
    begin
      OpStackArray[i]:=OpStackArray[i-1];
      OpStackArray[i-1]:='A';
      i:=i-1;
    end;
  OpStackArray[0]:=cPushValue;
end;

// Læser den øverste værdi fra stacken uden at slette den.
Function TOpStack.Peek : Char;
begin
  Result:=OpStackArray[0];
end;

function TOpStack.MyLength:Integer;
begin
  Result:=Length(OpStackArray);
end;

{------------------------------------------
  Slut på Operator Stack funktioner
------------------------------------------}

End.
Avatar billede linuxchristian Nybegynder
02. november 2005 - 17:50 #1
De vigtige dele af koden er:
Function TNumStack.Pop: Integer;
var
i : integer;
begin
Result:=NumStackArray[0];
if (Length(NumStackArray) > 2) then
  begin
  for i:=0 to (Length(NumStackArray)-2) do
    begin
      NumStackArray[i]:=NumStackArray[i+1];
      NumStackArray[i+1]:=0;
    end;
  end
    else
    begin
      NumStackArray[0]:=0;
      NumStackArray[1]:=0;
    end;
SetLength(NumStackArray, (Length(NumStackArray)-1));
end;

og

procedure Calculate;
begin
  Write('1. Length of Number array: ');
  WriteLn(NumberStack.MyLength);
  Write('1. Length of Op array: ');
  WriteLn(OpStack.MyLength);
  case OpStack.Pop of
      '*': begin
          NumberStack.push(NumberStack.Pop * NumberStack.Pop);
          end;
      '/': begin
          NumberStack.push(NumberStack.Pop div NumberStack.Pop);
          end;
      '-': begin
          NumberStack.push(NumberStack.Pop - NumberStack.Pop);
          end;
      '+': begin
          NumberStack.push(NumberStack.Pop + NumberStack.Pop);
          end;
  end;
  WriteLn(NumberStack.Peek);
  if (iRunTimes>0) then
  begin
    OpStack.Push(sExpression[iRunTimes]);
  end;
end;

Hvor koden laver fejl. Fejlen opstår ved SetLength i Pop, men kun når den køres anden gang.
Avatar billede pidgeot Nybegynder
03. november 2005 - 01:28 #2
Personligt ville jeg nu foretrække at bruge det SIDSTE element som toppen af stakken - så slipper du for konstant at flytte rundt. Det er dog en detalje.

Anyway - det er lidt et skud i tågen, men måske din SetLength forsøger at sætte arrayet til at indeholde enten -1 eller 0 elementer (kan ikke huske om man kan lave et dynamisk array på 0 elementer)? Prøv at smide et breakpoint ind lige før den sætning og se hvad der ligger i dit array.
Avatar billede pidgeot Nybegynder
03. november 2005 - 01:47 #3
Har lige læst lidt op på det - problemet kan tilsyneladende skyldes at du ikke definerer en array type (omend jeg ikke rigtigt forstår hvorfor din kode så virker i nogle tilfælde).

Dvs. du i din type sektion skal lave følgende:
TCharArray: array of char;
TIntegerArray: array of integer;

og bruge dem som typer til dine dynamiske arrays. Jeg synes dog ikke at kunne finde noget i dokumentationen til Delphi 2005 om dette, men det er vel et forsøg værd...

Og ja, selvfølgelig kan man lave et dynamisk array på 0 elementer, så hvis du tester for det jeg skrev ovenfor, skal du bare teste på om arrayet er tomt (beklager fejlen, men det er sent :))
Avatar billede linuxchristian Nybegynder
03. november 2005 - 11:26 #4
Jeg declare da dem i hver deres type:

    NumStackArray : Array of integer;
    OpStackArray : Array of char;
Avatar billede linuxchristian Nybegynder
03. november 2005 - 11:35 #5
Har fundet ud af hvad jeg gør, jeg laver bare et konstant array. Men ellers tak for hjælpen.
Avatar billede pidgeot Nybegynder
03. november 2005 - 12:44 #6
Det er stadig ikke godt nok - hvis du eksempelvis lavede en funktion der returnerede et array of string, SKAL du definere det som en seperat type før det kan returneres.

Man kan fint argumentere for at det ikke burde være nødvendigt (det mener jeg heller ikke det burde), men sådan virker det nu engang.

Men det vigtigste er jo du har fået det til at virke, så læg du et svar og behold pointene ;)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester