Passaggio di un argomento per 'indirizzo' ad una Funzione

Da Gambas-it.org - Wikipedia.

Il passaggio come argomento ad una Funzione mediante un Puntatore dell'indirizzo di memoria di una variabile automatica ovvero di un'area di memoria appositamente allocata, viene definito "passaggio per Indirizzo". [Nota 1] [Nota 2]
Per passare l'Indirizzo di memoria di una variabile, bisognerà servirsi della funzione "VarPtr()".
Per passare, invece, l'Indirizzo di memoria di un'area di memoria appositamente allocata mediante la funzione "Alloc()", si passerà come argomento la variabile di tipo Puntatore che punta alla predetta area di memoria riservata.

Analogamente il passaggio per "Indirizzo " avviene anche usando i "Vettori" e le "Strutture" (essendo le relative variabili nient'altro che Puntatori alle aree di memoria di tali Oggetti), nonché le variabili che identificano gli "Oggetti" quali istanze di Classi.

Il passaggio degli argomenti per indirizzo consente di eliminare l'istruzione Return, e di ritornare, così, il valore dalla Funzione chiamata alla routine principale chiamante attraverso il parametro medesimo di tipo Puntatore della Funzione.
Avere un riferimento all'oggetto è molto utile in fase di passaggio tramite parametri tra un Metodo e l'altro. La modifica dell'oggetto fa sì che venga mantenuta in uscita dal Metodo, eliminando così l'obbligo del ritorno di un valore.

E' appena il caso di ricordare che il passaggio di valori per "Indirizzo " a una sotto-procedura/funzione, non crea una copia dell'Oggetto, o comunque del tipo, passato, come avviene invece nel passaggio per "Valore ", ove si va ad occupare un'altra area di memoria (per generare la copia) di dimensioni pari a quella occupata dall'Oggetto (o dal tipo di valore) passato.
Va da sé che con il passaggio per "Valore ", avendo due copie uguali, avremo due indirizzi di memoria che si riferiscono ovviamente a due aree di memoria riservate automaticamente di uguale dimensione, ...e quindi un consumo doppio delle risorse (ossia della memoria necessaria per il passaggio dei dati).
Il passaggio per "Indirizzo " fa in modo che la modifica avvenga direttamente all'indirizzo di memoria dell'Oggetto (o del tipo di valore) passato evitando così la creazione di una copia e l'occupazione di altra memoria per il medesimo dato da passare.

Per scrivere in uno dei parametri di tipo Puntatore della Funzione chiamata il risultato finale delle operazioni compiute dalla Funzione medesima, bisognerà utilizzare - come sappiamo - i Memory Stream.


Passaggio per Indirizzo usando variabili di tipo Puntatore

Esempio di passaggio per Indirizzo dell'indirizzo di memoria di una variabile dichiarata come tipo Intero, usando la funzione "VarPtr()".
L'uso della funzione "VarPtr()" va effettuato prima che il Puntatore sia passato alla sub-routine funzione.

Public Sub Main()
 
  Dim i As Integer
 
' Assegna un valore iniziale alla variabile di tipo "Intero":
  i = 1000

 ' Usa la funzione "VarPtr()" prima della chiamata della sub-routine funzione:
  p = VarPtr(i)

' Passa mediante la funzione "VarPtr()" l'indirizzo di memoria della variabile di tipo "Intero":
  Funzione(p)
   
  Print i
  
End


Private Function Funzione(p As Pointer)
 
 Dim st As Stream
    
' Ottiene una variabile "Stream" dal "Puntatore" mediante i "Memory Stream":
 st = Memory p For Write
' Scrive nel flusso dell'area di memoria, puntata dalla variabile "p", il risultato di una moltiplicazione:
 Write #st, Int@(p) * 1000 As Integer
' Chiude il flusso di dati verso l'area di memoria:
 st.Close
 
End

Esempio di passaggio per Indirizzo di una variabile di tipo Puntatore che punta ad una piccola area di memoria opportunamente allocata con la funzione "Alloc()".
Il Puntatore va creato con la funzione "Alloc()" prima che esso sia passato alla sub-routine funzione.

Public Sub Main()
 
  Dim p As Pointer
 
' Allochiamo un'area di memoria prima che il suo "Puntatore" sia passato alla sub-routine funzione:
  p = Alloc(SizeOf(gb.Byte), 2)
   
' Passiamo la variabile di tipo "Puntatore" alla Funzione secondaria:
  Funzione(p)
   
' Dereferenziamo il "Puntatore", per vedere il valore presente nel primo byte dell'area riservata puntata:
  Print Byte@(p)
   
' Libera la memoria precedentemente allocata e si assicura che il "Puntatore" non punti a un indirizzo rilevante di memoria:
  Free(p)
  p = 0
  
End


Private Function Funzione(po As Pointer)
 
  Dim st As Stream

' Creiamo una variabile di tipo "Stream" mediante i "Memory Stream", al fine di poter scrivere all'interno dell'area di memoria allocata:
  st = Memory po For Write
  
' Scriviamo un valore di tipo "Byte" nel primo byte dell'area di memoria allocata:
  Write #st, 199 As Byte
  
' Chiudiamo il flusso di dati:
  st.Close
  
End


Passaggio per Indirizzo usando variabili di tipo vettoriale

Usando direttamente la variabile di tipo array nel passaggio per Indirizzo

Di seguito un semplice esempio usando un vettore, adeguatamente creato nella funzione principale chiamante:

 Public Sub Main()
 
  Dim vettore As New Integer[4]
  Dim i As Integer

  For i = 0 To vettore.Max
     vettore[i] = i + 1
  Next
  
  Funzione(vettore)   ' Passaggio per "Indirizzo"
  
  For Each i in vettore
    Print i
  Next
  
 End
 
 
Private Function Funzione(ii As Integer[])   ' Passaggio per "Indirizzo"
  
  Dim i As Integer
 
  For i = 0 To ii.Max
     ii[i] = ii[i] * 100
  Next
  
End

Usando nel passaggio per Indirizzo la Proprietà ".Data" della variabile di tipo array

Public Sub Main()

 Dim vettore As New Integer[4]
 Dim i As Integer

 Funzione(vettore.Data)   ' Passaggio per "Indirizzo"

 For Each i In vettore
   Print i
 Next

End


Private Function Funzione(data As Pointer)   ' Passaggio per "Indirizzo"

 Dim i As Integer

 For i = 0 To 3 * SizeOf(gb.Integer) Step SizeOf(gb.Integer)
    Int@(data + i) = i + 1000
 Next

End


Passaggio per Indirizzo usando variabili di tipo Struttura

Di seguito un semplice esempio usando una variabile di tipo Struttura, adeguatamente creata nella funzione principale chiamante:

 Public Struct STRUTTURA
   b As Byte
   c As Short
   i As Integer
 End Struct
 
 
 Public Sub Main()
 
  Dim stra As New STRUTTURA
  
  With stra
    .b = 100
    .c = 2000
    .i = 30000
  End With
  
  Funzione(stra)   ' Passaggio per "Indirizzo"
  
  With stra
    Print .b
    Print .c
    Print .i
  End With
  
 End

 
Private Function Funzione(st As STRUTTURA)
  
  With st
    .b = st.b * 2
    .c = st.c * 2
    .i = st.i * 2
  End With
  
 End


Note

[1] In Gambas elementi complessi come gli Oggetti vengono sempre passati come Riferimento, ovvero l'indirizzo di memoria in cui si trovano. Riguardo invece alle normali variabili di tipi base (Byte, Short, Integer/String/Long ecc.) queste non sono Oggetti ma "valori" ben precisi, che vengono passati come "Valore", e non come locazione di memoria. Questa differenza implica che, per quanto riguarda gli Oggetti, ogni modifica al suo contenuto viene mantenuta, mentre per le variabili, dato che ne viene passata una copia del valore, queste vengono perse all'uscita della stessa funzione che le gestisce.
Il concetto di parametro passato "per Riferimento" (ByRif) in Gambas, fa sì che la funzione prende in ingresso l'indirizzo di memoria in cui è depositato il valore, e non ne crea una copia. Ciò consente, come per gli Oggetti, di ritrovarsi la variabile di base modificata anche dopo l'uscita dalla funzione.

[2] Su questo argomento vedere anche la seguente pagina della WIKI: Passaggio per indirizzo