Compago

...free knowledge

 
  • Increase font size
  • Default font size
  • Decrease font size
Home Manuali Programmazione String Type in Delphi

String Type in Delphi

E-mail Stampa PDF

Il tipo string detto anche AnsiString o Long String è stato introdotto in alternativa al tipo Short String che aveva una limitazione a 255 caratteri. Come altri tipi quali Variant, OleVariant , Interface, dispInterface e Dynamic Array, rientra in un tipo di dati Life-Managed chiamati anche Garbage Collected type, perché possono condividere risorse e la loro esistenza e presenza è tenuta sotto controllo da un contatore.
Un altra loro caratteristica è che al momento della creazione sono inizializzati a zero o a qualche valore che li identifichi come non-usati.
Il compilatore in presenza di questo tipi di dati aggiunge che un blocco try-finally-end con il quale gestisce gli errori e garantisce il cleanup relativo alle variabili garbage collected.

Quello che deve essere chiaro è che una string in delphi non è un semplice puntatore a un array di caratteri come avviene in altri linguaggi. La sua struttura è più complessa ma anche più sicura (terminano sempre con il carattere nullo).

La assegnazione di una stringa implicitamente include una allocazione di memoria per i caratteri che la compongono:

var
s:string //dichiarazione variabile
begin
s:='ciao'; //assegnazione

Ora potremo fare riferimento ad essa come se fosse un array anche se in realtà non lo è.

  s[1]:='m'; //"ciao" -> "miao" 

Questo non avremo potuto farlo se prima non ci fosse stata una assegnazione, dato che lo spazio di memoria riservato alla stringa non sarebbe stato ancora allocato.

In questo articolo cercheremo di descrivere con un esempio la struttura dati di una stringa (string type). Per fare questo ci serviremo del seguente programma:

program StringType;

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  s:string;
begin
  s:='ciao';    //assegnazione variabile
  writeln(s);   //uso della variabile
  readln;
end.

Vediamo in dettaglio la prima istruzione di assegnazione:

Indirizzo  Codice         Comandi             Commenti
// s:='ciao';
0040915A   B808F94000     mov eax,$0040f908   //indirizzo destinazione
0040915F   BAB4914000     mov edx,$004091b4   //indirizzo sorgente
00409164   E80FB4FFFF     call @LStrAsg      

L'assegnazione del contenuto ad una variabile di tipo stringa avviene tramite la funzione LStrAsg, la quale prende in ingresso due parametri:

  • in EAX (destinazione) l'indirizzo di una cella di memoria che contiene l'indirizzo del primo carattere della stringa
    EAX-> s -> s.stringa_destinazione[1]
  • in EDX (sorgente) l'indirizzo del primo carattere della stringa da inserire
    EDX -> stringa_sorgente[1]

Per verificare quanto detto vediamo cosa contengono le celle di memoria a cui ci riferiamo:

Indirizzo  Codice hex    Codice decodificato         
0040F908   80 C2 A1 00  00A1C280            

Indirizzo  Codice hex      ASCII
00A1C280   63 69 61 6F 00  ciao.

Indirizzo  Codice hex      ASCII
004091B4   63 69 61 6F 00  ciao.

Il primo indirizzo è  la variabile che ho dichiarato, esso rimarrà vuoto fino a che non gli verrà assegnato il contenuto.
Il contenuto è  il puntatore alla struttura dati che contiene la stringa da inserire.

Giusto per essere chiari il tipo "stringa" in memoria è un record composto da diversi campi, e solo l'ultimo dei quali contiene la stringa vera e propria. Quindi per ogni stringa troviamo una prima parte composta da un record del tipo StrRec e dopo di esso inizia la stringa.

StrRec = packed record
    refCnt: Longint;
    length: Longint;
end;

Nell'esempio precedente avremo infatti :

Indirizzo  Codice          Decod. o ASCII   Commenti
00A1C278  01 00 00 00      1                ;refCnt
00A1C27C  04 00 00 00      4                ;dimensione
00A1C280  63 69 61 6F 00   ciao             ;stringa

Se proseguiamo nell'analisi del procedimento potremo osservare come questa stringa verrà utilizzata:

Indirizzo  Codice         Comandi             Commenti
// writeln(s);
00409169   A1F0A94000     mov eax,[$0040a9f0]
0040916E   8B1508F94000   mov edx,[$0040f908]
00409174   E8C3B7FFFF     call @Write0LString
00409179   E88AA4FFFF     call @WriteLn
0040917E   E8599CFFFF     call @_IOTest

quindi la variabile per il compilatore rimarrà sempre all'indirizzo 0040F908 e di fatto verrà usato come un puntatore alla stringa vera e propria.
Questa variabile viene allocata nella sezione .bss (Block Started by Symbol) che è  usata dai compilatori e linker come area dati contenente le variabili statiche che sono inizialmente impostate al valore zero (ad es. quando inizia l'esecuzione di un programma). Solitamente il program loader inizializza la memoria allocata per il bss all'atto del caricamento del programma stesso, dopo di che alle variabili verrà assegnato il rispettivo contenuto.

Il contenuto in questione invece sta in un altra sezione che è esterna al modulo che contiene il processo:

Memory map, item 31
  Address = 00900000
  Size = 00140000 (1310720.)
  Owner = 00900000 (self)
  Section =
  Contains =
  Type = Priv 00021004
  Access = RW
  Initial access = RW
  Mapped as =

Se è semplice capire i vantaggi d'avere un campo Length in una string, non è altrettanto immediato sapere a cosa serve il campo RefCnt. Questo campo contiene un numero intero e che viene chiamato Reference Counter. Tutte le operazioni di gestione di questo campo sono in carico alla funzione LStrAsg, che più o meno procede così:

Se la stringa di destinazione non è vuota allora :
1 - Decrementa il contatore della stringa di destinazione;
2 - Se se il contatore raggiunge il valore zero allora :
2.1 - Rilascia (libera) la stringa di destinazione;
3 - Se la stringa sorgente non è vuota allora :
3.1 - Incrementa il contatore della stringa sorgente;
4 - Assegna la stringa sorgente alla stringa destinazione

Tutto questo rientra in una logica di Garbage Collection, cioè più oggetti o blocchi di memoria possono essere usati da altri oggetti, per tenere traccia di questi link si usa un contatore, perciò dei dati possono essere liberati, cioè lo spazio dove risiedono può essere considerato libero, solo se questo contatore è zero, ovvero non è utilizzato da nessuno. Al contrario ogni volta che dei dati, come ad esempio una stringa viene assegnata, il suo contatore viene incrementato, ed indicherà quanti puntatori stanno facendo riferimento ad essa.

Ultimo aggiornamento ( Sabato 26 Giugno 2010 08:40 )