Błąd przy przypisywaniu wartości do tablicy dwuwymiarowej

0

Pisze program, ktory zamienia dwa stringi na dwie tablice jednocyfrowych intow o dlugosci kazdego ze stringow. Pozniej do dalszej czesci programu potrzebuje stworzyc tablice dwuwymiarowa o okreslonych indeksach i przypisac kazdemu jej elementowi wartość 0.
Kompilator wywala mi błąd przy próbie zapełnienia tablicy dwuwymiarowej zerami, wydaje mi sie ze moze byc przekroczenie zakresow tablic,a le nie wpadlem na to dlaczego, ponizej kod

   program Recordapp;

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
   liczba1, liczba2:string;
   DlugoscLiczba1,DlugoscLiczba2:integer;
   TablicaLiczba1, TablicaLiczba2, Wynik : array  of Integer;
    Obliczenia :array of array of Integer;

    WierszeTablicyWyniku,i,j : Integer;


begin
    Writeln('Podaj liczbe 1');
    Readln(liczba1);
    Writeln('Podaj liczbe 2');
    Readln (liczba2);
    DlugoscLiczba1:=length(liczba1);
    DlugoscLiczba2:=length(liczba2);
    SetLength(TablicaLiczba1, DlugoscLiczba1);
    SetLength(TablicaLiczba2, DlugoscLiczba2);
    SetLength(Wynik, DlugoscLiczba1-DlugoscLiczba2+1);
    WierszeTablicyWyniku:= DlugoscLiczba1*2+3;
    SetLength(Obliczenia,WierszeTablicyWyniku , DlugoscLiczba1);

    for  i:=1 to DlugoscLiczba1 do
      TablicaLiczba1[i]:=StrToInt(liczba1[i]);

    for  i:=1 to DlugoscLiczba2 do
      Write(TablicaLiczba1[i]);

    for  i:=1 to DlugoscLiczba2 do
      TablicaLiczba2[i]:=StrToInt(Liczba2[i]);

      writeln(' ');

    for  i:=1 to DlugoscLiczba2 do
      Write(TablicaLiczba2[i]);

    for  i:= 1 to WierszeTablicyWyniku do
       begin
         for  j:= 1 to DlugoscLiczba1  do  //tu kompilator sie zatrzymuje

            Obliczenia[i,j]:=0;

       end;


readln;
1

Podczas deklaracji tablicy możesz wskazać zakres indeksów - coś w stylu array [4..19] of xxx.
Ale to tak nie działa przy tablicach dynamicznych - tam zaczyna się do zera - http://www.delphibasics.co.uk/RTL.asp?Name=setlength setlength(singleArray, 4); // Now fill it up : note that dynamic arrays start at 0.

W związku z tym pętle powinny mieć postać

for i:= o to (DlugoscLiczba2 - 1)

Dałem Ci zresztą przykład w innym wątku, który założyłeś chwilę temu w tej samej sprawie - https://4programmers.net/Forum/1650693. Czy zrozumiałeś, jak działa wstawiony tam przykład?

0

Tak zrozumiałem, Czy tylko string deklarowany bez nawiasów kwadratowych ma pierwszy indeks domyślnie od 1 a nie od 0? Myślałem ze jak wypełniam tablice dynamiczne pętla z i=1 to indeksy będą od 1, tak jak przy deklarowaniu zakresu nr indeksów w nawiasach

1

Zwrócę Ci uwagę, żebyś nie brał za pewnik, że pisząc

var Napis: String;
Napis:= 'Ala ma kota';
ShowMessage(Napis[5]);

dostaniesz znak o numerze 5. W ten sposób uzyskujesz jedynie wartość bajtu numer 5, ale w wielu przypadkach może być zastosowane kodowanie wielobajtowe - chociażby UTF-8, przez co wartość zwrócona w ten sposób może być totalnie czymś przypadkowym (znaczy - przypadkowa nie będzie, ale patrząc z punktu widzenia osoby, która czeka na konkretną literę ze stringa - dostaniesz śmieci)

http://docwiki.embarcadero.com/RADStudio/Rio/en/String_Types_(Delphi)

