Scrivere nell'area di memoria puntata da un Puntatore
Come sappiamo, i Puntatori ci consentono, anche in Gambas, di operare all'interno di un'area di memoria allocata.
Indice
Uso dei Memory Stream
In particolare, in Gambas non è possibile scrivere i dati direttamente nella variabile di tipo Puntatore, ma si deve far uso della risorsa degli Stream.
La procedura al riguardo prevede la creazione di un flusso di dati mediante la risorsa dei Memory Stream. Tale flusso viene gestito con un particolare tipo di dati chiamato appunto Stream, nel quale si effettuerà la scrittura dei dati (come sappiamo è possibile anche la lettura di dati da un Puntatore).
Avvenuta la scrittura dei dati nell'area di memoria puntata dal Puntatore, si continuerà a operare con il Puntatore medesimo (ad esempio ad una funzione esterna verrà passato il Puntatore referenziato, e non la variabile di tipo Stream utilizzata per la scrittura !). Va comunque ricordato che anche ogni operazione successiva di Scrittura, che vorrà effettuarsi mediante la risorsa Memory Stream sul Puntatore referenziato, andrà effettuata - come già visto - utilizzando il tipo di dati Stream.
Mostriamo di seguito un semplice esempio:
Public Sub Main() Dim p As Pointer Dim st As Stream Dim b As Byte Dim i As Integer ' Con la funzione "Alloc()" alloca - ad esempio - 8 byte di memoria, che sarà puntata da un "Puntatore": p = Alloc(SizeOf(gb.Byte), 8) ' Con la risorsa "Memory Stream" genera una variabile di tipo "Stream", che consentirà di scrivere nell'area di memoria riservata puntata dal "Puntatore": st = Memory p For Write ' Scrive a titolo esemplificativo alcuni dati di tipo "Byte" nell'area di memoria riservata: For b = 1 To 8 Write #st, b * 10 As Byte Next ' Verifica il risultato in console: For i = 0 To 7 Print Byte@(p + i) Next ' In fine viene chiuso il flusso "Stream": st.Close ' Libera la memoria precedentemente allocata e si assicura che il Puntatore non punti a un indirizzo di memoria rilevante: Free(p) p = 0 End
Uso delle funzioni Byte@(), Short@()...etc...
Con la versione "09adb643" (Master) di Gambas, le funzioni "Byte@()", "Short@()" e le altre analoghe, che svolgevano il solo compito di dereferenziare una variabile di tipo Puntatore, hanno assunto anche la capacità di assegnare un valore a un'area di memoria, puntata da un Puntatore, secondo il tipo di dati al quale tali la rispettiva funzione fa riferimento. [nota 1]
Questa nuova facoltà non vale per la funzione "String@()".
La loro sintassi in tal caso è (ad esempio usando la funzione "Int@()" ):
Int@(Puntatore) = valore
In sostanza, se prima di tale versione di Gambas le suddette funzioni operavano soltanto in lettura dati da un'area di memoria, ora esse operano - diciamo - anche in scrittura dati in un'area di memoria.
La scrittura dei valori avviene secondo l'Ordine dei Byte della macchina.
Mostriamo un esempio pratico, in cui la variabile di tipo Puntatore è stata ottenuta con la funzione "VarPtr()":
Public Sub Main() Dim i As Integer Dim p As Pointer p = VarPtr(i) Int@(p) = 222 Print Int@(p) Int@(p) = 44444 Print Int@(p) End
In questo secondo esempio, invece, la variabile di tipo Puntatore è stata ottenuta con la funzione "Alloc()":
Public Sub Main() Dim p As Pointer p = Alloc(SizeOf(gb.Integer), 1) Int@(p) = 222 Print Int@(p) Int@(p) = 44444 Print Int@(p) Free(p) End
Ovviamente funziona anche l'aritmetica dei Puntatori. Bisognerà ricordare che la scrittura dei dati nell'area di memoria allocata avverrà secondo l'Ordine dei Byte posseduto dalla macchina che si sta usando.
Public Sub Main() Dim p As Pointer p = Alloc(SizeOf(gb.Byte), 8) Byte@(p + 2) = 222 Print Int@(p) Short@(p + 5) = 1234 Print Int@(p + 4) Free(p) End
In questo terzo esempio si assegneranno dei valori a una variabile vettoriale di tipo "Byte[]" attraverso il Puntatore restituito dalla sua Proprietà ".Data":
Public Sub Main() Dim bb As New Byte[4] Dim i As Integer For i = 0 To 3 Byte@(bb.Data + i) = 10 + i Next ' Verifica il risultato: For i = 0 To 3 Print bb[i] Next End
Qui invece si assegneranno i singoli caratteri di una stringa a un vettore di tipo "Byte[]":
Public Sub Main() Dim bb As New Byte[6] Dim s As String = "Gambas" Dim i As Integer For i = 0 To bb.Max Byte@(bb.Data + i) = Asc(s, i + 1) ' oppure: Asc(s[i, 1]) Next ' Verifica il risultato: Print bb.ToString(0, bb.Count) End
oppure direttamente un'intera stringa usando la funzione "Pointer@()":
Public Sub Main() Dim bb As New Byte[6] Dim s As String = "Gambas" Dim p As Pointer p = Alloc(s) ' Il "Puntatore" all'area contenente la stringa va anch'esso dereferenziato con la funzione "Pointer@()": Pointer@(bb.Data) = Pointer@(p) Print bb.ToString(0, bb.Count) Free(p) End
Da questo esempio si deduce che se si deve assegnare una variabile di tipo Puntatore alla funzione "Pointr@()", la variabile di tipo Puntatore va dereferenziata preliminarmente con la funzione "Pointer@()".
In questo quarto esempio si assegneranno dei valori ai membri dichiarati di una variabile di tipo Struttura.
Si otterrà innanzitutto il Puntatore all'area di memoria, ove sono memorizzati i dati dei membri dichiarati della Struttura, mediante il Metodo "Object.Address()".
Va ricordato che nell'area di memoria della Struttura i dati vengono memorizzati a cominciare dal 25° byte (numero indice offset: 24).
Public Struct STRUTTURA b As Byte c As Short i As Integer End Struct Public Sub Main() Dim p As Pointer Dim st As New STRUTTURA p = Object.Address(st) Byte@(p + 24) = 111 Short@(p + 24 + SizeOf(gb.Short)) = 1111 Int@(p + 24 + SizeOf(gb.Integer)) = 111111 ' Verifica il risultato: With st Print .b Print .c Print .i End With End
In questo quinto esempio si assegnano i valori dei colori dei 4 pixel, che costituiscono un Oggetto di tipo "Image":
Public Sub Button1_Click() Dim im As Image im = New Image(2, 2, Color.White, Image.Standard) Int@(im.Data) = &FF0000FF& Int@(im.Data + 4) = &FFFF0000& Int@(im.Data + 8) = &FF00FF00& Int@(im.Data + 12) = &FF00FFFF& im.Save("/tmp/immagine.png", 100) End
Uso della funzione esterna di C "memset()"
Per scrivere nell'area di memoria riservata puntata da una variabile di tipo Puntatore, volendo, è possibile utilizzare anche la funzione esterna del linguaggio C memset().
Mostriamo un semplice esempio:
Library "libc:6" ' void *memset (void *__s, int __c, size_t __n) ' Set N bytes of S to C. Private Extern memset(s As Pointer, c As Integer, n As Long) Public Sub Main() Dim p As Pointer Dim i As Integer p = Alloc(SizeOf(gb.Byte), 8) ' Scrive nell'area di memoria puntata dal "Puntatore": For i = 0 To 7 memset(p + i, (i + 1) * 10, 1) Next ' Verifica il risultato in console: For i = 0 To 7 Print Byte@(p + i) Next ' Libera la memoria precedentemente allocata e si assicura che il Puntatore non punti a un indirizzo di memoria rilevante: Free(p) p = 0 End
Note
[1] Vedi al riguardo: