Differenze tra le versioni di "Gestire con un Puntatore le Strutture esterne"
(Creata pagina con 'Come descritto anche in questa pagina, dedicata alla gestione ''in modo sicuro'' delle Strutture esterne, la lettura e la scritt...') |
|||
(97 versioni intermedie di uno stesso utente non sono mostrate) | |||
Riga 1: | Riga 1: | ||
− | Come descritto anche | + | Come descritto anche nella pagina dedicata alla [[Gestire_con_sicurezza_le_Strutture_esterne|gestione ''in modo sicuro'' delle Strutture esterne]], la lettura e la scrittura effettuate su ''Strutture'' esterne di una certa complessità è sempre molto difficile. Tanto è che, nella pagina segnalata nel collegamento, si è proposto <SPAN Style="text-decoration:underline">nei casi più difficili e ostici</span> anche di creare un'apposita libreria condivisa esterna, scritta in C e avente estensione .so, nella quale gestire con le risorse del linguaggio C i membri di tali ''Strutture'' molto complesse sia in lettura che in scrittura. |
− | La difficoltà della gestione delle ''Strutture'' molto complesse, appartenenti a librerie condivise .so esterne, consiste nel fatto che, tentando di riprodurre nel progetto Gambas tali ''Strutture, il linguaggio non riesce sempre a gestire la coerenza dei necessari ''allineamenti'' tra alcuni particolari membri costituenti la ''Struttura'' medesima. | + | La difficoltà della [[Gestione_delle_Strutture_esterne|gestione delle ''Strutture'']] molto complesse, appartenenti a librerie condivise .so esterne, consiste nel fatto che, tentando di riprodurre nel progetto Gambas tali ''Strutture'', il linguaggio non riesce sempre a gestire la coerenza dei necessari ''allineamenti'' tra alcuni particolari membri costituenti la ''Struttura'' medesima. |
− | Ad ogni modo alcune ''Strutture'' complesse possono essere affrontate tentandone la lettura e la scrittura attraverso un ''Puntatore'' | + | Ad ogni modo alcune ''Strutture'' complesse possono essere affrontate tentandone la lettura e la scrittura attraverso un ''Puntatore''. All'interno dell'area puntata da tale ''Puntatore'' si scorrerà con i ''Memory Stream'' (per la lettura e/o scrittura), oppure semplicemente sommando o sottrando unità al valore, che rappresenta l'indirizzo di memoria contenuto dalla variabile ''Puntatore'', e dereferenziando il ''Puntatore'' medesimo con le apposite funzioni di dereferenziazione previste da Gambas (ovviamente solo per la lettura). |
+ | <BR>Ad esempio: | ||
+ | Byte@(p '''+''' ''n'') As Byte | ||
− | + | Byte@(p '''-''' ''n'') As Byte | |
− | |||
− | + | ==La questione dell'indicizzazione e dell'''Allineamento'' dei membri della ''Struttura'' esterna== | |
− | <BR>Poniamo il caso che la ''Struttura'' presente nella libreria condivisa | + | Il problema principale che sorge nel leggere e nello scrivere nell'area riservata di una ''Struttura'' esterna, scritta in C, è quello della posizione dei suoi membri all'interno di detta area di memoria, ed in particolare la loro collocazione in riferimento al numero d'indice dei byte dell'area. |
− | struct STRUTTURA | + | |
+ | L'ostacolo maggiore è rappresentato dagli eventuali necessari ''allineamenti'' <SPAN Style="text-decoration:underline">fra due membri contingui</span> (ossia fra un membro e quello che lo segue immediatamente in successione all'interno della ''Struttura'' esterna), dei quali si dovrà tenere rigidamente conto durante la lettura e la scrittura dei dati nello spostamento lungo l'area puntata dal ''Puntatore''. | ||
+ | |||
+ | La questione dell'''Allineamento'' della memoria occupata fra due membri continui è di fondamentale importanza, poiché la conoscenza della sua dinamica e delle sue regole ci consente di conoscere e determinare esattamente all'interno dell'area di memoria puntata dal ''Puntatore'' il byte d'indice ove iniziano i byte che rappresentano il valore contenuto da un membro della ''Struttura'' esterna. | ||
+ | |||
+ | In particolare, sorvolando sull'ovvia circostanza che i valori del primo membro della ''Struttura'' esterna sono leggibili e scrivibili a cominciare dal primo byte (numero d'indice 0 "''zero''") dell'area puntata dal ''Puntatore'', v'è da precisare che per i dati - <SPAN Style="text-decoration:underline">appartenenti ai membri successivi al primo membro</span> - ''il primo byte del dato di ogni membro'' (come già detto, <B>successivo al primo membro</b> della ''Struttura'' esterna) ''deve trovarsi al numero di indice (offset) uguale al valore della dimensione (quantità di byte di memoria occupati) del suo "tipo" di dato, oppure - se tale indice è già occupato da altro membro - al numero d'indice più prossimo che risulti essere <SPAN Style="text-decoration:underline">multiplo</span> di quel suo predetto valore''. | ||
+ | <BR>Se invece la quantità occupata dal tipo di dati di un membro è inferiore o uguale a quella occupata dal membro precedente, esso è posto al numero d'indice immediatamente libero dopo il membro che lo precede. | ||
+ | |||
+ | Più brevemente possiamo riassumere che ciascun dato <SPAN Style="text-decoration:underline">successivo al primo membro</span> è allineato in un offset di memoria che è uguale alla sua lunghezza in memoria, o che ne è il multiplo (se tale indice è già occupato). | ||
+ | |||
+ | Qualora questa situazione non ricorra ''direttamente'' in base alla casuale disposizione dei ''membri/tipi'', si rende necessario l'<I>allineamento</i> della memoria occupata fra i due membri. Più in particolare l'<I>allineamento</i> della memoria occupata all'interno fra due membri della ''Struttura'' si rende sempre necessario, quando ricorrono ''contestualmente'' le due seguenti situazioni: | ||
+ | |||
+ | 1) il secondo membro è di un ''tipo'' <SPAN Style="text-decoration:underline">maggiore</span> (ossia occupa una quantità di memoria superiore) | ||
+ | rispetto al tipo del primo membro; | ||
+ | <BR>2) il secondo membro <SPAN Style="text-decoration:underline">non</span> ha inizio ad un byte di numero d'indice ''uguale'' o ''multiplo'' del valore della quantità di memoria occupata dal ''tipo'' del membro stesso. | ||
+ | |||
+ | Ad ogni modo la regola dell'indicizzazione dei membri di una ''Struttura'' esterna, scritta in C, all'interno dell'area di memoria viene comunque rispettata e perseguita - quando necessario - con l'''allineamento'' appena visto. | ||
+ | |||
+ | Una regola, ad ogni modo, assoluta, di cui possiamo sempre tenere conto tranquillamente, è che un membro non comincerà <SPAN Style="text-decoration:underline">mai</span> all'interno dell'area di memoria della ''Struttura'' da un byte d'indice dispari, eccezion fatta per il tipo "''char''" che può essere posizionato ad un byte d'indice sia pari che dispari. | ||
+ | |||
+ | |||
+ | Possiamo dire riguardo alla eventualità dell'''allineamento'' di memoria all'interno di due membri che: | ||
+ | * se la quantità di memoria occupata dall'inizio dell'area riservata sino al primo - compreso - dei due membri in questione è <SPAN Style="text-decoration:underline">minore</span> o <SPAN Style="text-decoration:underline">uguale</span> alla quantità di memoria occupata ordinariamente dal ''tipo'' a cui appartiene il secondo membro, allora la posizione del secondo membro nell'indice interno dell'area di memoria della ''Struttura'' esterna corrisponde al valore della quantità di memoria occupata ordinariamente dal ''tipo'' del secondo membro predetto. Va precisato che nel caso in cui la quantità di memoria occupata è uguale alla ''dimensione'' del ''tipo'' a cui appartiene il secondo membro, allora non v'è ''allineamento''. | ||
+ | Volendo esplicitare questa regola con un algoritmo in linguaggio C, nominato il primo membro ''Tipo_1'' ed il secondo membro (di cui si deve trovare l'indice di posizionamento) ''Tipo_2'', avremo: | ||
+ | if (memoria_occupata <= sizeof(Tipo_2) | ||
+ | posizione_indice_Tipo_2 = sizeof(Tipo_2) | ||
+ | * se, invece, la quantità di memoria occupata dall'inizio dell'area riservata sino al primo - compreso - dei due membri in questione è <SPAN Style="text-decoration:underline">superiore</span> alla quantità di memoria occupata ordinariamente dal ''tipo'' a cui appartiene il secondo membro, allora la questione è più complessa, e possiamo riassumerla esponendola nel seguente codice Gambas, capace di trovare la posizione del secondo membro nell'indice dell'area di memoria della ''Struttura'' esterna in ordine ai tipi ''char'', ''short'', ''int'', ''float'', ''long'' e ''double'', compresi i relativi ''array'' anche multidimensionali, ''puntatori'' ed ''array'' di ''puntatore'' anche multidimensionali. | ||
+ | Public Struct MEMBRI | ||
+ | tipo As String | ||
+ | dim_tipo As Byte | ||
+ | dim_tot As Short | ||
+ | End Struct | ||
+ | |||
+ | |||
+ | Public Sub Main() | ||
+ | |||
+ | Dim mm As New MEMBRI[] | ||
+ | Dim ss As New String[] | ||
+ | Dim memOcc, PosInd As Short | ||
+ | Dim b, lu, j As Byte | ||
+ | |||
+ | <FONT Color=gray>' ''Inseriamo i membri (tipo ed identificatore) della Struttura esemplificativa:''</font> | ||
+ | ss.Push("char Char") | ||
+ | ss.Push("short Short") | ||
+ | ss.Push("float Float") | ||
+ | ss.Push("int Int") | ||
+ | ss.Push("char * Puntatore") | ||
+ | ss.Push("char Vettore[3]") | ||
+ | ss.Push("char Vettore_multidim[2][3][2]") | ||
+ | ss.Push("long Long") | ||
+ | ss.Push("char * Vettore_Puntatore[2]") | ||
+ | ss.Push("char* Vettore_Puntatore_multidim[2][3]") | ||
+ | ss.Push("double Double") | ||
+ | |||
+ | |||
+ | For b = 0 To ss.Max | ||
+ | If Len(ss[b]) > lu Then lu = Len(ss[b]) | ||
+ | Next | ||
+ | |||
+ | Calcola(ss, mm) | ||
+ | |||
+ | |||
+ | Print "Variabile"; Space(lu); "Posizione indice Memoria occupata 'sino' a quel membro\n" | ||
+ | |||
+ | memOcc = mm[0].dim_tot | ||
+ | Print mm[0].tipo; Space(lu - Len(ss[0]) + 11); "0 "; memOcc;; "byte" | ||
+ | |||
+ | For b = 1 To ss.Max | ||
+ | |||
+ | If memOcc <= mm[b].dim_tipo Then PosInd = mm[b].dim_tipo | ||
+ | |||
+ | If memOcc > mm[b].dim_tipo Then | ||
+ | |||
+ | If mm[b - 1].dim_tipo >= mm[b].dim_tipo Then PosInd = memOcc | ||
+ | |||
+ | If mm[b - 1].dim_tipo < mm[b].dim_tipo Then | ||
+ | If memOcc Mod mm[b].dim_tipo = 0 Then | ||
+ | PosInd = memOcc | ||
+ | Else | ||
+ | PosInd = mm[b].dim_tipo * ((memOcc \ mm[b].dim_tipo) + 1) | ||
+ | Endif | ||
+ | Endif | ||
+ | |||
+ | Endif | ||
+ | |||
+ | memOcc = PosInd + mm[b].dim_tot | ||
+ | |||
+ | Select Case Len(CStr(PosInd)) | ||
+ | Case 1 | ||
+ | j = 11 | ||
+ | Case 2 | ||
+ | j = 10 | ||
+ | Case 3 | ||
+ | j = 9 | ||
+ | End Select | ||
+ | |||
+ | Print mm[b].tipo; Space(lu - Len(ss[b]) + j); PosInd; " "; memOcc;; "byte" | ||
+ | |||
+ | Next | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Private Function Calcola(ss As String[], mm As MEMBRI[]) | ||
+ | |||
+ | Dim m As MEMBRI | ||
+ | Dim b As Byte | ||
+ | Dim s As String | ||
+ | |||
+ | For b = 0 To ss.Max | ||
+ | m = New MEMBRI | ||
+ | Select Case Left(ss[b], 3) | ||
+ | Case "cha" | ||
+ | m.tipo = ss[b] | ||
+ | m.dim_tipo = 1 | ||
+ | Case "sho" | ||
+ | m.tipo = ss[b] | ||
+ | m.dim_tipo = 2 | ||
+ | Case "int" | ||
+ | m.tipo = ss[b] | ||
+ | m.dim_tipo = 4 | ||
+ | Case "flo" | ||
+ | m.tipo = ss[b] | ||
+ | m.dim_tipo = 4 | ||
+ | Case "lon" | ||
+ | m.tipo = ss[b] | ||
+ | m.dim_tipo = 8 | ||
+ | Case "dou" | ||
+ | m.tipo = ss[b] | ||
+ | m.dim_tipo = 8 | ||
+ | End Select | ||
+ | |||
+ | If InStr(ss[b], Chr(42)) > 0 Then m.dim_tipo = 8 | ||
+ | |||
+ | m.dim_tot = m.dim_tipo | ||
+ | |||
+ | If InStr(ss[b], "]") > 0 Then | ||
+ | For Each s In Scan(ss[b], "*" & String$(Split(ss[b], "[").Max, "\\[*\\]")) | ||
+ | If IsNumber(s) Then m.dim_tot *= Val(s) | ||
+ | Next | ||
+ | Endif | ||
+ | |||
+ | mm.Push(m) | ||
+ | Next | ||
+ | |||
+ | End | ||
+ | Vediamo un esempio con maggior dettaglio: | ||
+ | <BR>Poniamo il caso che la ''Struttura'' presente nella libreria condivisa esterna sia così composta: | ||
+ | struct STRUTTURA { | ||
char c; | char c; | ||
int i; | int i; | ||
Riga 20: | Riga 169: | ||
<BR>il <SPAN Style="text-decoration:underline">primo</span> membro di tipo ''char'' occupa 1 byte di memoria (come in Gambas il tipo ''Byte''); | <BR>il <SPAN Style="text-decoration:underline">primo</span> membro di tipo ''char'' occupa 1 byte di memoria (come in Gambas il tipo ''Byte''); | ||
<BR>il secondo membro di tipo ''int'' occupa 4 byte di memoria (come in Gambas il tipo ''Integer''); | <BR>il secondo membro di tipo ''int'' occupa 4 byte di memoria (come in Gambas il tipo ''Integer''); | ||
− | <BR>il terzo membro di tipo ''char *'' (''Puntatore'') occupa 8 byte di memoria (come in Gambas il tipo ''Pointer''); | + | <BR>il terzo membro di tipo ''char *'' (''Puntatore'') occupa 8 byte (nei sistemi a 64bit) di memoria (come in Gambas il tipo ''Pointer''); |
− | <BR>il terzo membro di tipo ''short'' occupa 2 byte di memoria (come in Gambas il tipo ''Short''). | + | <BR>il quarto membro di tipo ''short'' occupa 2 byte di memoria (come in Gambas il tipo ''Short''). |
+ | |||
+ | Tali membri sono dislocati all'interno dell'area di memoria occupata dalla loro ''Struttura'' secondo il seguente ragionamento e calcolo: | ||
+ | * il valore contenuto dal primo membro, ovviamente, ha inizio nell'area di memoria dal byte di indice 0 (il primo membro coincide sempre con l'indice ''zero''). | ||
+ | Il primo membro occupa, come abbiamo visto solo un byte di memoria nell'area riservata, pertanto il prossimo byte da verificare è quello di indice 1. | ||
+ | *Il secondo membro, però, essendo un Intero (''int'') ha una dimensione di 4 byte (occupa, cioè, 4 byte di memoria per rappresentare il valore assegnatogli). Pertanto, si dovrà verificare se il numero dell'indice (''offset'') disponibile (abbiamo visto che ora è uguale a 1) coincide con il valore della dimensione del tipo ''int'', oppure con un suo multiplo (8, 12, 16, 20, etc). Notiamo che 1 (l'indice attuale) non coincide con il 4 (dimensione di memoria occupata dal tipo ''int'') né con un suo multiplo. Pertanto, sposteremo in avanti l'indice di un altro byte per la verifica. L'indice 1 resterà con valore zero ed entrando così a far parte dell'<I>allineamento</i>. | ||
+ | |||
+ | Ora verifichiamo il nuovo indice disponibile: 2, che come il precedente non corrisponde al numero 4 (ricordiamo che v'è da piazzare un Intero !) né ad un multiplo di 4. Quindi spostiamo ancora in avanti il puntatore interno dell'indice (''offset''), come fatto appena prima, e lasceremo a zero anche il terzo byte (ossia quello di indice 2 !), entrando così anch'esso a far parte dell'<I>allineamento</i>. | ||
+ | |||
+ | Ora verifichiamo il nuovo indice disponibile: 3, che come il precedente non corrisponde al numero 4 (ricordiamo che v'è da piazzare un Intero !) né ad un multiplo di 4. Quindi spostiamo ancora in avanti il puntatore interno dell'indice, e lasceremo a zero anche il quarto byte (ossia quello di indice 3), entrando così anch'esso a far parte dell'<I>allineamento</i>. | ||
+ | |||
+ | Ora verifichiamo il nuovo indice disponibile: 4. Questo numero corrisponde esattamente con il valore della dimensione di un Intero (''int''). Quindi il primo byte del valore assegnato a tale Intero sarà posto al byte di indice 4 (dunque al 5° byte), e occuperà ovviamente ben 4 byte, spostando così il puntatore interno al byte di indice 8 dell'area di memoria della ''Struttura'' esterna. | ||
+ | |||
+ | * Possiamo passare ad individuare il terzo membro, che essendo una variabile ''Puntatore'' (''char *'') occupa 8 byte (nei sistemi a 64bit). Esso occuperà (nei sistemi a 64bit) comunque 8 byte di memoria, qualunque sia la dimensione dell'area di memoria riservata, che nel codice sarà stata allocata con la funzione ''malloc()'', alla quale esso punta. Dobbiamo, quindi, verificare se l'attuale numero dell'indice disponibile, al quale ci siamo sin'ora spostati, corrisponde a 8 o ad un multiplo di 8 (8, 16, 24, 32, etc). | ||
+ | Abbiamo che l'indice attuale è 8 (9° byte dell'area di memoria della ''Struttura'' esterna) che corrisponde esattamente con il valore della dimensione di un ''Puntatore''. Pertanto il primo byte del valore contenuto dalla variabile Puntatore ''char *'' (ossia l'indirizzo di memoria puntata da questa variabile di tipo ''Puntatore'') è posto al byte di indice 8 (9° byte), ed occuperà in totale ben 8 byte, spostando così il puntatore interno al byte di indice 16 dell'area di memoria della ''Struttura'' esterna. | ||
+ | |||
+ | * Possiamo passare ad individuare il quarto membro, che essendo una variabile ''Short'' (''short'') occupa 2 byte. Dobbiamo, quindi, verificare se l'attuale numero dell'indice disponibile, al quale ci siamo sin'ora spostati, corrisponde a 2 o ad un multiplo di 8 (4, 6, 8, 10, 12, 14, 16, etc). | ||
+ | Abbiamo che l'indice attuale è 16 (17° byte dell'area di memoria della ''Struttura'' esterna) che corrisponde ad un ''multiplo'' del valore della dimensione di uno ''short''. Pertanto il primo byte del valore contenuto dalla variabile ''short'' è posto al byte di indice 16 (17° byte), ed occuperà in totale ben 2 byte. | ||
+ | |||
+ | La dimensione totale della ''Struttura'' raggiungerà il valore più prossimo pari ad un multiplo di 8 (dato che è presente un ''Puntatore'' fra i membri). In tal caso: 24. Quindi la ''Struttura'' "in quanto tale" occuperà complessivamente 24 byte di memoria. | ||
+ | |||
+ | |||
+ | Mostriamo di seguito un esempio di gestione della ''Struttura'' in linguaggio C appena descritta sopra. In particolare, per essa scriveremo un'apposita libreria condivisa .so esterna, mediante la quale sarà utilizzata con un'applicazione Gambas principale la predetta ''Struttura'' in tutti i suoi membri sia scivendovi che leggendovi valori. | ||
+ | Private Extern Valorizza(stP As Pointer) As Pointer In "/tmp/libadhoc" | ||
+ | |||
+ | |||
+ | Public Sub Main() | ||
+ | |||
+ | Dim p, rit, rit2 As Pointer | ||
+ | Dim bb As Byte[] = [1, 2, 3, 4] | ||
+ | Dim st1, st2 As Stream | ||
+ | Dim b As Byte | ||
+ | Dim c As Short | ||
+ | Dim i As Integer | ||
+ | |||
+ | CreaSo() | ||
+ | |||
+ | <FONT Color=gray>' ''Per usare il "Puntatore", è necessario allocare un'area di memoria di dimensione pari alla quantità di memoria occupata dalla "Struttura" esterna in C da gestire:''</font> | ||
+ | p = Alloc(SizeOf(gb.Byte), 24) | ||
+ | |||
+ | <FONT Color=gray>' ''Scriviamo dei valori dell'area di memoria allocata, che sarà passata successivamente alla funzione esterna per assegnare detti valori alla "Struttura" esterna da gestire:''</font> | ||
+ | st1 = Memory p For Write | ||
+ | Write #st1, 9 As Byte | ||
+ | Seek #st1, 4 | ||
+ | Write #st1, 9999 As Integer | ||
+ | Write #st1, bb.Data As Pointer | ||
+ | Write #st1, 99 As Short | ||
+ | st1.Close | ||
+ | |||
+ | <FONT Color=gray>' ''Viene invocata la funzione esterna per l'assegnazione dei valori scritti nell'area di memoria allocata e puntata dal "Puntatore", e per leggere successivamente i dati dalla "Struttura" medesima:''</font> | ||
+ | rit = Valorizza(p) | ||
+ | |||
+ | <FONT Color=gray>' ''La lettura dei dati presenti nell'area di memoria puntata dal "Puntatore" passato dalla funzione esterna, può avvenire con la dereferenziazione mediante i "Memory Stream"...:''</font> | ||
+ | st1 = Memory rit For Read | ||
+ | Read #st1, b | ||
+ | Print "Valore del 1° membro (char): ";; b | ||
+ | Seek #st1, 4 | ||
+ | Read #st1, i | ||
+ | Print "Valore del 2° membro (int): ";; i | ||
+ | |||
+ | Read #st1, rit2 | ||
+ | st2 = Memory rit2 For Read | ||
+ | Read #st2, b | ||
+ | Print "\nValore indice 0 del 3° membro (char *): ";; b | ||
+ | Read #st2, b | ||
+ | Print "Valore indice 1 del 3° membro (char *): ";; b | ||
+ | Read #st2, b | ||
+ | Print "Valore indice 2 del 3° membro (char *): ";; b | ||
+ | Read #st2, b | ||
+ | Print "Valore indice 3 del 3° membro (char *): ";; b | ||
+ | st2.Close | ||
+ | Read #st1, c | ||
+ | Print "\nValore del 4° membro (short): ";; c | ||
+ | |||
+ | <FONT Color=gray>' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' | ||
+ | ' ''...oppure scorrendo all'interno del "Puntatore" e dereferenziando con le apposite funzioni di Gambas di dereferenziazione:''</font> | ||
+ | rit = Valorizza(p) | ||
+ | |||
+ | Print "Valore del 1° membro (char): ";; Byte@(rit) | ||
+ | |||
+ | rit2 = rit + 4 | ||
+ | Print "Valore del 2° membro (int): ";; Int@(rit2) | ||
+ | |||
+ | rit2 = rit + 8 | ||
+ | Print "\nValore indice 0 del 3° membro (char *): ";; Byte@(Pointer@(rit2)) | ||
+ | Print "Valore indice 1 del 3° membro (char *): ";; Byte@(Pointer@(rit2) + 1) | ||
+ | Print "Valore indice 2 del 3° membro (char *): ";; Byte@(Pointer@(rit2) + 2) | ||
+ | Print "Valore indice 3 del 3° membro (char *): ";; Byte@(Pointer@(rit2) + 3) | ||
+ | |||
+ | rit2 = rit + 16 | ||
+ | Print "\nValore del 4° membro (short): ";; Short@(rit2) | ||
+ | |||
+ | <FONT Color=gray>' ''Dealloca l'area di memoria precedentemente riservata e si assicura che il "Puntatore" non punti a un indirizzo rilevante di memoria:''</font> | ||
+ | Free(p) | ||
+ | p = 0 | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Private Procedure CreaSo() | ||
+ | |||
+ | Dim s As String | ||
+ | |||
+ | <FONT Color=gray>' ''Impostiamo il codice sorgente C della futura libreria codivisa .so:''</font> | ||
+ | s = "#include <stdlib.h>\n" & | ||
+ | "#include <stdio.h>\n\n" & | ||
+ | "struct STRUTTURA {\n" & | ||
+ | "char c;\n" & | ||
+ | "int i;\n" & | ||
+ | "char * p;\n" & | ||
+ | "short s;\n};\n\n" & | ||
+ | "struct STRUTTURA Ra;\n\n" & | ||
+ | "struct STRUTTURA * Valorizza(struct STRUTTURA *St) {\n" & | ||
+ | "St->c *= 10;\n" & | ||
+ | "St->i *= 10;\n" & | ||
+ | "St->p[0] *= 10;\n" & | ||
+ | "St->p[1] *= 10;\n" & | ||
+ | "St->p[2] *= 10;\n" & | ||
+ | "St->p[3] *= 10;\n" & | ||
+ | "St->s *= 10;\n" & | ||
+ | "Ra = *St; <FONT Color=gray>/* Assegna i dati della variabile St alla omogenea variabile Ra */</font>\n" & | ||
+ | "St = (struct STRUTTURA *) 0; <FONT Color=gray>/* Pulisce ed azzera i membri della Struttura */</font>\n" & | ||
+ | "return &Ra;\n}" | ||
+ | |||
+ | File.Save("/tmp/libadhoc.c", s) | ||
+ | |||
+ | <FONT Color=gray>' ''Crea la libreria condivisa .so:''</font> | ||
+ | Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared -fPIC" Wait | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | ===Passare a una funzione esterna un Puntatore di Puntatore a una Struttura=== | ||
+ | Se nel codice, scritto in C, è previsto il passaggio ad una funzione esterna di un ''Puntatore'' di ''Puntatore'' ad una ''Struttura'', allora in Gambas si potrà utilizzare la funzione nativa "''VarPtr( )''", ponendo come suo argomento una variabile di tipo ''Puntatore'' (senza allocazione di memoria mediante la funzione nativa di Gambas ''Alloc( )'' ). | ||
+ | |||
+ | Pertanto se nel codice C è così scritto: | ||
+ | Funzione_Esterna( &variabile_struttura, ...) | ||
+ | allora in Gambas avremo: | ||
+ | Public Struct STRUTTURA | ||
+ | ... | ||
+ | ... | ||
+ | End Struct | ||
+ | |||
+ | |||
+ | Public Sub Main() | ||
+ | |||
+ | Dim p As Pointer | ||
+ | Dim sr As STRUTTURA | ||
+ | |||
+ | Funzione_Esterna(<FONT Color=#B22222>VarPtr(p)</font>, .....) | ||
+ | |||
+ | <FONT Color=gray>' ''Quindi si assegnerà tale variabile di tipo ''Pointer'' alla variabile di tipo ''Struttura'' da gestire in Gambas:''</font> | ||
+ | sr = p | ||
+ | |||
+ | ... | ||
+ | |||
+ | |||
+ | ===Gestione se un membro è ''Array'' o una ''Matrice''=== | ||
+ | Se uno o più membri sono variabili vettoriali (''Array'') con il numero di elementi ''definito'', allora - per la determinazione della memoria occupata dal ''vettore'' - si conta il numero degli elementi indicato nel ''vettore'' moltiplicato per la quantità di memoria occupata dal ''tipo'' dell'''Array''. | ||
+ | <BR>Esempio: | ||
+ | int i[4] --> 4 x 4 (ossia 4 elementi moltiplicato 4 byte di memoria occupati dal tipo ''int'') = 16 byte complessivi | ||
+ | Così se ad esempio si ha un ''Puntatore'': | ||
+ | int *[4] --> 4 * 8 (ossia 4 elementi moltiplicato 8 byte di memoria occupati dal tipo ''Puntatore'') = 32 byte complessivi | ||
+ | Calcolo analogo in caso di ''Array'' multidimensionale (''Matrice''): si moltiplicheranno fra loro i valori che indicano gli elementi di ciascuna dimensione della ''Matrice'', il risultato sarà in fine moltiplicato per per la quantità di memoria occupata dal ''tipo'' della ''Matrice''. | ||
+ | <BR>Esempio: | ||
+ | int i[4][3] --> (4 x 3) x 4 (ossia 4 elementi della prima dimensione moltiplicato 3 elementi della seconda dimensione, moltiplicato i 4 byte di memoria occupati dal tipo ''int'') = 48 byte complessivi | ||
+ | In modo simile se la ''Matrice'' ha più di due dimensioni. | ||
+ | |||
+ | |||
+ | In ordine, invece, all'indicizzazione dell'array all'interno dell'area di memoria della ''Struttura'' esterna, il membro array o matrice si comporterà come consueto per il ''tipo'' di dato al quale esso appartiene. | ||
+ | <BR>Infatti, nel seguente esempio in C: | ||
+ | struct STRU { | ||
+ | char c; | ||
+ | char cc[100]; | ||
+ | }; | ||
+ | la ''Struttura'' esterna occupa 101 byte; laddove il primo membro si pone ''necessariamente'' all'indice zero, ed il secondo - essendo un ''char'' - si pone all'indice dispari 1 (immediatamente successivo al primo membro). | ||
+ | <BR>Invece in quest'altra ''Struttura'' esterna: | ||
+ | struct STRU { | ||
+ | char c; | ||
+ | int cc[100]; | ||
+ | }; | ||
+ | la ''Struttura'' esterna occupa 404 byte; laddove il primo membro si pone ''necessariamente'' all'indice zero, ed il secondo - essendo un ''int'' - si pone all'indice 4 (che è uguale alla dimensione del suo ''tipo''). | ||
+ | |||
+ | |||
+ | Mostriamo di seguito un altro esempio più complesso, in cui il codice sorgente in C della libreria condivisa .so esterna è il seguente: | ||
+ | #include <stdlib.h> | ||
+ | |||
+ | |||
+ | struct AAA { | ||
+ | char c; | ||
+ | short s; | ||
+ | int i; | ||
+ | char * p[5]; | ||
+ | char * p1, * p2; | ||
+ | int n; | ||
+ | }; | ||
+ | |||
+ | struct AAA a; | ||
+ | |||
+ | |||
+ | struct AAA * Estrae_Valori() { | ||
+ | |||
+ | a.c = 0x0F; | ||
+ | a.s = 0x0400; | ||
+ | a.i = 0x00444444; | ||
+ | |||
+ | a.p[0] = (char *) malloc(4); | ||
+ | a.p[1] = (char *) malloc(4); | ||
+ | a.p[2] = (char *) malloc(4); | ||
+ | a.p[3] = (char *) malloc(4); | ||
+ | a.p[4] = (char *) malloc(4); | ||
+ | |||
+ | a.p[0][0] = 0x01; | ||
+ | a.p[0][1] = 0x02; | ||
+ | a.p[0][2] = 0x03; | ||
+ | a.p[0][3] = 0x04; | ||
+ | |||
+ | a.p[1][0] = 11; | ||
+ | a.p[1][1] = 11; | ||
+ | a.p[1][2] = 11; | ||
+ | a.p[1][3] = 11; | ||
+ | |||
+ | a.p[2][0] = 22; | ||
+ | a.p[2][1] = 22; | ||
+ | a.p[2][2] = 22; | ||
+ | a.p[2][3] = 22; | ||
+ | |||
+ | a.p[3][0] = 33; | ||
+ | a.p[3][1] = 33; | ||
+ | a.p[3][2] = 33; | ||
+ | a.p[3][3] = 33; | ||
+ | |||
+ | a.p[4][0] = 44; | ||
+ | a.p[4][1] = 44; | ||
+ | a.p[4][2] = 44; | ||
+ | a.p[4][3] = 44; | ||
+ | |||
+ | a.p1 = (char *) malloc(4); | ||
+ | a.p1[0] = 99; | ||
+ | a.p1[1] = 99; | ||
+ | a.p1[2] = 99; | ||
+ | a.p1[3] = 99; | ||
+ | |||
+ | a.p2 = "efgh"; | ||
+ | |||
+ | a.n = 10000; | ||
+ | |||
+ | return &a; | ||
+ | |||
+ | } | ||
+ | che ritorna alla funzione chiamante di Gambas l'indirizzo di memoria della sua ''Struttura''. | ||
+ | |||
+ | Nel codice Gambas, che segue, si andrà a leggere da un ''Puntatore'', che ha raccolto l'indirizzo di memoria della ''Struttura'' esterna ritornata dalla libreria esterna, e con i ''Memory Stream'' i valori dei vari membri della predetta ''Struttura'' | ||
+ | <BR>Si ponga particolare attenzione al calcolo per l'eventuale necessario allineamento dei membri. | ||
+ | Private Extern Estrae_Valori() As Pointer In "/tmp/stru" | ||
+ | |||
+ | |||
+ | Public Sub Main() | ||
+ | |||
+ | Dim p, po, p1, p2 As Pointer | ||
+ | Dim st, re, s1, s2 As Stream | ||
+ | Dim b As Byte | ||
+ | Dim j As Short | ||
+ | Dim t As String | ||
+ | |||
+ | <FONT Color=gray>' ''Generiamo la libreria esterna, scritta in C, contenente la "Struttura":''</font> | ||
+ | Shell "gcc -o /tmp/stru.so " & Application.Path &/ "stru.c -shared -fPIC" Wait | ||
+ | |||
+ | p = Estrae_Valori() | ||
+ | |||
+ | st = Memory p For Read | ||
+ | |||
+ | Print Read #st As Byte <FONT Color=gray>' ''Legge il 1° membro (char) che occupa 1 byte''</font> | ||
+ | |||
+ | Seek #st, 2 <FONT Color=gray>' ''Ci spostiamo nel rispetto dell'allineamento dei membri''</font> | ||
+ | Print Read #st As Short <FONT Color=gray>' ''Legge il 2° membro (short) che occupa 2 byte''</font> | ||
+ | |||
+ | Print Read #st As Integer <FONT Color=gray>' ''Legge il 3° membro (int) che occupa 4 byte''</font> | ||
+ | |||
+ | |||
+ | Read #st, po <FONT Color=gray>' ''Legge il 3° membro: il puntatore (che occupa 8 byte) della prima dimensione di char * p[5]''</font> | ||
+ | |||
+ | re = Memory po For Read <FONT Color=gray>' ''Andiamo a dereferenziare il "Puntatore" per leggere nell'area di memoria da esso puntata''</font> | ||
+ | For j = 0 To 3 | ||
+ | Read #re, b | ||
+ | Print j, b | ||
+ | Next | ||
+ | Seek #re, 32 <FONT Color=gray>' ''Per ciascuna dimensione si salta di 32 byte avanti''</font> | ||
+ | For j = Seek(re) To Seek(re) + 3 | ||
+ | Read #re, b | ||
+ | Print j, b | ||
+ | Next | ||
+ | Seek #re, 64 <FONT Color=gray>' ''Per ciascuna dimensione si salta di 32 byte avanti''</font> | ||
+ | For j = Seek(re) To Seek(re) + 3 | ||
+ | Read #re, b | ||
+ | Print j, b | ||
+ | Next | ||
+ | Seek #re, 96 <FONT Color=gray>' ''Per ciascuna dimensione si salta di 32 byte avanti''</font> | ||
+ | For j = Seek(re) To Seek(re) + 3 | ||
+ | Read #re, b | ||
+ | Print j, b | ||
+ | Next | ||
+ | Seek #re, 128 <FONT Color=gray>' ''Per ciascuna dimensione si salta di 32 byte avanti''</font> | ||
+ | For j = Seek(re) To Seek(re) + 3 | ||
+ | Read #re, b | ||
+ | Print j, b | ||
+ | Next | ||
+ | |||
+ | re.Close | ||
+ | |||
+ | Seek #st, Seek(st) + (SizeOf(gb.Pointer) * 4) <FONT Color=gray>' ''Si moltiplica per l'indice massimo di char * p[n]''</font> | ||
+ | Read #st, p1 <FONT Color=gray>' ''Legge il 5° membro: il puntatore (che occupa 8 byte) della prima dimensione di char * p1''</font> | ||
+ | s1 = Memory p1 For Read <FONT Color=gray>' ''Andiamo a dereferenziare il "Puntatore" per leggere nell'area di memoria da esso puntata''</font> | ||
+ | For j = 0 To 3 | ||
+ | Read #s1, b | ||
+ | Print j, b | ||
+ | Next | ||
+ | s1.Close | ||
+ | |||
+ | Read #st, p2 <FONT Color=gray>' ''Legge il 6° membro: il puntatore (che occupa 8 byte) della prima dimensione di char * p2''</font> | ||
+ | s2 = Memory p2 For Read <FONT Color=gray>' ''Andiamo a dereferenziare il "Puntatore" per leggere nell'area di memoria da esso puntata''</font> | ||
+ | Read #s2, t | ||
+ | Print t | ||
+ | s2.Close | ||
+ | |||
+ | Print Read #st As Integer <FONT Color=gray>' ''Legge il 7° membro (int) che occupa 4 byte''</font> | ||
+ | |||
+ | st.Close | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | ===Gestione di un membro di una Struttura che fa riferimento a una ''Union''=== | ||
+ | Se uno o più membri della ''Struttura'' fanno riferimento ad una ''Union'', allora si calcola ''solo'' il membro della ''Union'' che occupa la quantità maggiore di memoria rispetto agli altri membri. Insomma nella ''Union'' - con riferimento alla determinazione della sua dimensione - prevale il membro che in assoluto (o per tipo di dati o perché è un array con dimensione superiore ai ''tipi'' di ciascun altro membro della ''Union'') occupa la maggiore quantità di memoria rispetto agli altri membri della ''Union'' medesima. | ||
+ | |||
+ | Però, riguardo alla posizione d'indice nell'area riservata di memoria si fa riferimento al ''tipo'' di dato più grande presente tra i membri della ''Union''. | ||
+ | |||
+ | Se due membri contingui della ''Struttura'' sono due ''Union'', e se l'ultimo membro della prima delle due ''Union'' è di tipo comunque ''diverso'' dal tipo dal tipo del primo membro della seconda ''Union'', va effettuato l'allineamento fra le due ''Union'' secondo i consueti criteri. | ||
+ | |||
+ | |||
+ | ===Gestione se un membro fa riferimento ad altra ''Struttura'' o ad una ''Struttura annidata''=== | ||
+ | Se un membro della ''Struttura'' fa riferimento ad un'altra ''Struttura'' secondaria esterna alla ''Struttura'' principale, ovvero è rappresentato da una ''Struttura'' definita e sviluppata esplicitamente all'interno della ''Struttura'' principale (''Struttura annidata''), si effettua il calcolo - nelle modalità consuete - della quantità di memoria occupata complessivamente dai membri della ''Struttura'' esterna secondaria ovvero nel secondo caso dai membri della ''Struttura annidata''. | ||
+ | |||
+ | Se il membro, che fa riferimento ad una ''Struttura'' secondaria esterna, è invece dichiarato come ''Puntatore'', allora esso occuperà semplicemente 8 byte, ossia la quantità di byte occupati ordinariamente dalla variabile di tipo ''Puntatore''. | ||
+ | |||
+ | Nel caso il membro, che si riferisce ad altra ''Struttura'' secondaria o ad una ''Struttura annidata'', sia un ''Array'' con numero definito di elementi, la quantità che occupa la ''Struttura'' secondaria o la ''Struttura annidata'' va moltiplicata per il numero espresso degli elementi dell''Array''. | ||
+ | |||
+ | Riguardo al numero d'indice del byte di inizio del primo membro della ''Struttura'' esterna secondaria o di quella ''annidata'' all'interno d'area di memoria della ''Struttura'' principale, bisogna prendere in considerazione il ''tipo'' di valore che occupa maggior memoria fra i ''tipi'' di valore ai quali appartengono i vari membri costituenti la ''Struttura'' predetta. Si confronterà, quindi, tale ''tipo'' maggiore della ''Struttura'' secondaria o di quella ''annidata'' con il ''tipo'' di appartenenza del membro della ''Struttura'' principale immediatamente ad esso precedente. Se la quantità di memoria occupata dal membro ''maggiore'' della ''Struttura'' secondaria o di quella ''annidata'' è superiore alla quantità di memoria occupata dal membro della ''Struttura'' principale immediatamente ad esso precedente, allora i dati relativi al valore contenuto dal primo membro della ''Struttura'' secondaria o di quella ''annidata'' avranno inizio ad un numero d'indice - se libero - uguale alla quantità di memoria occupata dal tipo di appartenenza del membro ''maggiore'' (individuato come già descritto), altrimenti - se tale byte è già occupato dai dati del membro precedente - quelli si porranno al numero d'indice più vicino che sia un ''multiplo'' della quantità di memoria occupata dal tipo di appartenenza del predetto membro ''maggiore''. | ||
+ | |||
+ | Questa regola va sempre applicata, anche se il primo membro della ''Struttura'' innestata o esterna dichiarata è di dimensione inferiore a quello, individuato come il più grande fra i membri della ''Struttura'' secondaria esterna od innestata per l'individuazione della posizione esatta del primo membro predetto. | ||
+ | |||
+ | Mostriamo due codici esemplificativi. | ||
+ | |||
+ | '''Primo esempio''' | ||
+ | <BR>Nel seguente codice la ''Struttura'' secondaria, innestata in quella principale, è dichiarata con una ordinaria varibile di tipo ''Struttura'': | ||
+ | #include <stdio.h> | ||
+ | |||
+ | |||
+ | struct STRUTTURA { | ||
+ | char c; | ||
+ | short s; | ||
+ | int i; | ||
+ | <FONT Color=#B22222>struct Innesto { | ||
+ | short Is; | ||
+ | long Il; | ||
+ | } <B>In</b>;</font> | ||
+ | } St; | ||
+ | |||
+ | |||
+ | int main() { | ||
+ | |||
+ | printf("%ld\n", sizeof(St)); | ||
+ | |||
+ | return (0); | ||
+ | |||
+ | } | ||
+ | In tal caso la dimensione complessiva della ''Struttura'' principale (che nell'esempio abbiamo denominato con l'identificativo: ''STRUTTURA'') è pari a 24 byte, così distribuiti: | ||
+ | * 1 byte per il primo membro (essendo di tipo ''char'') - inizia dall'indice 0; | ||
+ | * 1 byte per il necessario allineamento; | ||
+ | * 2 byte per il secondo membro (essendo di tipo ''short'') - inizia dall'indice 2; | ||
+ | * 4 byte per il terzo membro (essendo di tipo ''int'') - inizia dall'indice 4; | ||
+ | * 2 byte per il primo membro della ''Struttura'' innestata (essendo di tipo ''short''). '''Essendo però il successivo membro un ''long'' (quindi "maggiore" del tipo ''short''), allora i valori di tale membro ''short'' avranno inzio dal byte d'indice 8'''; | ||
+ | * 6 byte per il necessario allineamento; | ||
+ | * 8 byte per il secondo membro della ''Struttura'' innestata (essendo di tipo ''long'') - inizia dall'indice 16. | ||
+ | La ''Struttura'' C complessivamente occupa 24 byte. | ||
+ | |||
+ | |||
+ | '''Secondo esempio''' | ||
+ | <BR>In questo secondo codice, invece, la ''Struttura'' secondaria, innestata in quella principale, è dichiarata mediante una varibile di tipo ''Puntatore'': | ||
+ | #include <stdio.h> | ||
+ | |||
+ | |||
+ | struct STRUTTURA { | ||
+ | char c; | ||
+ | short s; | ||
+ | int i; | ||
+ | <FONT Color=#B22222>struct Innesto { | ||
+ | short Is; | ||
+ | long Il; | ||
+ | } <B>*In</b>;</font> | ||
+ | } St; | ||
+ | |||
+ | |||
+ | int main() { | ||
+ | |||
+ | printf("%ld\n", sizeof(St)); | ||
+ | |||
+ | return (0); | ||
+ | |||
+ | } | ||
+ | In tal caso la dimensione complessiva della ''Struttura'' principale è pari a 16 byte, così distribuiti: | ||
+ | * 1 byte per il primo membro (essendo di tipo ''char''); | ||
+ | * 1 byte per il necessario allineamento; | ||
+ | * 2 byte per il secondo membro (essendo di tipo ''short''); | ||
+ | * 4 byte per il terzo membro (essendo di tipo ''int''); | ||
+ | * 8 byte (nei sistemi a 64bit) per il quarto membro (essendo una variabile di tipo ''Puntatore''). | ||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | ===Determinazione definitiva della quantità di memoria occupata da una ''Struttura''=== | ||
+ | Per la determinazione finale e complessiva della quantità di memoria occupata dalla ''Struttura'' da effettuare <SPAN Style="text-decoration:underline">dopo</span> l'ultimo membro, può risultare necessario individuare la quantità di memoria residua aggiuntiva, da occupare, tale da raggiungere il valore del byte <SPAN Style="text-decoration:underline">multiplo</span> immediatamente successivo (il più prossimo) della quantità di memoria occupata dal ''tipo'' <SPAN Style="text-decoration:underline">più grande</span> (in quantità di memoria occupata) fra quelli utilizzati come membri nella ''Struttura'' medesima. | ||
+ | Vediamo un esempio: | ||
+ | struct STRUTTURA_1 { | ||
+ | long l; <FONT Color=blue>/* Questo è il tipo "maggiore" rispetto agli altri, perché occupa più memoria (8 byte) rispetto agli altri. */</font> | ||
+ | char c1; | ||
+ | short sh; | ||
+ | char c2; | ||
+ | int i; | ||
+ | }; | ||
+ | Ebbene, la ''Struttura'' sembrerebbe occupare complessivamente 20 byte. Però il tipo di valore che occupa maggiore memoria è il tipo ''long'' (8 byte) rispetto agli altri. Il valore 20, che sembrerebbe essere la quantità di memoria occupata dalla ''Struttura'' non è esatto, poiché il valore 20 non è un multiplo di ''sizeof(long)'' = 8. Pertanto, per sapere quanta memoria in questo caso occupa complessivamente e correttamente la ''Struttura'', si dovrà tenere conto dell'<I>allineamento</i> sino al valore successivo più vicino in ordine crescente a 20, ossia 24. Quindi la ''Struttura'' in esempio occupa 24 byte di memoria. | ||
+ | |||
+ | Vediamo un altro esempio simile al precedente: | ||
+ | struct STRUTTURA_2 { | ||
+ | long l; <FONT Color=blue>/* Questo è il tipo "maggiore" rispetto agli altri, perché occupa più memoria (8 byte) rispetto agli altri. */</font> | ||
+ | char c1; | ||
+ | short sh; | ||
+ | char c2; | ||
+ | int i1; | ||
+ | int i2; | ||
+ | int i3; | ||
+ | }; | ||
+ | La ''Struttura'' sembra occupare 28 byte; ma 28 non è un multiplo del valore della quantità di memoria occupata dal tipo più grande presente nella ''Struttura'': il ''long''. Pertanto, l'<I>allineamento</i> porterà ad occupare complessivamente una quantità di memoria aggiuntiva terminale sino al valore multiplo più prossimo in ordine crescente al 28, ossia 32. Quindi la ''Struttura'' in esempio occupa 32 byte di memoria. | ||
+ | Altro esempio: | ||
+ | struct STRUTTURA_3 { | ||
+ | char c1; | ||
+ | int i; <FONT Color=blue>/* Questo è il tipo "maggiore" rispetto agli altri, perché occupa più memoria (4 byte) rispetto agli altri. */</font> | ||
+ | short sh; | ||
+ | char c2; | ||
+ | }; | ||
+ | La ''Struttura'' sembra occupare 11 byte; ma 11 non è un multiplo del valore della quantità di memoria occupata dal tipo più grande presente nella ''Struttura'': il ''int''. Pertanto, l'<I>allineamento</i> porterà ad occupare complessivamente una quantità di memoria aggiuntiva terminale sino al valore multiplo più prossimo in ordine crescente al 11, ossia 12. Quindi la ''Struttura'' in esempio occupa 12 byte di memoria. | ||
− | < | + | |
+ | ===Esempio riepilogativo=== | ||
+ | Nel seguente esempio in C cerchiamo di riepilogare tutti i casi principali visti, comprensivo di indicizzazione ed allineamenti eventuali: | ||
+ | struct esterna { | ||
+ | short se; <FONT Color=blue>// 24 -> 25 + 6 di allineamento</font> | ||
+ | long le; <FONT Color=blue>// 32 -> 39</font> | ||
+ | }; | ||
+ | |||
+ | |||
+ | struct STRUTTURA { | ||
+ | char c; <FONT Color=blue>// 0 + 3 di allineamento</font> | ||
+ | int i; <FONT Color=blue>// 4 -> 7</font> | ||
+ | struct innestata { | ||
+ | char ci; <FONT Color=blue>// 8 + 1 di allineamento</font> | ||
+ | short si; <FONT Color=blue>// 10 -> 11</font> | ||
+ | } inn; | ||
+ | int i2; <FONT Color=blue>// 12 -> 15</font> | ||
+ | int i3; <FONT Color=blue>// 16 -> 19</font> | ||
+ | struct esterna es; <FONT Color=blue>// (24 ----> 39)</font> | ||
+ | short s; <FONT Color=blue>// 40 -> 41 + 6 di allineamento</font> | ||
+ | struct esterna * es2; <FONT Color=blue>// 48 -> 55</font> | ||
+ | char * p; <FONT Color=blue>// 56 -> 71</font> | ||
+ | int i3b; <FONT Color=blue>// 72 -> 75 + 4 di allineamento</font> | ||
+ | union { | ||
+ | short us; | ||
+ | int ui; | ||
+ | long ul; | ||
+ | char uc[12]; <FONT Color=blue>// 80 -> 91 + 4 di allineamento</font> | ||
+ | }; | ||
+ | short v[8]; <FONT Color=blue>// 96 -> 103</font> | ||
+ | struct innestata_2 { | ||
+ | char ci; | ||
+ | short si; | ||
+ | } * inn2; <FONT Color=blue>// 104 -> 111</font> | ||
+ | char c2; <FONT Color=blue>// 112 + 1 di allineamento</font> | ||
+ | short v2[2][3]; <FONT Color=blue>// 114 ->125 + 2 di allineamento</font> | ||
+ | long l; <FONT Color=blue>// 128 -> 135</font> | ||
+ | }; | ||
+ | |||
+ | <FONT Color=blue>// TOT. 136 byte di memoria occupata dalla Struttura "STRUTTURA"</font> | ||
+ | |||
+ | |||
+ | int main() { | ||
+ | |||
+ | printf("%ld\n", sizeof(struct STRUTTURA)); | ||
+ | |||
+ | return (0); | ||
+ | |||
+ | } |
Versione attuale delle 14:12, 14 nov 2024
Come descritto anche nella pagina dedicata alla gestione in modo sicuro delle Strutture esterne, la lettura e la scrittura effettuate su Strutture esterne di una certa complessità è sempre molto difficile. Tanto è che, nella pagina segnalata nel collegamento, si è proposto nei casi più difficili e ostici anche di creare un'apposita libreria condivisa esterna, scritta in C e avente estensione .so, nella quale gestire con le risorse del linguaggio C i membri di tali Strutture molto complesse sia in lettura che in scrittura.
La difficoltà della gestione delle Strutture molto complesse, appartenenti a librerie condivise .so esterne, consiste nel fatto che, tentando di riprodurre nel progetto Gambas tali Strutture, il linguaggio non riesce sempre a gestire la coerenza dei necessari allineamenti tra alcuni particolari membri costituenti la Struttura medesima.
Ad ogni modo alcune Strutture complesse possono essere affrontate tentandone la lettura e la scrittura attraverso un Puntatore. All'interno dell'area puntata da tale Puntatore si scorrerà con i Memory Stream (per la lettura e/o scrittura), oppure semplicemente sommando o sottrando unità al valore, che rappresenta l'indirizzo di memoria contenuto dalla variabile Puntatore, e dereferenziando il Puntatore medesimo con le apposite funzioni di dereferenziazione previste da Gambas (ovviamente solo per la lettura).
Ad esempio:
Byte@(p + n) As Byte
Byte@(p - n) As Byte
Indice
- 1 La questione dell'indicizzazione e dell'Allineamento dei membri della Struttura esterna
- 1.1 Passare a una funzione esterna un Puntatore di Puntatore a una Struttura
- 1.2 Gestione se un membro è Array o una Matrice
- 1.3 Gestione di un membro di una Struttura che fa riferimento a una Union
- 1.4 Gestione se un membro fa riferimento ad altra Struttura o ad una Struttura annidata
- 1.5 Determinazione definitiva della quantità di memoria occupata da una Struttura
- 1.6 Esempio riepilogativo
La questione dell'indicizzazione e dell'Allineamento dei membri della Struttura esterna
Il problema principale che sorge nel leggere e nello scrivere nell'area riservata di una Struttura esterna, scritta in C, è quello della posizione dei suoi membri all'interno di detta area di memoria, ed in particolare la loro collocazione in riferimento al numero d'indice dei byte dell'area.
L'ostacolo maggiore è rappresentato dagli eventuali necessari allineamenti fra due membri contingui (ossia fra un membro e quello che lo segue immediatamente in successione all'interno della Struttura esterna), dei quali si dovrà tenere rigidamente conto durante la lettura e la scrittura dei dati nello spostamento lungo l'area puntata dal Puntatore.
La questione dell'Allineamento della memoria occupata fra due membri continui è di fondamentale importanza, poiché la conoscenza della sua dinamica e delle sue regole ci consente di conoscere e determinare esattamente all'interno dell'area di memoria puntata dal Puntatore il byte d'indice ove iniziano i byte che rappresentano il valore contenuto da un membro della Struttura esterna.
In particolare, sorvolando sull'ovvia circostanza che i valori del primo membro della Struttura esterna sono leggibili e scrivibili a cominciare dal primo byte (numero d'indice 0 "zero") dell'area puntata dal Puntatore, v'è da precisare che per i dati - appartenenti ai membri successivi al primo membro - il primo byte del dato di ogni membro (come già detto, successivo al primo membro della Struttura esterna) deve trovarsi al numero di indice (offset) uguale al valore della dimensione (quantità di byte di memoria occupati) del suo "tipo" di dato, oppure - se tale indice è già occupato da altro membro - al numero d'indice più prossimo che risulti essere multiplo di quel suo predetto valore.
Se invece la quantità occupata dal tipo di dati di un membro è inferiore o uguale a quella occupata dal membro precedente, esso è posto al numero d'indice immediatamente libero dopo il membro che lo precede.
Più brevemente possiamo riassumere che ciascun dato successivo al primo membro è allineato in un offset di memoria che è uguale alla sua lunghezza in memoria, o che ne è il multiplo (se tale indice è già occupato).
Qualora questa situazione non ricorra direttamente in base alla casuale disposizione dei membri/tipi, si rende necessario l'allineamento della memoria occupata fra i due membri. Più in particolare l'allineamento della memoria occupata all'interno fra due membri della Struttura si rende sempre necessario, quando ricorrono contestualmente le due seguenti situazioni:
1) il secondo membro è di un tipo maggiore (ossia occupa una quantità di memoria superiore)
rispetto al tipo del primo membro;
2) il secondo membro non ha inizio ad un byte di numero d'indice uguale o multiplo del valore della quantità di memoria occupata dal tipo del membro stesso.
Ad ogni modo la regola dell'indicizzazione dei membri di una Struttura esterna, scritta in C, all'interno dell'area di memoria viene comunque rispettata e perseguita - quando necessario - con l'allineamento appena visto.
Una regola, ad ogni modo, assoluta, di cui possiamo sempre tenere conto tranquillamente, è che un membro non comincerà mai all'interno dell'area di memoria della Struttura da un byte d'indice dispari, eccezion fatta per il tipo "char" che può essere posizionato ad un byte d'indice sia pari che dispari.
Possiamo dire riguardo alla eventualità dell'allineamento di memoria all'interno di due membri che:
- se la quantità di memoria occupata dall'inizio dell'area riservata sino al primo - compreso - dei due membri in questione è minore o uguale alla quantità di memoria occupata ordinariamente dal tipo a cui appartiene il secondo membro, allora la posizione del secondo membro nell'indice interno dell'area di memoria della Struttura esterna corrisponde al valore della quantità di memoria occupata ordinariamente dal tipo del secondo membro predetto. Va precisato che nel caso in cui la quantità di memoria occupata è uguale alla dimensione del tipo a cui appartiene il secondo membro, allora non v'è allineamento.
Volendo esplicitare questa regola con un algoritmo in linguaggio C, nominato il primo membro Tipo_1 ed il secondo membro (di cui si deve trovare l'indice di posizionamento) Tipo_2, avremo:
if (memoria_occupata <= sizeof(Tipo_2) posizione_indice_Tipo_2 = sizeof(Tipo_2)
- se, invece, la quantità di memoria occupata dall'inizio dell'area riservata sino al primo - compreso - dei due membri in questione è superiore alla quantità di memoria occupata ordinariamente dal tipo a cui appartiene il secondo membro, allora la questione è più complessa, e possiamo riassumerla esponendola nel seguente codice Gambas, capace di trovare la posizione del secondo membro nell'indice dell'area di memoria della Struttura esterna in ordine ai tipi char, short, int, float, long e double, compresi i relativi array anche multidimensionali, puntatori ed array di puntatore anche multidimensionali.
Public Struct MEMBRI tipo As String dim_tipo As Byte dim_tot As Short End Struct Public Sub Main() Dim mm As New MEMBRI[] Dim ss As New String[] Dim memOcc, PosInd As Short Dim b, lu, j As Byte ' Inseriamo i membri (tipo ed identificatore) della Struttura esemplificativa: ss.Push("char Char") ss.Push("short Short") ss.Push("float Float") ss.Push("int Int") ss.Push("char * Puntatore") ss.Push("char Vettore[3]") ss.Push("char Vettore_multidim[2][3][2]") ss.Push("long Long") ss.Push("char * Vettore_Puntatore[2]") ss.Push("char* Vettore_Puntatore_multidim[2][3]") ss.Push("double Double") For b = 0 To ss.Max If Len(ss[b]) > lu Then lu = Len(ss[b]) Next Calcola(ss, mm) Print "Variabile"; Space(lu); "Posizione indice Memoria occupata 'sino' a quel membro\n" memOcc = mm[0].dim_tot Print mm[0].tipo; Space(lu - Len(ss[0]) + 11); "0 "; memOcc;; "byte" For b = 1 To ss.Max If memOcc <= mm[b].dim_tipo Then PosInd = mm[b].dim_tipo If memOcc > mm[b].dim_tipo Then If mm[b - 1].dim_tipo >= mm[b].dim_tipo Then PosInd = memOcc If mm[b - 1].dim_tipo < mm[b].dim_tipo Then If memOcc Mod mm[b].dim_tipo = 0 Then PosInd = memOcc Else PosInd = mm[b].dim_tipo * ((memOcc \ mm[b].dim_tipo) + 1) Endif Endif Endif memOcc = PosInd + mm[b].dim_tot Select Case Len(CStr(PosInd)) Case 1 j = 11 Case 2 j = 10 Case 3 j = 9 End Select Print mm[b].tipo; Space(lu - Len(ss[b]) + j); PosInd; " "; memOcc;; "byte" Next End Private Function Calcola(ss As String[], mm As MEMBRI[]) Dim m As MEMBRI Dim b As Byte Dim s As String For b = 0 To ss.Max m = New MEMBRI Select Case Left(ss[b], 3) Case "cha" m.tipo = ss[b] m.dim_tipo = 1 Case "sho" m.tipo = ss[b] m.dim_tipo = 2 Case "int" m.tipo = ss[b] m.dim_tipo = 4 Case "flo" m.tipo = ss[b] m.dim_tipo = 4 Case "lon" m.tipo = ss[b] m.dim_tipo = 8 Case "dou" m.tipo = ss[b] m.dim_tipo = 8 End Select If InStr(ss[b], Chr(42)) > 0 Then m.dim_tipo = 8 m.dim_tot = m.dim_tipo If InStr(ss[b], "]") > 0 Then For Each s In Scan(ss[b], "*" & String$(Split(ss[b], "[").Max, "\\[*\\]")) If IsNumber(s) Then m.dim_tot *= Val(s) Next Endif mm.Push(m) Next End
Vediamo un esempio con maggior dettaglio:
Poniamo il caso che la Struttura presente nella libreria condivisa esterna sia così composta:
struct STRUTTURA { char c; int i; char * p; short s; };
Laddove sostanzialmente abbiamo:
il primo membro di tipo char occupa 1 byte di memoria (come in Gambas il tipo Byte);
il secondo membro di tipo int occupa 4 byte di memoria (come in Gambas il tipo Integer);
il terzo membro di tipo char * (Puntatore) occupa 8 byte (nei sistemi a 64bit) di memoria (come in Gambas il tipo Pointer);
il quarto membro di tipo short occupa 2 byte di memoria (come in Gambas il tipo Short).
Tali membri sono dislocati all'interno dell'area di memoria occupata dalla loro Struttura secondo il seguente ragionamento e calcolo:
- il valore contenuto dal primo membro, ovviamente, ha inizio nell'area di memoria dal byte di indice 0 (il primo membro coincide sempre con l'indice zero).
Il primo membro occupa, come abbiamo visto solo un byte di memoria nell'area riservata, pertanto il prossimo byte da verificare è quello di indice 1.
- Il secondo membro, però, essendo un Intero (int) ha una dimensione di 4 byte (occupa, cioè, 4 byte di memoria per rappresentare il valore assegnatogli). Pertanto, si dovrà verificare se il numero dell'indice (offset) disponibile (abbiamo visto che ora è uguale a 1) coincide con il valore della dimensione del tipo int, oppure con un suo multiplo (8, 12, 16, 20, etc). Notiamo che 1 (l'indice attuale) non coincide con il 4 (dimensione di memoria occupata dal tipo int) né con un suo multiplo. Pertanto, sposteremo in avanti l'indice di un altro byte per la verifica. L'indice 1 resterà con valore zero ed entrando così a far parte dell'allineamento.
Ora verifichiamo il nuovo indice disponibile: 2, che come il precedente non corrisponde al numero 4 (ricordiamo che v'è da piazzare un Intero !) né ad un multiplo di 4. Quindi spostiamo ancora in avanti il puntatore interno dell'indice (offset), come fatto appena prima, e lasceremo a zero anche il terzo byte (ossia quello di indice 2 !), entrando così anch'esso a far parte dell'allineamento.
Ora verifichiamo il nuovo indice disponibile: 3, che come il precedente non corrisponde al numero 4 (ricordiamo che v'è da piazzare un Intero !) né ad un multiplo di 4. Quindi spostiamo ancora in avanti il puntatore interno dell'indice, e lasceremo a zero anche il quarto byte (ossia quello di indice 3), entrando così anch'esso a far parte dell'allineamento.
Ora verifichiamo il nuovo indice disponibile: 4. Questo numero corrisponde esattamente con il valore della dimensione di un Intero (int). Quindi il primo byte del valore assegnato a tale Intero sarà posto al byte di indice 4 (dunque al 5° byte), e occuperà ovviamente ben 4 byte, spostando così il puntatore interno al byte di indice 8 dell'area di memoria della Struttura esterna.
- Possiamo passare ad individuare il terzo membro, che essendo una variabile Puntatore (char *) occupa 8 byte (nei sistemi a 64bit). Esso occuperà (nei sistemi a 64bit) comunque 8 byte di memoria, qualunque sia la dimensione dell'area di memoria riservata, che nel codice sarà stata allocata con la funzione malloc(), alla quale esso punta. Dobbiamo, quindi, verificare se l'attuale numero dell'indice disponibile, al quale ci siamo sin'ora spostati, corrisponde a 8 o ad un multiplo di 8 (8, 16, 24, 32, etc).
Abbiamo che l'indice attuale è 8 (9° byte dell'area di memoria della Struttura esterna) che corrisponde esattamente con il valore della dimensione di un Puntatore. Pertanto il primo byte del valore contenuto dalla variabile Puntatore char * (ossia l'indirizzo di memoria puntata da questa variabile di tipo Puntatore) è posto al byte di indice 8 (9° byte), ed occuperà in totale ben 8 byte, spostando così il puntatore interno al byte di indice 16 dell'area di memoria della Struttura esterna.
- Possiamo passare ad individuare il quarto membro, che essendo una variabile Short (short) occupa 2 byte. Dobbiamo, quindi, verificare se l'attuale numero dell'indice disponibile, al quale ci siamo sin'ora spostati, corrisponde a 2 o ad un multiplo di 8 (4, 6, 8, 10, 12, 14, 16, etc).
Abbiamo che l'indice attuale è 16 (17° byte dell'area di memoria della Struttura esterna) che corrisponde ad un multiplo del valore della dimensione di uno short. Pertanto il primo byte del valore contenuto dalla variabile short è posto al byte di indice 16 (17° byte), ed occuperà in totale ben 2 byte.
La dimensione totale della Struttura raggiungerà il valore più prossimo pari ad un multiplo di 8 (dato che è presente un Puntatore fra i membri). In tal caso: 24. Quindi la Struttura "in quanto tale" occuperà complessivamente 24 byte di memoria.
Mostriamo di seguito un esempio di gestione della Struttura in linguaggio C appena descritta sopra. In particolare, per essa scriveremo un'apposita libreria condivisa .so esterna, mediante la quale sarà utilizzata con un'applicazione Gambas principale la predetta Struttura in tutti i suoi membri sia scivendovi che leggendovi valori.
Private Extern Valorizza(stP As Pointer) As Pointer In "/tmp/libadhoc" Public Sub Main() Dim p, rit, rit2 As Pointer Dim bb As Byte[] = [1, 2, 3, 4] Dim st1, st2 As Stream Dim b As Byte Dim c As Short Dim i As Integer CreaSo() ' Per usare il "Puntatore", è necessario allocare un'area di memoria di dimensione pari alla quantità di memoria occupata dalla "Struttura" esterna in C da gestire: p = Alloc(SizeOf(gb.Byte), 24) ' Scriviamo dei valori dell'area di memoria allocata, che sarà passata successivamente alla funzione esterna per assegnare detti valori alla "Struttura" esterna da gestire: st1 = Memory p For Write Write #st1, 9 As Byte Seek #st1, 4 Write #st1, 9999 As Integer Write #st1, bb.Data As Pointer Write #st1, 99 As Short st1.Close ' Viene invocata la funzione esterna per l'assegnazione dei valori scritti nell'area di memoria allocata e puntata dal "Puntatore", e per leggere successivamente i dati dalla "Struttura" medesima: rit = Valorizza(p) ' La lettura dei dati presenti nell'area di memoria puntata dal "Puntatore" passato dalla funzione esterna, può avvenire con la dereferenziazione mediante i "Memory Stream"...: st1 = Memory rit For Read Read #st1, b Print "Valore del 1° membro (char): ";; b Seek #st1, 4 Read #st1, i Print "Valore del 2° membro (int): ";; i Read #st1, rit2 st2 = Memory rit2 For Read Read #st2, b Print "\nValore indice 0 del 3° membro (char *): ";; b Read #st2, b Print "Valore indice 1 del 3° membro (char *): ";; b Read #st2, b Print "Valore indice 2 del 3° membro (char *): ";; b Read #st2, b Print "Valore indice 3 del 3° membro (char *): ";; b st2.Close Read #st1, c Print "\nValore del 4° membro (short): ";; c ' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' ...oppure scorrendo all'interno del "Puntatore" e dereferenziando con le apposite funzioni di Gambas di dereferenziazione: rit = Valorizza(p) Print "Valore del 1° membro (char): ";; Byte@(rit) rit2 = rit + 4 Print "Valore del 2° membro (int): ";; Int@(rit2) rit2 = rit + 8 Print "\nValore indice 0 del 3° membro (char *): ";; Byte@(Pointer@(rit2)) Print "Valore indice 1 del 3° membro (char *): ";; Byte@(Pointer@(rit2) + 1) Print "Valore indice 2 del 3° membro (char *): ";; Byte@(Pointer@(rit2) + 2) Print "Valore indice 3 del 3° membro (char *): ";; Byte@(Pointer@(rit2) + 3) rit2 = rit + 16 Print "\nValore del 4° membro (short): ";; Short@(rit2) ' Dealloca l'area di memoria precedentemente riservata e si assicura che il "Puntatore" non punti a un indirizzo rilevante di memoria: Free(p) p = 0 End Private Procedure CreaSo() Dim s As String ' Impostiamo il codice sorgente C della futura libreria codivisa .so: s = "#include <stdlib.h>\n" & "#include <stdio.h>\n\n" & "struct STRUTTURA {\n" & "char c;\n" & "int i;\n" & "char * p;\n" & "short s;\n};\n\n" & "struct STRUTTURA Ra;\n\n" & "struct STRUTTURA * Valorizza(struct STRUTTURA *St) {\n" & "St->c *= 10;\n" & "St->i *= 10;\n" & "St->p[0] *= 10;\n" & "St->p[1] *= 10;\n" & "St->p[2] *= 10;\n" & "St->p[3] *= 10;\n" & "St->s *= 10;\n" & "Ra = *St; /* Assegna i dati della variabile St alla omogenea variabile Ra */\n" & "St = (struct STRUTTURA *) 0; /* Pulisce ed azzera i membri della Struttura */\n" & "return &Ra;\n}" File.Save("/tmp/libadhoc.c", s) ' Crea la libreria condivisa .so: Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared -fPIC" Wait End
Passare a una funzione esterna un Puntatore di Puntatore a una Struttura
Se nel codice, scritto in C, è previsto il passaggio ad una funzione esterna di un Puntatore di Puntatore ad una Struttura, allora in Gambas si potrà utilizzare la funzione nativa "VarPtr( )", ponendo come suo argomento una variabile di tipo Puntatore (senza allocazione di memoria mediante la funzione nativa di Gambas Alloc( ) ).
Pertanto se nel codice C è così scritto:
Funzione_Esterna( &variabile_struttura, ...)
allora in Gambas avremo:
Public Struct STRUTTURA ... ... End Struct Public Sub Main() Dim p As Pointer Dim sr As STRUTTURA Funzione_Esterna(VarPtr(p), .....) ' Quindi si assegnerà tale variabile di tipo Pointer alla variabile di tipo Struttura da gestire in Gambas: sr = p ...
Gestione se un membro è Array o una Matrice
Se uno o più membri sono variabili vettoriali (Array) con il numero di elementi definito, allora - per la determinazione della memoria occupata dal vettore - si conta il numero degli elementi indicato nel vettore moltiplicato per la quantità di memoria occupata dal tipo dell'Array.
Esempio:
int i[4] --> 4 x 4 (ossia 4 elementi moltiplicato 4 byte di memoria occupati dal tipo int) = 16 byte complessivi
Così se ad esempio si ha un Puntatore:
int *[4] --> 4 * 8 (ossia 4 elementi moltiplicato 8 byte di memoria occupati dal tipo Puntatore) = 32 byte complessivi
Calcolo analogo in caso di Array multidimensionale (Matrice): si moltiplicheranno fra loro i valori che indicano gli elementi di ciascuna dimensione della Matrice, il risultato sarà in fine moltiplicato per per la quantità di memoria occupata dal tipo della Matrice.
Esempio:
int i[4][3] --> (4 x 3) x 4 (ossia 4 elementi della prima dimensione moltiplicato 3 elementi della seconda dimensione, moltiplicato i 4 byte di memoria occupati dal tipo int) = 48 byte complessivi
In modo simile se la Matrice ha più di due dimensioni.
In ordine, invece, all'indicizzazione dell'array all'interno dell'area di memoria della Struttura esterna, il membro array o matrice si comporterà come consueto per il tipo di dato al quale esso appartiene.
Infatti, nel seguente esempio in C:
struct STRU { char c; char cc[100]; };
la Struttura esterna occupa 101 byte; laddove il primo membro si pone necessariamente all'indice zero, ed il secondo - essendo un char - si pone all'indice dispari 1 (immediatamente successivo al primo membro).
Invece in quest'altra Struttura esterna:
struct STRU { char c; int cc[100]; };
la Struttura esterna occupa 404 byte; laddove il primo membro si pone necessariamente all'indice zero, ed il secondo - essendo un int - si pone all'indice 4 (che è uguale alla dimensione del suo tipo).
Mostriamo di seguito un altro esempio più complesso, in cui il codice sorgente in C della libreria condivisa .so esterna è il seguente:
#include <stdlib.h> struct AAA { char c; short s; int i; char * p[5]; char * p1, * p2; int n; }; struct AAA a; struct AAA * Estrae_Valori() { a.c = 0x0F; a.s = 0x0400; a.i = 0x00444444; a.p[0] = (char *) malloc(4); a.p[1] = (char *) malloc(4); a.p[2] = (char *) malloc(4); a.p[3] = (char *) malloc(4); a.p[4] = (char *) malloc(4); a.p[0][0] = 0x01; a.p[0][1] = 0x02; a.p[0][2] = 0x03; a.p[0][3] = 0x04; a.p[1][0] = 11; a.p[1][1] = 11; a.p[1][2] = 11; a.p[1][3] = 11; a.p[2][0] = 22; a.p[2][1] = 22; a.p[2][2] = 22; a.p[2][3] = 22; a.p[3][0] = 33; a.p[3][1] = 33; a.p[3][2] = 33; a.p[3][3] = 33; a.p[4][0] = 44; a.p[4][1] = 44; a.p[4][2] = 44; a.p[4][3] = 44; a.p1 = (char *) malloc(4); a.p1[0] = 99; a.p1[1] = 99; a.p1[2] = 99; a.p1[3] = 99; a.p2 = "efgh"; a.n = 10000; return &a; }
che ritorna alla funzione chiamante di Gambas l'indirizzo di memoria della sua Struttura.
Nel codice Gambas, che segue, si andrà a leggere da un Puntatore, che ha raccolto l'indirizzo di memoria della Struttura esterna ritornata dalla libreria esterna, e con i Memory Stream i valori dei vari membri della predetta Struttura
Si ponga particolare attenzione al calcolo per l'eventuale necessario allineamento dei membri.
Private Extern Estrae_Valori() As Pointer In "/tmp/stru" Public Sub Main() Dim p, po, p1, p2 As Pointer Dim st, re, s1, s2 As Stream Dim b As Byte Dim j As Short Dim t As String ' Generiamo la libreria esterna, scritta in C, contenente la "Struttura": Shell "gcc -o /tmp/stru.so " & Application.Path &/ "stru.c -shared -fPIC" Wait p = Estrae_Valori() st = Memory p For Read Print Read #st As Byte ' Legge il 1° membro (char) che occupa 1 byte Seek #st, 2 ' Ci spostiamo nel rispetto dell'allineamento dei membri Print Read #st As Short ' Legge il 2° membro (short) che occupa 2 byte Print Read #st As Integer ' Legge il 3° membro (int) che occupa 4 byte Read #st, po ' Legge il 3° membro: il puntatore (che occupa 8 byte) della prima dimensione di char * p[5] re = Memory po For Read ' Andiamo a dereferenziare il "Puntatore" per leggere nell'area di memoria da esso puntata For j = 0 To 3 Read #re, b Print j, b Next Seek #re, 32 ' Per ciascuna dimensione si salta di 32 byte avanti For j = Seek(re) To Seek(re) + 3 Read #re, b Print j, b Next Seek #re, 64 ' Per ciascuna dimensione si salta di 32 byte avanti For j = Seek(re) To Seek(re) + 3 Read #re, b Print j, b Next Seek #re, 96 ' Per ciascuna dimensione si salta di 32 byte avanti For j = Seek(re) To Seek(re) + 3 Read #re, b Print j, b Next Seek #re, 128 ' Per ciascuna dimensione si salta di 32 byte avanti For j = Seek(re) To Seek(re) + 3 Read #re, b Print j, b Next re.Close Seek #st, Seek(st) + (SizeOf(gb.Pointer) * 4) ' Si moltiplica per l'indice massimo di char * p[n] Read #st, p1 ' Legge il 5° membro: il puntatore (che occupa 8 byte) della prima dimensione di char * p1 s1 = Memory p1 For Read ' Andiamo a dereferenziare il "Puntatore" per leggere nell'area di memoria da esso puntata For j = 0 To 3 Read #s1, b Print j, b Next s1.Close Read #st, p2 ' Legge il 6° membro: il puntatore (che occupa 8 byte) della prima dimensione di char * p2 s2 = Memory p2 For Read ' Andiamo a dereferenziare il "Puntatore" per leggere nell'area di memoria da esso puntata Read #s2, t Print t s2.Close Print Read #st As Integer ' Legge il 7° membro (int) che occupa 4 byte st.Close End
Gestione di un membro di una Struttura che fa riferimento a una Union
Se uno o più membri della Struttura fanno riferimento ad una Union, allora si calcola solo il membro della Union che occupa la quantità maggiore di memoria rispetto agli altri membri. Insomma nella Union - con riferimento alla determinazione della sua dimensione - prevale il membro che in assoluto (o per tipo di dati o perché è un array con dimensione superiore ai tipi di ciascun altro membro della Union) occupa la maggiore quantità di memoria rispetto agli altri membri della Union medesima.
Però, riguardo alla posizione d'indice nell'area riservata di memoria si fa riferimento al tipo di dato più grande presente tra i membri della Union.
Se due membri contingui della Struttura sono due Union, e se l'ultimo membro della prima delle due Union è di tipo comunque diverso dal tipo dal tipo del primo membro della seconda Union, va effettuato l'allineamento fra le due Union secondo i consueti criteri.
Gestione se un membro fa riferimento ad altra Struttura o ad una Struttura annidata
Se un membro della Struttura fa riferimento ad un'altra Struttura secondaria esterna alla Struttura principale, ovvero è rappresentato da una Struttura definita e sviluppata esplicitamente all'interno della Struttura principale (Struttura annidata), si effettua il calcolo - nelle modalità consuete - della quantità di memoria occupata complessivamente dai membri della Struttura esterna secondaria ovvero nel secondo caso dai membri della Struttura annidata.
Se il membro, che fa riferimento ad una Struttura secondaria esterna, è invece dichiarato come Puntatore, allora esso occuperà semplicemente 8 byte, ossia la quantità di byte occupati ordinariamente dalla variabile di tipo Puntatore.
Nel caso il membro, che si riferisce ad altra Struttura secondaria o ad una Struttura annidata, sia un Array con numero definito di elementi, la quantità che occupa la Struttura secondaria o la Struttura annidata va moltiplicata per il numero espresso degli elementi dellArray.
Riguardo al numero d'indice del byte di inizio del primo membro della Struttura esterna secondaria o di quella annidata all'interno d'area di memoria della Struttura principale, bisogna prendere in considerazione il tipo di valore che occupa maggior memoria fra i tipi di valore ai quali appartengono i vari membri costituenti la Struttura predetta. Si confronterà, quindi, tale tipo maggiore della Struttura secondaria o di quella annidata con il tipo di appartenenza del membro della Struttura principale immediatamente ad esso precedente. Se la quantità di memoria occupata dal membro maggiore della Struttura secondaria o di quella annidata è superiore alla quantità di memoria occupata dal membro della Struttura principale immediatamente ad esso precedente, allora i dati relativi al valore contenuto dal primo membro della Struttura secondaria o di quella annidata avranno inizio ad un numero d'indice - se libero - uguale alla quantità di memoria occupata dal tipo di appartenenza del membro maggiore (individuato come già descritto), altrimenti - se tale byte è già occupato dai dati del membro precedente - quelli si porranno al numero d'indice più vicino che sia un multiplo della quantità di memoria occupata dal tipo di appartenenza del predetto membro maggiore.
Questa regola va sempre applicata, anche se il primo membro della Struttura innestata o esterna dichiarata è di dimensione inferiore a quello, individuato come il più grande fra i membri della Struttura secondaria esterna od innestata per l'individuazione della posizione esatta del primo membro predetto.
Mostriamo due codici esemplificativi.
Primo esempio
Nel seguente codice la Struttura secondaria, innestata in quella principale, è dichiarata con una ordinaria varibile di tipo Struttura:
#include <stdio.h> struct STRUTTURA { char c; short s; int i; struct Innesto { short Is; long Il; } In; } St; int main() { printf("%ld\n", sizeof(St)); return (0); }
In tal caso la dimensione complessiva della Struttura principale (che nell'esempio abbiamo denominato con l'identificativo: STRUTTURA) è pari a 24 byte, così distribuiti:
- 1 byte per il primo membro (essendo di tipo char) - inizia dall'indice 0;
- 1 byte per il necessario allineamento;
- 2 byte per il secondo membro (essendo di tipo short) - inizia dall'indice 2;
- 4 byte per il terzo membro (essendo di tipo int) - inizia dall'indice 4;
- 2 byte per il primo membro della Struttura innestata (essendo di tipo short). Essendo però il successivo membro un long (quindi "maggiore" del tipo short), allora i valori di tale membro short avranno inzio dal byte d'indice 8;
- 6 byte per il necessario allineamento;
- 8 byte per il secondo membro della Struttura innestata (essendo di tipo long) - inizia dall'indice 16.
La Struttura C complessivamente occupa 24 byte.
Secondo esempio
In questo secondo codice, invece, la Struttura secondaria, innestata in quella principale, è dichiarata mediante una varibile di tipo Puntatore:
#include <stdio.h> struct STRUTTURA { char c; short s; int i; struct Innesto { short Is; long Il; } *In; } St; int main() { printf("%ld\n", sizeof(St)); return (0); }
In tal caso la dimensione complessiva della Struttura principale è pari a 16 byte, così distribuiti:
- 1 byte per il primo membro (essendo di tipo char);
- 1 byte per il necessario allineamento;
- 2 byte per il secondo membro (essendo di tipo short);
- 4 byte per il terzo membro (essendo di tipo int);
- 8 byte (nei sistemi a 64bit) per il quarto membro (essendo una variabile di tipo Puntatore).
Determinazione definitiva della quantità di memoria occupata da una Struttura
Per la determinazione finale e complessiva della quantità di memoria occupata dalla Struttura da effettuare dopo l'ultimo membro, può risultare necessario individuare la quantità di memoria residua aggiuntiva, da occupare, tale da raggiungere il valore del byte multiplo immediatamente successivo (il più prossimo) della quantità di memoria occupata dal tipo più grande (in quantità di memoria occupata) fra quelli utilizzati come membri nella Struttura medesima.
Vediamo un esempio:
struct STRUTTURA_1 { long l; /* Questo è il tipo "maggiore" rispetto agli altri, perché occupa più memoria (8 byte) rispetto agli altri. */ char c1; short sh; char c2; int i; };
Ebbene, la Struttura sembrerebbe occupare complessivamente 20 byte. Però il tipo di valore che occupa maggiore memoria è il tipo long (8 byte) rispetto agli altri. Il valore 20, che sembrerebbe essere la quantità di memoria occupata dalla Struttura non è esatto, poiché il valore 20 non è un multiplo di sizeof(long) = 8. Pertanto, per sapere quanta memoria in questo caso occupa complessivamente e correttamente la Struttura, si dovrà tenere conto dell'allineamento sino al valore successivo più vicino in ordine crescente a 20, ossia 24. Quindi la Struttura in esempio occupa 24 byte di memoria.
Vediamo un altro esempio simile al precedente:
struct STRUTTURA_2 { long l; /* Questo è il tipo "maggiore" rispetto agli altri, perché occupa più memoria (8 byte) rispetto agli altri. */ char c1; short sh; char c2; int i1; int i2; int i3; };
La Struttura sembra occupare 28 byte; ma 28 non è un multiplo del valore della quantità di memoria occupata dal tipo più grande presente nella Struttura: il long. Pertanto, l'allineamento porterà ad occupare complessivamente una quantità di memoria aggiuntiva terminale sino al valore multiplo più prossimo in ordine crescente al 28, ossia 32. Quindi la Struttura in esempio occupa 32 byte di memoria.
Altro esempio:
struct STRUTTURA_3 { char c1; int i; /* Questo è il tipo "maggiore" rispetto agli altri, perché occupa più memoria (4 byte) rispetto agli altri. */ short sh; char c2; };
La Struttura sembra occupare 11 byte; ma 11 non è un multiplo del valore della quantità di memoria occupata dal tipo più grande presente nella Struttura: il int. Pertanto, l'allineamento porterà ad occupare complessivamente una quantità di memoria aggiuntiva terminale sino al valore multiplo più prossimo in ordine crescente al 11, ossia 12. Quindi la Struttura in esempio occupa 12 byte di memoria.
Esempio riepilogativo
Nel seguente esempio in C cerchiamo di riepilogare tutti i casi principali visti, comprensivo di indicizzazione ed allineamenti eventuali:
struct esterna { short se; // 24 -> 25 + 6 di allineamento long le; // 32 -> 39 }; struct STRUTTURA { char c; // 0 + 3 di allineamento int i; // 4 -> 7 struct innestata { char ci; // 8 + 1 di allineamento short si; // 10 -> 11 } inn; int i2; // 12 -> 15 int i3; // 16 -> 19 struct esterna es; // (24 ----> 39) short s; // 40 -> 41 + 6 di allineamento struct esterna * es2; // 48 -> 55 char * p; // 56 -> 71 int i3b; // 72 -> 75 + 4 di allineamento union { short us; int ui; long ul; char uc[12]; // 80 -> 91 + 4 di allineamento }; short v[8]; // 96 -> 103 struct innestata_2 { char ci; short si; } * inn2; // 104 -> 111 char c2; // 112 + 1 di allineamento short v2[2][3]; // 114 ->125 + 2 di allineamento long l; // 128 -> 135 }; // TOT. 136 byte di memoria occupata dalla Struttura "STRUTTURA" int main() { printf("%ld\n", sizeof(struct STRUTTURA)); return (0); }