You can index a string variable just as you would an array. If S is a non-UnicodeString string variable and i, an integer expression, S[i] represents the ith byte in S, which may not be the ith character or an entire character at all for a multibyte character string (MBCS). Similarly, indexing a UnicodeString variable results in an element that may not be an entire character. If the string contains characters in the Basic Multilingual Plane (BMP), all characters are 2 bytes, so indexing the string gets characters. However, if some characters are not in the BMP, an indexed element may be a surrogate pair - not an entire character.

Ale odpowiadając na Twoje pytanie - tak, w przypadku dostępu w pokazany powyżej sposób do elementów string pierwszy znak jest pod indeksem 1 (z zastrzeżeniem podanym powyżej dot. kodowania znaków niebędących typowymi ASCII).

1
srk71 napisał(a):

Kompilator wywala mi błąd przy próbie zapełnienia tablicy dwuwymiarowej zerami, wydaje mi sie ze moze byc przekroczenie zakresow tablic,a le nie wpadlem na to dlaczego, ponizej kod

Wydaje się? Podaj treść błędu lub od biedy zrzut ekranu, a nie opis tego co Ci się wydaje.

srk71 napisał(a):

Czy tylko string deklarowany bez nawiasów kwadratowych ma pierwszy indeks domyślnie od 1 a nie od 0?

Wszystkie stringi – o określonej długości lub nie – i tylko stringi indeksowane są od 1. Macierze dynamiczne zawsze indeksowane są od 0. Statyczne macierze o własnoręcznie wybranej indeksacji, indeksowane są tak jak się chce – od 1, od A, od False itd. Innych indeksowanych kontenerków nie ma, więc to tyle.

O listach w postaci klas nie wspominam, bo to nie są typy zarządzane. A zbiory to inna bajka.

0

Dzieki za pomoc, odpowiedz @cerrato juz mi pomogla, chodzilo o przekroczenie zakresu tablic przez numerowanie od 1 do WielkoscTablicy zamiast od 0 do WielkoscTablicy-1, nastepnym razem bede wklejal komunikat kompilatora.

0

Do tablic używaj HighLength tylko do stringów. A jeśli potrzebujesz przeiterować po wszystkich elementach tablicy po to by odczytać dane z kolejnych komórek, to skorzystaj pętli for in – nie będziesz musiał się martwić o indeksy.

I za każdym razem gdy zakładasz wątek, nadaj mu tag delphi oraz podaj wersję środowiska. To ważne, bo im nowsza wersja tym więcej zmian w stosunku do podstawowej wersji języka.

0

Do tablic używaj High – Length tylko do stringów.

A czy możesz wyjaśnić, dlaczego?
Nawet na oficjalnej stronie Free Pascala - https://www.freepascal.org/docs-html/rtl/system/length.html jest napisane, że length jest używane do tablic:
Length returns the length of the string or array [...] For dynamical or statical arrays, the function returns the number of elements in the array.

1

@cerrato: jak ktoś zadeklaruje tablice

array[100..115] of integer

to będziesz wiedział ;-)

0

@Clarc: też o tym myślałem, ale po pierwsze - samo high nic nam nie da, bo wiemy, jaki indeks ma końcowy element, ale nie wiemy, od czego się numeracja zaczyna, więc i tak trzeba sprawdzić początek. A w tym momencie nie widzę większej różnicy, czy weźmiemy high i low czy np. low oraz length.

