Dlaczego po zamknięciu programu wyskakuje Runtime Error?

0

Dzień dobry,
zacznę od tego, że pisałem dawno temu programy w Pascalu. Ostatnio w ramach hobby zacząłem pisać w Delphi programy. Dlatego proszę aby ze zrozumieniem potraktować ten post z prośbą o pomoc. Nie opanowałem jeszcze tego kompilatora(i języka).

Otóż napisałem program, którego zadaniem jest wypisać do pliku wszystkie kombinacje z dwumianu Newtona dla zadanego n i k. Algorytm opracowałem, program napisałem ale jest problem. Po uruchomieniu wszystko działa zgodnie z założeniem. Robi co ma robić z jednym wyjątkiem a mianowicie po zakończeniu działania i wyjściu z programu pojawia się "runtime error". Oczywiście próbowałem na własną rękę szukać w sieci informacji o tym konkretnym błędzie ale nie są to odpowiedzi, które rozwiązały by problem a tym bardziej odpowiedziałyby na pytanie "dlaczego tak się dzieje".

Nie dla wszystkich wartości n i k tak się dzieje. Dla n=6 i k =4 lub n=49 k=6 występuje problem i dla wielu innych również ale te zapamiętałem. I teraz najciekawsza rzecz problem nie występuje w przypadku kompilacji w Lazarusie. Korzystam z legalnego Delphi 10.3 Community Edition.

Bardzo proszę kogoś kto zna się na rzeczy aby rzucił okiem i doradził co z tym zrobić.
Z góry dziękuję.

poniżej kod

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Label2: TLabel;
    Label3: TLabel;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  n,k,aktualne_pole : integer;
  tablica : array of integer;
  tablica_pomocnicza : array of integer;
  plik : TextFile;
  zmiana : boolean;
  licznik_lini_pliku : integer;
  licznik_konca_pisania : boolean;
implementation

{$R *.dfm}

function porownanie_tablic(tab,tab2 :array of integer):integer;
var
  c,d : byte;
begin
  Result := 0;
  for c := 0 to k-1 do
    begin
      for d := 0 to k-1 do
        begin
          if tab[c] = tab2[d] then
          Result:=Result+1;
        end;
    end;
end;

procedure inicjalizacja_plikow;
begin
  AssignFile(plik, 'D:\dwumian_wyniki.txt');
  Rewrite(plik);
end;

procedure zamknij_pliki;
begin
  CloseFile(plik);
end;

procedure wypisz;
var
  w : integer;
begin

  for w := 0 to k-1 do
    begin
      write(plik,IntToStr(tablica[w])+' ');
    end;
  writeln(plik,' ');
  licznik_lini_pliku := licznik_lini_pliku + 1;
end;
procedure wypisz_liczbe_operacji;
begin
  write(plik,IntToStr(licznik_lini_pliku));
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  index, factor,j,i,l : integer;
begin
  inicjalizacja_plikow;
  licznik_lini_pliku:=0;
  n:= StrToint(Edit1.Text);
  k:= StrToint(Edit2.Text);
  index := 1;
  factor := n;
  SetLength(tablica,k-1);
  SetLength(tablica_pomocnicza,k-1);
  for j := k-1 downto 0 do
    begin
      tablica_pomocnicza[j] := factor;
      factor := factor - 1;
    end;
  for i := 0 to k-1 do
    begin
      tablica[i] := index;
      index := index + 1;
    end;
  wypisz;
  aktualne_pole := k-1;
  zmiana := false;
  repeat
    if tablica[aktualne_pole]=n then
      begin
        zmiana := true;
        aktualne_pole := aktualne_pole - 1;
      end;
    tablica[aktualne_pole] := tablica[aktualne_pole] + 1;
    if zmiana = true then
      begin
        for l := aktualne_pole + 1 to k-1 do
        begin
          tablica[l] := tablica[l-1] + 1;
        end;
        if tablica[k-1] = n then
        begin
          aktualne_pole := aktualne_pole -1;
          zmiana := true;
        end
                            else
                            begin
                              aktualne_pole := k - 1;
                              zmiana := false;
                            end;
      end;
      wypisz;
  until porownanie_tablic(tablica,tablica_pomocnicza) = k;
  ShowMessage('Gotowe !!!');
  wypisz_liczbe_operacji;
  zamknij_pliki;
end;


end.
1

Runtime Error zawsze występuje w parze z numerem. Tak więc, jaki numer ma ten błąd?

0

ten błąd pojawia się gdy zamykasz program?
Coś mi się wydaje że jest jakiś problem z ustalaniem wielkości tablic?

8

ustalasz rozmiar tablicy na SetLength(tablica, k - 1); a potem jedziesz poza zakresem tablicy aktualne_pole := k - 1; i If tablica[aktualne_pole] = n Then. Jeśli rozmiar tablicy to 4 to tablica ma komórki o indeksach 0, 1, 2 i 3. Takich miejsc jest dużo więcej, np. tu

   SetLength(tablica_pomocnicza, k - 1);
    For j := k - 1 Downto 0 Do
    Begin
        tablica_pomocnicza[j] := factor;
        factor                := factor - 1;
    End;

wejdź w opcje projektu: menu Project -> Options potem Compiling i włącz wszystko w Runtime errors:

2021-07-19 23_58_26-Project Options for Project4.exe  (Win32 - Debug).png

0

Musiałem zmienić login dlatego, że na poprzednio podany mail nie dostałem linka a teraz piszę z pracy więc założyłem nowe konto.

