Programowanie w języku Delphi

PChar

  • 2014-02-09 00:29
  • 6 komentarzy
  • 5102 odsłony
  • Oceń ten tekst jako pierwszy


Ogólna informacja o typie danych


PChar jest typem danych, tzw. wskaźnikowym. To znaczy w samej zmiennej przechowywany jest tylko adres w pamięci wskazujący na tzw. null-terminated string - ciąg znaków (String) zakończony znakiem #0.


Zastosowanie


Zastosowanie w bibliotekach DLL


Typ PChar używany jest do zastępowania zmiennej typu String tam, gdzie nie można przekazywać zmiennych o zmiennej długości (String jest teoretycznie nieograniczony, więc jego długość, a zatem ilość zajmowanej przez niego pamięci jest dynamicznie dostosowywana do potrzeb), czyli na przykład przy przekazywaniu danych pomiędzy różnymi modułami programu. Przez moduł rozumiem tutaj bibliotekę dll czy główny program.

Można też zauważyć, że wszystkie funkcje WinApi są wywoływane również za pomocą zmiennych typu PChar, gdyż również przekazujemy w ten sposób zmienne do biblioteki DLL, której używa nasza aplikacja za pośrednictwem modułów takich jak Windows.pas, ShellApi.pas itp.


Zastosowanie do skracania stringów


Dodatkowo typ PChar można używać do prostszego skracania zmiennej typu string o określoną ilość znaków. Zauważmy, że jest to tylko adres w pamięci, a koniec tej zmiennej nie jest określony przez długość, a przez wystąpienie zarezerwowanego znaku #0. Dzięki temu wystarczy, że przesuniemy taki wskaźnik w prawo, a "obetniemy" pierwszą część naszego ciągu znaków. Jednocześnie nie musimy troszczyć się o odpowiednią zmianę długości, gdyż koniec dalej jest końcem.


Przykład dla skracania stringów


var
  S: String;
  C: Integer;
begin
  Write('Podaj ciąg znaków: ');
  ReadLn(S);
  Write('Podaj całkowitą liczbę znaków, którą chcesz usunąć z ciągu: ');
  ReadLn(I);
  S := String(PChar(S) + I);
  WriteLn(S);
end;

Można zauważyć tutaj dwie rzeczy:

1) Można by zastosować zamiast tego funkcję Copy albo Delete. Ta pierwsza jednak wymagałaby wykonania kolejnej funkcji Length (chyba, że znamy długość string'a), zaś ta druga działałaby równie skutecznie tylko w tym przypadku, gdy przypisujemy wynik naszego "ucinania" pod tą samą zmienną. Przy zastosowaniu PChar nie ma takiej konieczności. Dodatkowo zastosowanie funkcji Copy jest mniej czytelne szczególnie, gdy ilość znaków do ucięcia nie jest w prostej zmiennej, tylko wyraża się bardziej rozbudowanym wyrażeniem.

2) Zastosowałem tutaj coś, co mogłoby wyglądać jak dodawanie zmiennej typu Integer (liczba całkowita) do ciągu znaków. Musimy jednak pamiętać, że zmienna PChar jako taka jest liczbą, wskazującą tylko na adres w pamięci. Dodając więc liczbę całkowitą do wskaźnika "przesuwamy" jego wskazanie o żądaną liczbę bajtów, a zatem znaków (gdyż w zmiennej string każdy znak zajmuje 1 bajt). Dzięki temu uzyskujemy ciąg znaków rozpoczynający się od (I+1)-go znaku.


Przestroga


Rozważmy taki przykład:

function Foo(): PChar;
var 
  S: String;
begin
  S := 'Ala ma kota';
  Result := PChar(S);
end;

Po wywołaniu powyższej funkcji program zaalokuje w pamięci miejsce dla zmiennej S, przypisze jej tekst 'Ala ma kota', po czym zrzutuje ją na PChara i przypisze do wyniku.

Niebezpieczeństwo jest takie, że po opuszczeniu funkcji zmienna S jest dealokowana, a wynik dalej pokazuje na miejsce w pamięci gdzie ona była. Jeżeli koniecznością jest zwracanie zmiennej typu PChar, można zastosować jedną z poniżej podanych metod:

1.

function Foo(): PChar;
var
  S: String;
begin
  D := 'Ala ma kota';
  Result := NewStr(S);
  // NewStr nie powinno być używane - jest wykorzystywane tylko dla wstecznej kompadybilności
end;

2.

function Foo(): PChar;
var
  S: String;
begin
  S := 'Ala ma kota';
  GetMem(Result, Length(S));
end;

Należy jednak pamiętać o zwolnieniu tak zaalokowanej pamięci poprzez funkcję DisposeStr lub w drugim przypadku przez procedurę FreeMem.

6 komentarzy

Wolverine 2005-12-22 16:07

<quote>NewStr powinno nie powinno być używane. Jest ono używane tylko dla kompatybilności wstecz</quote>
Dobre :D

Szczawik 2005-12-22 02:21

A ja się czepię:

<quote>samej zmiennej przechowywany jest tylko adres w pamięci wskazujący na tzw. null-terminated string</quote>

PChar przechowuje adres wskazujący na Char. Nic więcej. To, że są funkcje pozwalające na obsługę null-terminated string pod tym adresem to sprawa funkcji, ale nie typu PChar. Warto byłoby to zaznaczyć.

Adam.Pilorz 2005-12-21 21:23

Hmm... Faktycznie trochę nienajlepsze sformułowanie. Ale nie mogę wymyślić nic, czym by to zastąpić (w sensie jak by to w słowa ująć ładnie). Niech ktoś podpowie :P

Adam.Pilorz 2005-12-21 17:38

Jak ktoś ma pomysł, co można dodać, to zapraszam do wprowadzania twórczych modyfikacji :]

Wolverine 2005-12-21 17:50

<quote>Typ PChar używany jest do zastępowania zmiennej typu String tam, gdzie nie można przekazywaćzmiennych o zmiennej długości</quote>
Jakos mi to nie tages, to tez "zmienna o zmiennej dlugosci", robi realloca i masz wiekszy/mniejszy ciag, napisalbym raczej cos w stylu, ze to jest taki jakby uniwersalny format przekazywania lancuchow, no bo delphi ma jakas swoja koncepcje na stringi, stl znowu jakas inna itp.