A po drugie - jeśli (jak w moim przykładzie podanym w odpowiedzi na inne pytanie autora tego wątku - https://4programmers.net/Forum/1650693) tablica zostanie przekazana jako argument funkcji (procedure ZmienNazwe(PrzekazanaTablica: array of String); ), to niezależnie od indeksów podanych przy deklaracji oryginalnej tablicy, ta będąca przekazana do funkcji (czyli PrzekazanaTablica) będzie indeksowana od zera. Wtedy także length nada się idealnie.

0

Chodziło mi o to, że w przypadku tablic, użycie Length do określenia czegoś innego niż rozmiar jest bez sensu, bo nawet jeśli w pewnych przypadkach nie spowoduje błędu (indeksowanie od 1), to w innych spowoduje. I nie dość że będą błędy, to jeszcze złe praktyki się wpoją (np. indeksowanie od 1 jest jedną z nich).

Ciągi znaków są tak indeksowane, ze względu na wsteczną kompatybilność z typem ShortString, który mógł posiadać maksymalnie 255 znaków, a w komórce o indeksie 0 przechowywana była aktualna jego długość. Gdyby nie to, to w Pascalu stringi byłyby indeksowane od 0, tak jak w innych językach, ale zachowanie kompatybilności było ważniejsze.


cerrato napisał(a):

Nawet na oficjalnej stronie Free Pascala - https://www.freepascal.org/docs-html/rtl/system/length.html jest napisane, że length jest używane do tablic:

Tak, i wyraźnie jest napisane, że służy do pobrania rozmiaru, czyli liczby komórek, a nie do określania indeksów. Dlatego używa się go do określenia rozmiaru ciągów znaków i rozmiaru tablic, bo nie jest ona wrażliwa na indeksowanie. Natomiast do iterowania po elementach macierzy powinno się używać Low i High, a w przypadku iterowania tylko do odczytu, wygodniejsze jest użycie pętli for in.

cerrato napisał(a):

@Clarc: też o tym myślałem, ale po pierwsze - samo high nic nam nie da, bo wiemy, jaki indeks ma końcowy element […]

No nie – w przypadku macierzy dynamicznych i otwartych nie wiemy jaki jest ostatni indeks i użycie Length w takim przypadku jest bez sensu, bo trzeba pamiętać o dekrementacji rozmiaru, co jest kolejnym miejscem do popełnienia błędów.

Kod powinno się pisać tak, aby używać tych samych czytelnych i krótkich konstrukcji dla wszystkich macierzy, bez względu na ich typ. I oczywiście tak, aby nie utrudniać analizy kodu i jego utrzymania.

1

@furious programming napisał właściwie wszystko to co chciałem. low i high idą z sobą w parze jak begin i end. Dla mnie bardziej logiczny jest zapis

for i := low(tablica) to high(tablica) do

niż

for i := low(tablica) to (low(tablica) + length(tablica) - 1) do

a najlepiej użyć

for var element in tablica do

i nie martwić się indeksami.

A po drugie - jeśli (jak w moim przykładzie podanym w odpowiedzi na inne pytanie autora tego wątku - https://4programmers.net/Forum/1650693) tablica zostanie przekazana jako argument funkcji (procedure ZmienNazwe(PrzekazanaTablica: array of String); ), to niezależnie od indeksów podanych przy deklaracji oryginalnej tablicy, ta będąca przekazana do funkcji (czyli PrzekazanaTablica) będzie indeksowana od zera. Wtedy także length nada się idealnie.

Aż głupio się przyznać ale tego nie znałem, musiałem sprawdzić na kodzie. Fajnie, że można się ciągle nauczyć jeszcze czegoś z podstaw :]

0

Macierze otwarte to już inna bajka. Teoretycznie, jeśli zawsze dbamy o indeksację od 0, to pętle możemy indeksować nie od Low, a od hardkodowanej wartości 0, co sam preferuję (ze względu na ”kompatybilność” konstrukcji z iterowaniem po listach). No ale to już bardziej kwestia gustu niż wytycznych pisania czytelnego kodu.

0

Ten przykład jest niepoprawny – spowoduje wykroczenie poza zakres

Moim zdaniem nie masz racji, aczkolwiek mogę się mylić. Poniżej napiszę, dlaczego uważam, że to, co napisał @Clarc jest OK i żadnego błędu nie będzie, a Ty proszę rzuć na to okiem i pokaż, w którym miejscu się mylę.

  • deklaruję tablicę [4..6]
  • low(tablica) daje mi 4
  • length(tablica) daje mi 3
  • low(tablica) + length(tablica) -1 daje mi 4 + 3 - 1 czyli 6
  • biorąc pod uwage w/w dostaję pętlę for i:=4 to 6 - gdzie mamy przekroczenie zakresów?
0

Zdążyłem już poprawić post – po dokładniejszym przyjrzeniu się temu fragmentowi. Fakt, zabezpiecza przed innym indeksowaniem, ale i tak pokazana konstrukcja jest zbyt skomplikowana, by była warta uwagi. Bez sensu – całą arytmetykę załatwia jeden poczciwy High.

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