PChar

Adam.Pilorz

1 Ogólna informacja o typie danych
2 Zastosowanie
     2.1 Zastosowanie w bibliotekach DLL
     2.2 Zastosowanie do skracania stringów
     2.3 Przykład dla skracania stringów
3 Przestroga

Ogólna informacja o typie danych

PChar jest [[Delphi/typy danych|typem danych]], tzw. wskaźnikowym. To znaczy w samej [[Delphi/zmienne|zmiennej]] przechowywany jest tylko adres w pamięci wskazujący na tzw. null-terminated string - ciąg znaków ([[Delphi/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

type PChar = ^Char;

i tyle...

NewStr powinno nie powinno być używane. Jest ono używane tylko dla kompatybilności wstecz

Dobre :D

A ja się czepię:

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

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ć.

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

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

Typ PChar używany jest do zastępowania zmiennej typu String tam, gdzie nie można przekazywaćzmiennych o zmiennej długości

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.