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()":

Public Sub Main()
 
  Dim i As Integer
 
' Assegnamo un valore iniziale alla variabile di tipo "Intero":
  i = 1000
   
' Passiamo mediante la funzione "VarPtr()" l'indirizzo di memoria della variabile di tipo "Intero":
  Funzione(VarPtr(i))
   
  Print i
  
End

Private Function Funzione(p As Pointer)
 
 Dim st As Stream
    
' Otteniamo una variabile "Stream" dal "Puntatore" mediante i "Memory Stream":
 st = Memory p For Write
' Scriviamo nel flusso dell'area di memoria, puntata dalla variabile "p", il risultato di una moltiplicazione:
 Write #st, Int@(p) * 1000 As Integer
' Chiudiamo 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()":

Public Sub Main()
 
  Dim p As Pointer
 
' Allochiamo un'area di memoria:
  p = Alloc(SizeOf(gb.Byte), 2)
   
' Passiamo la viariabile 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 di memoria rilevante:
  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

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[])
  
  Dim i As Integer
 
  For i = 0 To ii.Max
     ii[i] = ii[i] * 100
  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