Wskaźnik do tablic dynamicznych

0

Witam forumowiczów,

Mam problem z poleceniami high(tablica); i setlength(tablica);. Otóż mam funkcję:

Function Bool_to_byte(tablica:Pointer;pozycja:word):byte;

funkcja ma za zadanie operować na kilku dynamicznych tablicach z którymi to jest wywoływana.
I których adres jest umieszczony w wskaźniku.
Funkcję wywołuję w ten sposób:

zm:=bool_to_byte(@dynamiczna_tablica;zm1);

Ale już w samej funkcji instrukcje High(tablica) nie działają jak i przypisanie wartości np:

tablica[10]^:=12;

Jak mogę określić ilość elementów w tablicy lub zmienić jej wielkość posiadając jedynie jej adres ?. Lub jak przekazać do funkcji dynamiczną tablicę ?. Czuję, że czegoś z tymi wskaźnikami nie rozumiem :(.

0

funkcja ma za zadanie operować na kilku dynamicznych tablicach
Pokaż definicję tej "dynamicznej tablicy".

Jak mogę określić ilość elementów w tablicy lub zmienić jej wielkość posiadając jedynie jej adres

Jeśli przekazujesz pointer (co samo w sobie jest dziwne, bo od tego są wskaźniki typowane, jak pboolean czy pbyte) to jedynym sposobem jest przekazanie osobnego parametru z rozmiarem.

Lub jak przekazać do funkcji dynamiczną tablicę

Function Bool_to_byte(tablica:array of boolean; pozycja:word):byte; // to jest tzw. "open array"

(* albo *)

type TBoolArray = array of boolean;
Function Bool_to_byte(tablica:TBoolArray; pozycja:word):byte; // to jest "dynamic array". nie to samo co open array

?

Nie stawiamy spacji przed znakiem zapytania. Jak? Tak.

0

zdefiniuj nowy typ np:

type moja_tablica = array of "coś tam";

dalej zmienną

 var dynamiczna_tablica:moja_tablica; 

definicję funkcji jako

Function Bool_to_byte(tablica:moja_tablica;pozycja:word):byte;

a samo wywołanie funkcji jako

zm:=bool_to_byte(dynamiczna_tablica;zm1);

i powinno śmigać

0

Dziękuję za szybką odpowiedź. Najbardziej zrozumiała dla mnie starego pascalowca konstrukcja to :

Function Bool_to_byte(tablica:array of boolean; pozycja:word):byte;

Co najważniejsze działa. Zasadniczo problem jest inny - by zapisać tablicę typu boolean do pliku zamieniam osiem kolejnych komórek true/false na bity 0/1 i utworzony bajt zapisuje do pliku. Może istnieje inna prostsza metoda zapisu zawartości dynamicznej tablicy typu boolean do pliku ?

0

gdybyś zdefniował tablicę o z góry znanym rozmiarze , np array [0..1000] of boolean , to miał byś w pamięci ciągły obszar zawierający treść tablicy , wtedy można by próbować zrzucić do pliku zawartość tego obszaru pamięci , o znanym adresie i rozmiarze .
Nie mam pewności , ale wydaje mi się że definiując tablicę jako "array of boolean" nie ma żadej gwarancji że zawartość tablicy będzie w jednym , ciągłym , obszarze pamięci

0
grzegorz_so napisał(a)

gdybyś zdefniował tablicę o z góry znanym rozmiarze , np array [0..1000] of boolean , to miał byś w pamięci ciągły obszar zawierający treść tablicy , wtedy można by próbować zrzucić do pliku zawartość tego obszaru pamięci , o znanym adresie i rozmiarze

Taa, a przy okazji otrzymał plik wynikowy osiem razy większy od tego, jaki jest naprawdę potrzebny.

Hint: na większości platform (wszystkich?) boolean to asmowy odpowiednik uint8, który jednak na pewnej warstwie abstrakcji przyjmuje tylko wartości 0 oraz 1, tak więc w przypadku np. x86 masz zmarnowane 7 bitów na każdy bajt (zawsze będą zerami). Choć może istnieje jakiś kompilator, który potrafi to ładnie pamięciowo zoptymalizować.

Nie mam pewności , ale wydaje mi się że definiując tablicę jako "array of boolean" nie ma żadej gwarancji że zawartość tablicy będzie w jednym , ciągłym , obszarze pamięci

Jest gwarancja.

@Marko1976: pamiętaj, że

Procedure Foo(Tab: Array of Integer);

jest czymś innym odType TIntegerArray = Array of Integer;
Procedure Foo(Tab: TIntegerArray);

1

@Patryk27 musi zajmować ciągły obszar - gdyby tak nie było nie działało by poruszanie się po tablicy poprzez inkrementowany wskaźnik

0

zastanawia mnie jedno , deklarujemy tablicę otwartą, bez ustalonego rozmiaru np 'Mtable = array of byte ' , która ma wstępnie przydzielony pewien pewien określony i skończony ciągły blok pamięci na treść tablicy . Dopóki zmiana rozmiaru tablicy funkcją 'setlength()' nie powoduje przekroczenia rozmiaru wstępnie przydzielonej pamięci to jest ok, ale co się dzieje kiedy programowo ustali się nowy , większy rozmiar tablicy, wykraczający poza przydzieloną wstępnie pamieć ? Aby zachować ciągłość pamięci należało by przydzielić dla tablicy , nowy , większy kawałek pamięci dostosowany do nowego jej rozmiaru i równocześnie przenieść dotychczasową zawartość tablicy w nowy obszar, a stary, już nie używany zakres pamięci zwolnić

2

i tak się też dzieje http://docwiki.embarcadero.com/RADStudio/XE8/en/Internal_Data_Formats#Dynamic_Array_Types

dlatego zaleca się zamiast

for i := 1 to 10000 do
begin
  SetLength(arr, Length(arr) + 1);
  arr[i] := 'xxx';
end;

//stosować np. tak
for i := 1 to 1000 do
begin
  if Length(arr) < i then
    SetLength(arr, Length(arr) + 100);
  arr[i] := 'xxx';
end;

znaczy zwiększać rozmiar tablicy o większe bloki i ew. na końcu (jeśli wcześniej nie jest znany rozmiar) zmniejszyć rozmiar do wymaganego - zmniejszysz liczbę przesunięć tablicy po pamięci

0

@abrakadaber
dyskutowany temat ,jak do tej pory , nigdy nie był istotnym problemem w moich aplikacjach , ale bardzo dziękuję za wyjaśnienie które może przyda się na przyszłość :)