Jeżeli chodzi o numer błędu wygląda to tak.
Podczas wyjścia czyli program zrobi co ma zrobić i wtedy po kliknięciu w X w prawym górnym rogu okna. Czyli do tego momentu wszystko jest ok i aplikacja jest stabilna.
Najpierw pojawia się "Application Error : Exception Einvalid Pointer in module Project1.exe at 0000CC14. Invalid pointer operation"
a następnie "Runtime Error 255 at 77B55B5F"

Jeżeli chodzi o to co napisał abrakadaber to nie widzę (w tym co napisałeś) przejścia poza tablicę. Właśnie dlatego, że komórki zaczynają się o [0] tam jest k-1. W wycinku kodu , który zacytowałeś jasno to widać czyli zaczynam pętlę od k-1 i idąc w dół do 0 robię operację na polach. Przepraszam ale nie widzę tutaj wyjścia poza zakres tablicy.

Wieczorem spróbuję przestawić opcję kompilatora jak radziłeś teraz nie mam takiej możliwości.

W związku z tym co napisał abrakadaber jest coś co na zdrowy rozsądek mi się nie zgadza otóż gdyby tak było jak napisałeś to program nie miałby prawa wykonać wszystkich operacji poprawnie. Plik jest tworzony i dane w nim zawarte są takie jakie są oczekiwane. Wysypał by błąd dużo wcześniej.
Poza tym to co mnie najbardziej zastanawia to jest to dlaczego Lazarus nie ma tych problemów a Delphi tak. Przecież gdyby był problem z adresowaniem tablicy to konsekwentnie Lazarus powinien sygnalizować błąd.

5

@zolthor2: jak nie widzisz przejścia poza zakres?

ustalamy że k = 4,

  n:= StrToint(Edit1.Text); // 6
  k:= StrToint(Edit2.Text); // 4
  index := 1;
  factor := n;
  SetLength(tablica,k-1); // czyli tablica ma 3 elementy: 0,1,2
  SetLength(tablica_pomocnicza,k-1); // to samo, tablica ma elementy: 0,1,2
  for j := k-1 downto 0 do // zaczynasz od k-1, czyli 3!
    begin
      tablica_pomocnicza[j] := factor; // tutaj w pierwszej iteracji odwołujesz się do tablica_pomocnicza[3] a przecież największy element to 2?
      factor := factor - 1;
    end;
0

Dziękuję. Bardzo jasno teraz to widzę ;-)

z jakiegoś powodu myślałem, że abrakadaber ma na myśli zakres tablicy w dół kompletnie nie widziałem góry.

Jakim cudem to działa nie mam pojęcia. Grunt, że pokazaliście mi mój błąd.

Jedno wiadomo na pewno - Delphi jest lepszym kompilatorem niż Lazarus bo błąd jest ewidentny więc reakcja również prawidłowa.

raz jeszcze wszystkim dziękuję.

0

@zolthor2:

Jedno wiadomo na pewno - Delphi jest lepszym kompilatorem niż Lazarus bo błąd jest ewidentny więc reakcja również prawidłowa.

to że aplikacja w Delphi rzuciła wyjątkiem a w Lazarusie nie, jest czystym przypadkiem wynikającym zapewne z różnego przydziału pamięci dla zmiennych .
Wychodząc poza zakres tablicy nadpisałeś jakieś inne dane. W lazarusie prawdopodobnie były to jakieś nieistotne już dane, albo wręcz obszar wolny , a w Delphi nadpisałeś jakiś wskaźnik, o czym może świadczyć błąd "Exception Einvalid Pointer in module ... "

0

Być może to przypadek tylko, że sprawdzałem to na kilku komputerach z różnym OS itd. problem zawsze był konsekwentnie taki sam więc założyłem, że różnica tkwi w kompilatorze chociaż początkowo zakładałem, że zwaliłem coś w programie ale szybko zmieniłem zdanie i to był błąd.

Szkoda, że kompilator nie sprawdza tego typu błędów podczas kompilacji.

proszę o zamknięcie wątku

raz jeszcze wielkie dzięki za pomoc

5

sprawdza ale nie podczas kompilacji (bo nie ma fizycznie możliwości ponieważ nie wiadomo co będziesz chciał odczytać) a podczas runtime (czyli działania aplikacji) i właśnie to sprawdzanie możesz włączyć w opcjach projektu tak jak Ci pisałem wcześniej.

2
grzegorz_so napisał(a):

Jedno wiadomo na pewno - Delphi jest lepszym kompilatorem niż Lazarus bo błąd jest ewidentny więc reakcja również prawidłowa.

Grzesio, Lazarus nie jest kompilatorem — jest nim FPC. ;)

to że aplikacja w Delphi rzuciła wyjątkiem a w Lazarusie nie, jest czystym przypadkiem wynikającym zapewne z różnego przydziału pamięci dla zmiennych .

Tutaj chodzi o to, że utworzony w Lazarusie projekt domyślnie używa trybu Default, w którym testowanie zakresów i innych rzeczy jest wyłączone i dzięki temu można sobie mazać po pamięci. Dlatego zawsze sugeruję najpierw wejść do ustawień projektu, wejść do okna Build modes i wygenerować dwa nowe tryby — Debug i Release. Ten pierwszy ma włączone wszystkie opcje pozwalające wyłapywać wszelkie błędy i ogólnie wygodnie debugować kod.

1 użytkowników online, w tym zalogowanych: 0, gości: 1