3

@grzegorz_so - w Delphi7 (w nowszych nie wiem) i bibliotece FPC taki przykład znajdziesz w klasie TStringList; Pamięć dla listy alokowana jest większymi blokami, jesli Count zrówna się z Capacity - wtedy do akcji wkracza metoda Grow (piszę o FPC), która oblicza nowy rozmiar i wywołuje SetCapacity, aby realokować pamęć listy:

procedure TStringList.Grow();
var
  NC: Integer;
begin
  NC := FCapacity;

  If NC >= 256 then
    NC := NC + (NC Div 4)
  else
    if NC = 0 then
      NC := 4
    else
      NC := NC * 4;

  SetCapacity(NC);
end;

Tyle że całość nie opiera się na zwykłej tablicy - wzamian trzymany jest wskaźnik na poczatek bloku pamięci jako rezultat funkcji GetMem; Potem bloki pamięci są kopiowane za pomocą Move, pusta część jest zerowana, a cały stary blok pamięci jest zwalniany;

Bloki pamięci realokuje się rzadko, bo przenoszenie jednego obszaru pamięci do drugiego trochę trwa; Tym bardziej, jeśli blok jest duży; Każdorazowa relokacja pamięci znacznie spowalniała by program, dlatego całość bazuje na alokowaniu pamięci z dynamicznie obliczanym zapasem i sprawdzanie "pełności" bloku na podstawie pomocniczego pola;

Ale to jeśli chodzi o samo alokowanie pamięci z zapasem;


Nie wiem co lepsze w przypadku zwykłych tablic dynamicznych, ale powiększanie tablicy każdorazowo przy dodawaniu nowego elementu nie będzie sprzyjać przy dużych tablicach; Dla małych na pewno nie będzie to miało większego znaczenia, żeby się tym przejmować;

PS: W przypadku zmiany rozmiaru dynamicznej tablicy za pomocą SetLength, blok pamięci macierzy może zostać w całości przeniesiony w inne miejsce przez menedżer pamięci, albo i też nie; Widać jeśli obecny blok po powiększeniu zmieści się (jest na tyle wolnej pamięci obok), to zostanie on tylko powiększony; A jeżeli nie zmieści się - przeniesiony w całości w inne miejsce;

Duże tablice często mogą być przenoszone w całości, więc potrzeba będzie więcej czasu na ich powiększenie; Zresztą można sobie sprawdzić w zwykłej konsolówce:

var
  arrTest: array of UInt8;
begin
  SetLength(arrTest, 10);
  WriteLn('Address: ', PtrInt(@arrTest[0]));

  SetLength(arrTest, 20);
  WriteLn('Address: ', PtrInt(@arrTest[0]));
end.

Wynikiem u mnie będzie:

Address: 347112
Address: 347112

Jak widać adres pierwszej komórki nie zmienil się - widać menedżer mógł tylko poszerzyć blok pamięci; Natomiast przy większej macierzy adres może się zmienić - test:

var
  arrTest: array of UInt8;
begin
  SetLength(arrTest, 5000000);
  WriteLn('Address: ', PtrInt(@arrTest[0]));

  SetLength(arrTest, 7000000);
  WriteLn('Address: ', PtrInt(@arrTest[0]));
end.

Czego efektem będzie inny adres pierwszej komórki macierzy:

Address: 25362520
Address: 30474328

Adres zmienił się, choć nowy rozmiar tablicy to tylko 7MB.

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