Differenze tra le versioni di "Gestione delle Strutture esterne"

Da Gambas-it.org - Wikipedia.
 
(33 versioni intermedie di uno stesso utente non sono mostrate)
Riga 13: Riga 13:
  
 
==Struttura esterna che contiene membri di tipo array==
 
==Struttura esterna che contiene membri di tipo array==
 +
Una ''Struttura'' sia in C che in Gambas può avere membri di tipo vettoriale appartenenti a qualsiasi tipo di dati.
  
<FONT Color=red size=4><B>Paragrafo in costruzione !</b></font>
+
===Problema riscontrato nella quantità di memoria occupata===
 +
Va rilevato che, però, nel caso della definizione in Gambas di una ''Struttura'' esterna, sctitta in C e avente come membro un vettore (''array'' ), per quanto riguarda il posizionamento del valore del primo elemento del vettore all'interno dell'area di memoria della ''Struttura'' sembra che Gambas faccia riferimento al ''Puntatore'' dell'Oggetto ''array''.
 +
<BR>Ciò comporta che il posizionamento del primo elemento del vettore all'interno dell'area di memoria della ''Struttura'' fa riferimento al numero d'indice (''offset'' ) 8, se libero, altrimenti al numero d'indice multiplo di 8 più prossimo.
 +
 
 +
Per verificare questo problema, è sufficiente confrontare il diverso comportamento del linguaggio C e del linguaggio Gambas rispetto alla gestione di una medesima ''Struttura''.
 +
 
 +
Nel codice C:
 +
#include <stdio.h>
 +
 +
struct STRUTTURA {
 +
    char c;
 +
    char cc[8];
 +
    char n;
 +
    short int ss[8];
 +
};
 +
 +
 +
int main() {
 +
 +
  printf("%ld\n", sizeof(struct STRUTTURA));
 +
 +
  return (0);
 +
 +
}
 +
La dimensione complessiva della ''Struttura'' è pari a '''26''' byte.
 +
 
 +
Nel codice Gambas:
 +
Public Struct PRINCIPALE
 +
  b As Byte
 +
  bb[8] As Byte
 +
  n As Byte
 +
  cc[8] As Short
 +
End Struct
 +
 +
 +
Public Sub Main()
 +
 +
  Dim pr As New PRINCIPALE
 +
  Dim p As Pointer
 +
  Dim i As Integer
 +
 +
<FONT Color=gray>' ''All'indirizzo di memoria della "Struttura" si aggiungono 24 byte, poiché è dal 25° byte (numero d'indice '''24''') che sono memorizzati i valori appartenenti ai membri di una "Struttura" in Gambas:''</font>
 +
  p = Object.Address(pr) '''+ 24'''
 +
 +
  pr.b = 99
 +
 +
  For i = 0 To 7
 +
    pr.bb[i] = CByte(i)
 +
  Next
 +
 +
  pr.n = 111
 +
 +
  For i = 0 To 7
 +
    pr.cc[i] = CShort(i + 1 * 1000)
 +
  Next
 +
 +
<FONT Color=gray>' ''Verifica il posizionamento dei dati immessi all'interno dell'area di memoria della "Struttura", dedicata lla memoriazzazione dei valori dei suoi membri, tenendo conto che in tal caso l'indice 0 di questo ciclo "FOR" corrisponde in vero all'indice 24 della predetta area di memoria:''</font>  
 +
  For i = 0 To 39
 +
    Print i, Hex(Byte@(p + i), 2)
 +
  Next
 +
 +
  Print "\nLa dimensione complessiva della "Struttura" è pari a "; Object.SizeOf(pr); " byte"
 +
 +
End
 +
La dimensione complessiva della ''Struttura'' risulta essere pari a '''40''' byte.
  
  
 
==Membro del tipo di un'altra Struttura==
 
==Membro del tipo di un'altra Struttura==
 +
Se un membro della Struttura esterna, scritta in C, fa riferimento ad un'altra Struttura secondaria esterna alla Struttura principale, come in questo esempio:
 +
struct Secondaria {
 +
    int i;
 +
    long int l;
 +
};
 +
 +
struct PRINCIPALE {
 +
    char c;
 +
    struct Secondaria sc;
 +
};
 +
laddove la ''Struttura'', chiamata "Secondaria", occupa una quantità di memroia pari a 16 byte, mentre la ''Struttura'', chiamata "PRINCIPALE", occupa una quantità di memoria pari a 24 byte, così specificata:
 +
<BR>- 1 byte dal primo membro che è un tipo di dato "char";
 +
<BR>- 16 byte dal membro che fa riferimento all'altra ''Struttura'' (denominata "Secondaria"). Poiché la quantità di memoria occupata da questo secondo membro è superiore a quella occupata dal primo membro, allora per la norma dell'<I>allineamento</i> esso - <SPAN Style="text-decoration:underline">essendo successivo al primo membro della ''Struttura''</span> - sarà posizionato al byte pari al numero d'indice corrispondente alla quantità di memoria occupata, se libero, e comunque quantità non superiore a 8. La memoria occupata da questo secondo membro è 16, pertanto sarà adattato al valore d'indice 8 (o multiplo di 8, se il numero d'indice 8 è occupato da altro membro).
 +
<BR>Pertanto la quantità di memoria, occupata dalla ''Struttura'', chiamata "PRINCIPALE", è pari a 24 byte, come è agevolmente individuabile dal seguente codice in C:
 +
#include <stdio.h>
 +
 +
 +
struct Secondaria {
 +
    int i;
 +
    long int l;
 +
};
 +
 +
struct PRINCIPALE {
 +
    char c;
 +
    '''struct Secondaria''' sc;
 +
};
 +
 +
 +
int main() {
 +
 +
    printf("%ld\n", sizeof(struct Secondaria));
 +
 +
    printf("%ld\n", sizeof(struct PRINCIPALE));
 +
 +
    return (0);
 +
 +
}
 +
====Sull'individuazione del numero d'indice di inizio del primo membro della Struttura esterna secondaria====
 +
Riguardo al numero d'indice del byte di inizio del primo membro della ''Struttura'' secondaria 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 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 è 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 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.
 +
<BR>Questa regola va sempre applicata, anche se il primo membro della ''Struttura'' secondaria esterna dichiarata è di dimensione inferiore a quello, individuato come il più grande fra i membri della ''Struttura'' secondaria esterna per l'individuazione della posizione esatta del primo membro predetto.
 +
 +
====Trasposizione in Gambas====
 +
In Gambas le due ''Strutture'' del precedente esempio, scritto in C, saranno così impostate:
 +
Public Struct Secondaria
 +
  c As Byte
 +
  l As Long
 +
End Struct
 +
 +
Public Struct PRINCIPALE
 +
  c As Byte
 +
  sc As '''Struct''' Secondaria
 +
End Struct
 +
 +
 +
Public Sub Main()
 +
 +
  Dim sc As New Secondaria
 +
  Dim pr As New PRINCIPALE
 +
 +
  Print Object.SizeOf(sc)
 +
  Print Object.SizeOf(pr)
 +
 +
End
 +
V'è da sottolineare che il secondo membro della ''Struttura'', anche qui chiamata "PRINCIPALE", se fosse dichiarato senza la parola-chiave "Struct" non solleverebbe un errore di sintassi, ma la quantità di memria occupata riportata sarebbe errata: in questo caso l'istruzione con "Print" riporterebbe che la ''Struttura'' "PRINCIPALE" occupa 16 byte. Come sappiamo, invece, la ''Struttura'', anche qui chiamata "PRINCIPALE", occupa 24 byte, e tale corretto valore viene riportato soltanto se il secondo membro, che fa riferimento alla ''Struttura'', denominata "Secondaria", è dichiarato con la parola-chiave "Struct".
 +
 +
===Problema riscontrato nella quantità di memoria occupata===
 +
Va rilevato che, però, nel caso della definizione in Gambas di una ''Struttura'' esterna, sctitta in C e avente come membro un riferimento a un'altra ''Struttura'' esterna, la quantità di memoria assunta da detta ''Struttura'' principale, ricreata in linguaggio Gambas, non sarà mai inferiore a 16 byte.
 +
<BR>Probabilmente Gambas fa comunque riferimento all'Oggetto.
 +
 +
A tal riguardo si faccia questa prova.
 +
<BR>Verifica in C:
 +
  #include <stdio.h>
 +
 +
 +
struct Secondaria {
 +
    int i;
 +
    int n;
 +
};
 +
 +
struct PRINCIPALE {
 +
    char c;
 +
    struct Secondaria sc;
 +
};
 +
 +
 +
int main() {
 +
 +
    printf("%ld\n", sizeof(struct PRINCIPALE));
 +
 +
    return (0);
 +
 +
}
 +
Si verà che la dimensione della ''Struttura'' principale è pari a '''12''' byte.
 +
 +
Facendo invece la prova in Gambas:
 +
Public Struct Secondaria
 +
  i As Integer
 +
  n As Integer
 +
End Struct
 +
 +
Public Struct PRINCIPALE
 +
  c As Byte
 +
  sc As Struct Secondaria
 +
End Struct
 +
 +
 +
Public Sub Main()
 +
 +
  Dim pr As New PRINCIPALE
 +
 +
  Print Object.SizeOf(pr)
 +
 +
End
 +
Il risultato è diverso: la ''Struttura'' principale risulterà avere una dimensione pari a '''16''' byte.
  
<FONT Color=red size=4><B>Paragrafo in costruzione !</b></font>
+
In tal caso (ossia, qualora la dimensione della ''Struttura'' principale in C sia inferiore di 16 byte) la soluzione ''sicura'' del problema nella fase di trasposizione in linguaggio Gambas sta nel non richiamare la ''Struttura'' secondaria esterna mediante uno specifico membro della ''Struttura'' principale chiamante, bensì ''innestando'' i singoli membri della ''Struttura'' secondaria all'interno di quella principale; facendo insomma sì che i singoli membri della ''Struttura'' secondaria diventino semplicemente membri propri della ''Struttura'' principale, nel rispetto ovviamente degli allineamenti eventualmente necessari.
  
  
 
==Membro del tipo di una Struttura innestata (annidata) all'interno della Struttura principale==
 
==Membro del tipo di una Struttura innestata (annidata) all'interno della Struttura principale==
 +
Se un membro della ''Struttura'' è 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'' annidata.
  
<FONT Color=red size=4><B>Paragrafo in costruzione !</b></font>
+
Nel caso il membro, che si riferisce a una ''Struttura'' annidata, sia un Array con numero definito di elementi, la quantità che occupa la ''Struttura'' annidata va ovviamente moltiplicata per il numero espresso degli elementi dell'array.
 +
 
 +
Riguardo al numero d'indice all'interno dell'area puntata dal primo membro della ''Struttura'' annidata, vale quanto già esposto nel precedente paragrafo relativo alle ''Strutture'' esterne richiamate da un membro della ''Struttura'' principale.
 +
 
 +
====Trasposizione in Gambas di una Struttura ''innestata''====
 +
In Gambas non è possibile definire una ''Struttura'' "innestata" all'interno di una ''Struttura'' principale con modalità analoghe a quelle previste nel linguaggio C; pertanto si provvederà semplicemente a far diventare i singoli membri della 'Struttura'' "innestata" semplicemente membri propri della ''Struttura'' principale, nel rispetto ovviamente degli allineamenti eventualmente necessari.
  
  
 
==Membro del tipo Puntatore a un'altra Struttura==
 
==Membro del tipo Puntatore a un'altra Struttura==
 +
Il membro di una ''Struttura'', scritta in C, può essere dichiarato come variabile di tipo ''Puntatore'' al tipo di dato di un'altra ''Struttura''.
  
<FONT Color=red size=4><B>Paragrafo in costruzione !</b></font>
+
L'argomento in questione è affrontato nella seguente pagina della Wiki:
 +
<BR>[[Utilizzare come Struttura in Gambas una Struttura esterna dichiarata, come proprio membro di tipo Puntatore, da una Struttura principale esterna]]
  
  
==Gestione delle "Union"==
+
==Gestione di un membro di una Struttura che fa riferimento a una ''Union''==
Una "Unione" è un'area di memoria allocata che può contenere - in momenti diversi - dati di diversa dimensione e tipo, comunque <SPAN Style="text-decoration:underline">condividendo sempre il medesimo spazio di memoria</span>. In tal senso <SPAN Style="text-decoration:underline">lo spazio riservato per una "Union" corrisponde soltanto a quello necessario per il suo membro più grande</span>. Pertanto la ''Union'' contiene il dato di <SPAN Style="text-decoration:underline">un solo membro per volta</span>.
+
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.
<BR>Così se abbiamo questa "Unione":
+
 
union UNIONE {
+
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''.
  char c;
 
  short int s;
 
  long int l;
 
}
 
la quantità di memoria occupata da tale esempio di ''Union'' è uguale a 8, ossia alla quantità di memoria occupata dal membro più grande (in tal caso quello di tipo "long int"). Inoltre, dovendosi potrà contenere <SPAN Style="text-decoration:underline">ogni qual volta</span> deve essere valorizzato un suo membro, il dato di <SPAN Style="text-decoration:underline">uno solo</span> dei suoi tre membri.
 
  
In Gambas una "Union" va ricreata attraverso la usuale Classe "Struct", avendo l'accortezza di occupare una quantità di memoria pari alla totale quantità di memoria allocata per la "Union" della libreria esterna scritta in C.
+
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.
<BR>Di ciò bisogna avere cura anche qualora si utilizzi, poi, nel codice Gambas esclusivamente un membro della "Union" la cui complessiva dimensione di memoria occupata sia inferiore alla quantità di memoria che deve essere riservata per la "Union". In tal caso si aggiungerà un membro fittizio, ad esempio un array di tipo Byte, con un numero di elementi pari al numero necessario per coprire l'intera quantità di memoria da riservare per la "Union".
 
<BR>Se ad esempio la "Union" nella libreria esterna, formata da un membro di tipo di una "Struttura" e da un array di tipo Byte, laddove il membro di tipo "Struttura" occupa una quantità di memoria inferiore alla complessiva quantità di memoria occupata dall'array, ovviamente la quantità di memoria che il sistema riserverà per la "Union" è pari alla quantità di memoria occupata dal predetto array. In questo caso, se interessa in Gambas utilizzare solamente la "Struttura" quale <SPAN Style="text-decoration:underline">membro</span> (in questo esempio) della "Union", porremo nella "Struttura" (che in Gambas deve ricreare la "Union" esterna) anche l'array di tipo Byte (che casualmente è già un membro componente della "Union" esterna) con un numero di elementi pari alla differenza fra la quantità di byte di memoria, occupata dalla "Union" esterna scritta in C, e la quantità di memoria che sarà occupata dal membro di tipo della "Struttura" (anch'esso, come sappiamo, costituente la "Union" esterna).
 
  
  
==Allineamento dei membri nell'area di memoria allocata di una Struttura==
+
==Allineamento dei membri nell'area di memoria allocata di una Struttura esterna scritta in C==
Quando la quantità di byte, occupati dal tipo di dati di un membro, successivo al primo, di una ''Struttura'', è superiore alla quantità occupata dal tipo di dati del membro precedente, esso è posto al numero d'indice (''offset'' ) uguale alla quantità di byte occupati dal tipo di dati di appartenenza. Se il numero d'indice è già occupato, allora la collocazione del membro scorre al numero d'indice uguale al multiplo della predetta quantità di byte, occupati da suo tipo di dati, immediatamente successivo.
+
Quando la quantità di byte, occupati dal tipo di dati di un membro, <SPAN Style="text-decoration:underline">successivo al primo/span>, di una ''Struttura'', è superiore alla quantità occupata dal tipo di dati del membro precedente, esso è posto al numero d'indice (''offset'' ) uguale alla quantità di byte occupati dal tipo di dati di appartenenza e <SPAN Style="text-decoration:underline">in ogni caso mai superiore a 8 byte</span>. Se il numero d'indice è già occupato, allora la collocazione del membro scorre al numero d'indice uguale al multiplo della predetta quantità di byte, occupati da suo tipo di dati, immediatamente successivo purché libero.
  
Se invece la quantità occupata dal tipo di dati di un membro è inferiore a quella occupata dal mebro precedente, esso è posto al numero d'indice immediatamente libero dopo il membro che lo precede.
+
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.
  
 
Così in C, se una ''Struttura'' è composta ad esempio da un membro di tipo "char" e da un membro di tipo "int", la quantità di memoria, occupata dal titpo di dati ("int" = 4 byte) del secondo membro, è superiore alla quantità occupata dal tipo di dati del primo ("char" = 1 byte), l'allineamento dei due membri all'interno dell'area di memoria riservata della ''Struttura'' sarà il seguente:
 
Così in C, se una ''Struttura'' è composta ad esempio da un membro di tipo "char" e da un membro di tipo "int", la quantità di memoria, occupata dal titpo di dati ("int" = 4 byte) del secondo membro, è superiore alla quantità occupata dal tipo di dati del primo ("char" = 1 byte), l'allineamento dei due membri all'interno dell'area di memoria riservata della ''Struttura'' sarà il seguente:
Riga 56: Riga 236:
 
     char c; <FONT Color=blue>/* Posizionato al numero d'indice 0; occupa un solo byte */</font>
 
     char c; <FONT Color=blue>/* Posizionato al numero d'indice 0; occupa un solo byte */</font>
 
     int i;  <FONT Color=blue>/* Poiché il 2° membro occupa 4 byte, allora esso è collocato al numero d'indice 4, atteso che è libero */</font>
 
     int i;  <FONT Color=blue>/* Poiché il 2° membro occupa 4 byte, allora esso è collocato al numero d'indice 4, atteso che è libero */</font>
  }
+
  };
 +
Pertanto, la quantità complessiva di memoria occupata dai membri da questa ''Struttura'' è pari a 8 byte.
 +
 
 
In quest'altra ''Struttura'' invece avremo:
 
In quest'altra ''Struttura'' invece avremo:
  struct STRUTTURA {
+
struct STRUTTURA {
    char c; <FONT Color=blue>/* Posizionato al numero d'indice 0; occupa un solo byte */</font>
+
<FONT Color=blue>/* Il 1° membro è posizionato al numero d'indice 0 e occupa un solo byte */</font>
     int i;  <FONT Color=blue>/* Poiché il 2° membro occupa 4 byte (numero superiore a quello del primo membro), allora esso è collocato al numero d'indice 4, atteso che è libero */</font>
+
     char c;
     short int s;  <FONT Color=blue>/* Poiché la quantità occupata da questo occupa è inferiore a quella del mebro precedente, esso è posto al numero d'indice immediatamente libero dopo il membro precedente: ossia 8 */</font>
+
  <FONT Color=blue>/* Poiché il 2° membro occupa 4 byte (numero superiore a quello del primo membro), allora esso è collocato al numero d'indice 4, atteso che è libero */</font>
     int n;  <FONT Color=blue>/* Poiché questo membro occupa 4 byte (numero superiore a quello del membro immediatamente precedente), allora esso dovrebbe essereè collocato al numero d'indice 4. Essendo però tale numero d'indice già occupato, lo si porrà al primo numero d'indice multiplo di 4 libero: in tal caso 12 */</font>
+
     int i;
     Long int l;   <FONT Color=blue>/* Poiché questo membro occupa 8 byte, ossia una quantità superiore a quella occupata dal membro precedente, esso dovrebbe essere collocato al numero d'indice 8, ma essendo già occupato, si porrà al primo numero d'indice multiplo di 8 libero: in tal caso 16 */</font>
+
  <FONT Color=blue>/* Poiché la quantità occupata dal 3° membro è inferiore a quella del membro precedente, esso è posto al numero d'indice immediatamente libero dopo il membro precedente: ossia 8 */</font>
  }
+
     short int s;
 +
  <FONT Color=blue>/* Poiché il 4° membro occupa 4 byte (numero superiore a quello del membro immediatamente precedente), allora esso dovrebbe essereè collocato al numero d'indice 4. Essendo però tale numero d'indice già occupato, lo si porrà al primo numero d'indice multiplo di 4 libero: in questo caso 12 */</font>
 +
     int n;
 +
<FONT Color=blue>/* Poiché questo ultimo membro occupa 8 byte, ossia una quantità superiore a quella occupata dal membro precedente, esso dovrebbe essere collocato al numero d'indice 8, ma essendo già occupato, si porrà al primo numero d'indice multiplo di 8 libero: in uesto caso 16 */</font>
 +
    long int l;
 +
  };
 
Pertanto, la quantità complessiva di memoria occupata dai membri da questa ''Struttura'' è pari a 24 byte.
 
Pertanto, la quantità complessiva di memoria occupata dai membri da questa ''Struttura'' è pari a 24 byte.
 
  
 
==Riassunto esemplificativo==
 
==Riassunto esemplificativo==
Riga 119: Riga 305:
 
La quantità di memoria comlessiva, occupata dalla descritta ''Struttura'' principale, è pari a 200 byte.
 
La quantità di memoria comlessiva, occupata dalla descritta ''Struttura'' principale, è pari a 200 byte.
  
==Gestione dei Campi bit di una Struttura==
 
  
<FONT Color=red size=4><B>Paragrafo in costruzione !</b></font>
+
=Gestione delle "Union"=
 +
Una "Unione" è un'area di memoria allocata che può contenere - in momenti diversi - dati di diversa dimensione e tipo, comunque <SPAN Style="text-decoration:underline">condividendo sempre il medesimo spazio di memoria</span>. In tal senso <SPAN Style="text-decoration:underline">lo spazio riservato per una "Union" corrisponde soltanto a quello necessario per il suo membro più grande</span>. Pertanto la ''Union'' contiene il dato di <SPAN Style="text-decoration:underline">un solo membro per volta</span>.
 +
<BR>Così se abbiamo questa "Unione":
 +
union UNIONE {
 +
  char c;
 +
  short int s;
 +
  long int l;
 +
}
 +
la quantità di memoria occupata da tale esempio di ''Union'' è uguale a 8, ossia alla quantità di memoria occupata dal membro più grande (in tal caso quello di tipo "long int"). Inoltre, dovendosi potrà contenere <SPAN Style="text-decoration:underline">ogni qual volta</span> deve essere valorizzato un suo membro, il dato di <SPAN Style="text-decoration:underline">uno solo</span> dei suoi tre membri.
 +
 
 +
====Gestione in Gambas di una "Union" emulandola con una "Struttura" avente un unico membro====
 +
In Gambas una "Union" può essere ricreata attraverso la usuale Classe "Struct", avendo l'accortezza di occupare una quantità di memoria pari alla totale quantità di memoria allocata per la "Union" della libreria esterna scritta in C.
 +
<BR>Nel caso della "Union" precedente, potremo definire in Gambas:
 +
Public Struct UNIONE
 +
  l As Long
 +
End Struct
 +
Poniamo l'esempio di una "Union" restituita per "Valore" da una funzione esterna contenuta in una libreria condivisa da noi creata. Sebbene venga valorizzato il membro del tipo "int", che corrisponde in Gambas al tipo di dato "Integer", andremo a leggere l'unico membro di dimensioni maggiori che costituisce la "Struttura" che emula la "Union".
 +
Public Struct Simula_Union
 +
  l As Long
 +
End Struct
 +
 +
<FONT Color=gray>' ''union UNIONE * funzione()''</font>
 +
Private Extern funzione() As Simula_Union In "/tmp/libadhoc"
 +
 +
 +
Public Sub Main()
 +
 +
  Dim un As Simula_Union
 +
 +
  Creaso()
 +
 +
  un = funzione()
 +
 +
  Print CInt(un.l)
 +
 +
End
 +
 +
Private Procedure Creaso()
 +
 
 +
  File.Save("/tmp/libadhoc.c", "#include <stdlib.h>\n\n" &
 +
                              "union UNIONE {\n" &
 +
                              "  int i;\n" &
 +
                              "  long int l;\n" &
 +
                              "  short int s;\n};\n\n" &
 +
                              "union UNIONE *funzione() {\n" &
 +
                              "  union UNIONE *un = malloc(sizeof(union UNIONE));\n" &
 +
                              "  un->i = 4660;\n" &
 +
                              "  return un;\n}")
 +
 
 +
  Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared" Wait
 +
 
 +
End
 +
Se invece la "Union" è passata e restituita per "Indirizzo", allora avremo:
 +
Public Struct Simula_Union
 +
  l As Long
 +
End Struct
 +
 +
<FONT Color=gray>' ''void funzione(union UNIONE *un)''</font>
 +
Private Extern funzione(un As Simula_Union) In "/tmp/libadhoc"
 +
 +
 +
Public Sub Main()
 +
 +
  Dim sim_uni As New Simula_Union
 +
 +
  Creaso()
 +
 +
  funzione(sim_uni)
 +
 +
  Print CInt(sim_uni.l)
 +
 +
End
 +
 +
Private Procedure Creaso()
 +
 
 +
  File.Save("/tmp/libadhoc.c",  "union UNIONE {\n" &
 +
                              "  int i;\n" &
 +
                              "  long int l;\n" &
 +
                              "  short int s;\n};\n\n" &
 +
                              "void funzione(union UNIONE *un) {\n" &
 +
                              "  un->i = 4660;\n}")
 +
 
 +
  Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared" Wait
 +
 
 +
End
 +
 
 +
====Gestione in Gambas di una "Union" emulandola con un Puntatore====
 +
La "Union" altresì può essere gestita mediante una variabile di tipo ''Puntatore''.
 +
<BR>Poniamo l'esempio di una "Union" restituita per "Valore" da una funzione esterna contenuta in una libreria condivisa da noi creata. Poiché viene valorizzato il membro del tipo "int", che corrisponde in Gambas al tipo di dato "Integer", otterremo il valore usando la funzione propria di Gambas per di dereferenziazione "Int@()".
 +
<FONT Color=gray>' ''union UNIONE * funzione()''</font>
 +
Private Extern funzione() As Pointer In "/tmp/libadhoc"
 +
 +
 +
Public Sub Main()
 +
 +
  Dim p As Pointer
 +
  Dim i As Integer
 +
 
 +
  Creaso()
 +
 
 +
  p = funzione()
 +
 +
  Print Int@(p)
 +
 +
End
 +
 +
Private Procedure Creaso()
 +
 
 +
  File.Save("/tmp/libadhoc.c", "union UNIONE {\n" &
 +
                              "  int i;\n" &
 +
                              "  long int l;\n" &
 +
                              "  short int s;\n};\n\n" &
 +
                              "union UNIONE un;\n\n" &
 +
                              "union UNIONE *funzione() {\n" &
 +
                              "  un.i = 4660;\n" &
 +
                              "  return &un;\n}")
 +
 
 +
  Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared -fPIC" Wait
 +
 
 +
End
 +
oppure anche:
 +
Private Procedure Creaso()
 +
 
 +
  File.Save("/tmp/libadhoc.c", "#include <stdlib.h>\n\n" &
 +
                              "union UNIONE {\n" &
 +
                              "  int i;\n" &
 +
                              "  long int l;\n" &
 +
                              "  short int s;\n};\n\n" &
 +
                              "union UNIONE *funzione() {\n" &
 +
                              "  union UNIONE *un = malloc(sizeof(union UNIONE));\n" &
 +
                              "  un->i = 4660;\n" &
 +
                              "  return un;\n}")
 +
 
 +
  Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared" Wait
 +
 
 +
End
 +
 
 +
=Gestione dei Campi bit di una Struttura=
 +
Gambas non prevede nella gestione delle proprie ''Strutture'' l'uso dei "Campi" che fanno riferimento ai singoli bit.
 +
<BR>Ad ogni modo, poiché in C ogni insieme di "Campi" occupa una quantità di memoria pari a un "Intero", in Gambas si dovrà provare ad utilizzare un membro di tipo di dato "Integer", assegnandogli un valore tale che i suoi bit siano posti a 1 e/o a 0, come dovuto. Nel fare ciò, bisognerà tenere conto che il primo identificatore di bit di "Campo", all'interno di una ''Struttura'' esterna scritta in C, corrisponde al bit meno significativo del tipo "Integer" (ossia quello più a destra nella rappresentazione binaria di un numero).
 +
 
 +
Vediamo di seguito un esempio pratico di utilizzo dei ''Campi'' con Gambas, per il quale sarà necessario creare un'apposita libreria esterna:
 +
Private Extern campi(cmp As Pointer) As Pointer In "/tmp/libcampi"
 +
 +
 +
Public Sub Main()
 +
 +
<FONT Color=gray>' ''I bit che saranno elevati a 1, partendo dal 2° bit (dato che il flag del primo bit effettivo è stato posto a zero nell'array "bb" e qui contrassegnato in rosso) come fosse il primo identificato con il valore 1 (il 3° bit con valore 2 e così via).''
 +
' ''In questo caso, cominciando dal 2° bit da destra), il valore 11 è dato da 3 bit:
 +
'  ''1 (che corrisponde al 2° bit da destra avente valore  2) +''
 +
'  ''2 (che corrisponde al 3° bit da destra avente valore  4) +''
 +
'  ''8 (che corrisponde al 4° bit da destra avente valore 16) =''
 +
' ''11                                                    22  +''
 +
' '' 1 (che corrisponde al 6° bit da destra avente valore 32) =''
 +
' ''                                                      54  +''
 +
' '' 1 (che corrisponde al 10° bit da destra con valore  512) =''
 +
' '''''                                            totale:  566''
 +
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''</font>
 +
<FONT Color=gray>' ''Il 1° bit da destra <B>↙</b></font>
 +
  Dim bb As Byte[] = [<FONT Color=red><B>0</b></font>, 11, 1, 0, 0, 0, 1, 0]
 +
 +
  Dim p As Pointer
 +
 +
  Creaso()
 +
 +
  p = campi(bb.Data)
 +
  If p == 0 Then Error.Raise("Errore !")
 +
 +
  Print CInt(p), "&h"; CStr(p)
 +
 +
End
 +
 +
Private Procedure Creaso()
 +
 
 +
  File.Save("/tmp/libcampi.c", "struct STRUTTURA {\n" &
 +
"<FONT Color=blue>/* Numero di bit necessari da coinvolgere per ciascun Campo a partire dal meno-significativo:*/</font>\n" &
 +
            "  unsigned a: 1;\n" &  ' occupa un solo bit a cominciare dal 1° bit da destra
 +
            "  unsigned b: 4;\n" &  ' occupa 4 bit a cominciare dal 2° bit da destra
 +
            "  unsigned c: 1;\n" &  ' occupa un bit a cominciare dal 6° bit da destra (valore-byte 32)
 +
            "  unsigned d: 1;\n" &  ' etc...
 +
            "  unsigned e: 1;\n" &
 +
            "  unsigned f: 1;\n" &
 +
            "  unsigned g: 1;\n};\n\n" &
 +
            "struct STRUTTURA st;\n\n" &
 +
            "struct STRUTTURA campi(char *cc) {\n" &
 +
            "  st.a = cc[0];\n" &
 +
            "  st.b = cc[1];\n" &
 +
            "  st.c = cc[2];\n" &
 +
            "  st.d = cc[3];\n" &
 +
            "  st.e = cc[4];\n" &
 +
            "  st.f = cc[5];\n" &
 +
            "  st.g = cc[6];\n" &
 +
            "  return st;\n}")
 +
 +
  Shell "gcc -o /tmp/libcampi.so /tmp/libcampi.c -shared -fPIC" Wait
 +
 
 +
End
  
  

Versione attuale delle 16:41, 9 giu 2023

Nell'uso delle funzioni esterne mediante la risorsa "Extern" è frequente imbattersi nell'uso e nella gestione di Strutture, dichiarate e definite nei file d'intestazione delle librerie esterne utilizzate.
Pertanto un'accurata comprensione e un'attenta gestione di tali Strutture esterne nel codice Gambas è assolutamente determinante per il corretto funzionamento del codice medesimo. [nota 1] [nota 2]

Nel codice Gambas si dovrà innanzitutto ricostruire la Struttura esterna.
Tale ricostruzione in Gambas deve essere precisa, avendo estrema cura per la definizione del tipo di dato di ciascun membro della Struttura esterna.
Ciò richiede una conoscenza almeno sufficiente del linguaggio C, poiché nella ricostruzione in Gambas della Struttura esterna, scritta in C, bisognerà rispettare la totale dimensione (ossia la quantità di byte di memoria occupata da quest'ultima), che potrà avvenire soltanto attraverso il rigido rispetto della quantità di byte occupati del tipo di dato dichiarato per ciascun membro componente della Struttura esterna.
Va aggiunto a ciò che ugualmente estrema attenzione bisognerà avere per le regole dell'allineamento dei membri e dei campi di una Struttura. Tale "allineamento" presente nella Struttura, ricostruita in Gambas, dovrà perfettamente coincidere con quello presente nella Struttura esterna.

A titolo esemplificativo va detto che:
- in un Struttura esterna un membro di tipo "char" con segno (capace dunque di rappresentare un valore compreso tra -127 e +127, occupando così 1 byte di memoria) andrà comunque dichiarato nella ricostruzione della Struttura in Gambas come un membro di tipo "Byte" (anche se questo tipo di dato in Gambas è senza segno);
- in C non esiste il tipo di dati "String"; e pertanto un membro di una Struttura esterna, dichiarato come "char *" (che occupa dunque la quantità di memoria occupata da una variabile di tipo "Puntatore"), va dichiarato nella Struttura ricostruita in Gambas come un "Puntatore".


Struttura esterna che contiene membri di tipo array

Una Struttura sia in C che in Gambas può avere membri di tipo vettoriale appartenenti a qualsiasi tipo di dati.

Problema riscontrato nella quantità di memoria occupata

Va rilevato che, però, nel caso della definizione in Gambas di una Struttura esterna, sctitta in C e avente come membro un vettore (array ), per quanto riguarda il posizionamento del valore del primo elemento del vettore all'interno dell'area di memoria della Struttura sembra che Gambas faccia riferimento al Puntatore dell'Oggetto array.
Ciò comporta che il posizionamento del primo elemento del vettore all'interno dell'area di memoria della Struttura fa riferimento al numero d'indice (offset ) 8, se libero, altrimenti al numero d'indice multiplo di 8 più prossimo.

Per verificare questo problema, è sufficiente confrontare il diverso comportamento del linguaggio C e del linguaggio Gambas rispetto alla gestione di una medesima Struttura.

Nel codice C:

#include <stdio.h>

struct STRUTTURA {
   char c;
   char cc[8];
   char n;
   short int ss[8];
};


int main() {

  printf("%ld\n", sizeof(struct STRUTTURA));

  return (0);

}

La dimensione complessiva della Struttura è pari a 26 byte.

Nel codice Gambas:

Public Struct PRINCIPALE
  b As Byte
  bb[8] As Byte
  n As Byte
  cc[8] As Short
End Struct


Public Sub Main()

 Dim pr As New PRINCIPALE
 Dim p As Pointer
 Dim i As Integer

' All'indirizzo di memoria della "Struttura" si aggiungono 24 byte, poiché è dal 25° byte (numero d'indice 24) che sono memorizzati i valori appartenenti ai membri di una "Struttura" in Gambas:
 p = Object.Address(pr) + 24

 pr.b = 99

 For i = 0 To 7
   pr.bb[i] = CByte(i)
 Next

 pr.n = 111

 For i = 0 To 7
   pr.cc[i] = CShort(i + 1 * 1000)
 Next

' Verifica il posizionamento dei dati immessi all'interno dell'area di memoria della "Struttura", dedicata lla memoriazzazione dei valori dei suoi membri, tenendo conto che in tal caso l'indice 0 di questo ciclo "FOR" corrisponde in vero all'indice 24 della predetta area di memoria: 
 For i = 0 To 39
   Print i, Hex(Byte@(p + i), 2)
 Next

 Print "\nLa dimensione complessiva della "Struttura" è pari a "; Object.SizeOf(pr); " byte"

End

La dimensione complessiva della Struttura risulta essere pari a 40 byte.


Membro del tipo di un'altra Struttura

Se un membro della Struttura esterna, scritta in C, fa riferimento ad un'altra Struttura secondaria esterna alla Struttura principale, come in questo esempio:

struct Secondaria {
   int i;
   long int l;
};

struct PRINCIPALE {
   char c;
   struct Secondaria sc;
};

laddove la Struttura, chiamata "Secondaria", occupa una quantità di memroia pari a 16 byte, mentre la Struttura, chiamata "PRINCIPALE", occupa una quantità di memoria pari a 24 byte, così specificata:
- 1 byte dal primo membro che è un tipo di dato "char";
- 16 byte dal membro che fa riferimento all'altra Struttura (denominata "Secondaria"). Poiché la quantità di memoria occupata da questo secondo membro è superiore a quella occupata dal primo membro, allora per la norma dell'allineamento esso - essendo successivo al primo membro della Struttura - sarà posizionato al byte pari al numero d'indice corrispondente alla quantità di memoria occupata, se libero, e comunque quantità non superiore a 8. La memoria occupata da questo secondo membro è 16, pertanto sarà adattato al valore d'indice 8 (o multiplo di 8, se il numero d'indice 8 è occupato da altro membro).
Pertanto la quantità di memoria, occupata dalla Struttura, chiamata "PRINCIPALE", è pari a 24 byte, come è agevolmente individuabile dal seguente codice in C:

#include <stdio.h>


struct Secondaria {
   int i;
   long int l;
};

struct PRINCIPALE {
   char c;
   struct Secondaria sc;
};


int main() {

   printf("%ld\n", sizeof(struct Secondaria));

   printf("%ld\n", sizeof(struct PRINCIPALE));	

   return (0);

}

Sull'individuazione del numero d'indice di inizio del primo membro della Struttura esterna secondaria

Riguardo al numero d'indice del byte di inizio del primo membro della Struttura secondaria 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 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 è 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 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 secondaria esterna dichiarata è di dimensione inferiore a quello, individuato come il più grande fra i membri della Struttura secondaria esterna per l'individuazione della posizione esatta del primo membro predetto.

Trasposizione in Gambas

In Gambas le due Strutture del precedente esempio, scritto in C, saranno così impostate:

Public Struct Secondaria
  c As Byte
  l As Long
End Struct

Public Struct PRINCIPALE
  c As Byte
  sc As Struct Secondaria
End Struct


Public Sub Main()

 Dim sc As New Secondaria
 Dim pr As New PRINCIPALE

 Print Object.SizeOf(sc)
 Print Object.SizeOf(pr)

End

V'è da sottolineare che il secondo membro della Struttura, anche qui chiamata "PRINCIPALE", se fosse dichiarato senza la parola-chiave "Struct" non solleverebbe un errore di sintassi, ma la quantità di memria occupata riportata sarebbe errata: in questo caso l'istruzione con "Print" riporterebbe che la Struttura "PRINCIPALE" occupa 16 byte. Come sappiamo, invece, la Struttura, anche qui chiamata "PRINCIPALE", occupa 24 byte, e tale corretto valore viene riportato soltanto se il secondo membro, che fa riferimento alla Struttura, denominata "Secondaria", è dichiarato con la parola-chiave "Struct".

Problema riscontrato nella quantità di memoria occupata

Va rilevato che, però, nel caso della definizione in Gambas di una Struttura esterna, sctitta in C e avente come membro un riferimento a un'altra Struttura esterna, la quantità di memoria assunta da detta Struttura principale, ricreata in linguaggio Gambas, non sarà mai inferiore a 16 byte.
Probabilmente Gambas fa comunque riferimento all'Oggetto.

A tal riguardo si faccia questa prova.
Verifica in C:

 #include <stdio.h>


struct Secondaria {
   int i;
   int n;
};

struct PRINCIPALE {
   char c;
   struct Secondaria sc;
};


int main() {

   printf("%ld\n", sizeof(struct PRINCIPALE));	

   return (0);

}

Si verà che la dimensione della Struttura principale è pari a 12 byte.

Facendo invece la prova in Gambas:

Public Struct Secondaria
  i As Integer
  n As Integer
End Struct

Public Struct PRINCIPALE
  c As Byte
  sc As Struct Secondaria
End Struct


Public Sub Main()

 Dim pr As New PRINCIPALE

 Print Object.SizeOf(pr)

End

Il risultato è diverso: la Struttura principale risulterà avere una dimensione pari a 16 byte.

In tal caso (ossia, qualora la dimensione della Struttura principale in C sia inferiore di 16 byte) la soluzione sicura del problema nella fase di trasposizione in linguaggio Gambas sta nel non richiamare la Struttura secondaria esterna mediante uno specifico membro della Struttura principale chiamante, bensì innestando i singoli membri della Struttura secondaria all'interno di quella principale; facendo insomma sì che i singoli membri della Struttura secondaria diventino semplicemente membri propri della Struttura principale, nel rispetto ovviamente degli allineamenti eventualmente necessari.


Membro del tipo di una Struttura innestata (annidata) all'interno della Struttura principale

Se un membro della Struttura è 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 annidata.

Nel caso il membro, che si riferisce a una Struttura annidata, sia un Array con numero definito di elementi, la quantità che occupa la Struttura annidata va ovviamente moltiplicata per il numero espresso degli elementi dell'array.

Riguardo al numero d'indice all'interno dell'area puntata dal primo membro della Struttura annidata, vale quanto già esposto nel precedente paragrafo relativo alle Strutture esterne richiamate da un membro della Struttura principale.

Trasposizione in Gambas di una Struttura innestata

In Gambas non è possibile definire una Struttura "innestata" all'interno di una Struttura principale con modalità analoghe a quelle previste nel linguaggio C; pertanto si provvederà semplicemente a far diventare i singoli membri della 'Struttura "innestata" semplicemente membri propri della Struttura principale, nel rispetto ovviamente degli allineamenti eventualmente necessari.


Membro del tipo Puntatore a un'altra Struttura

Il membro di una Struttura, scritta in C, può essere dichiarato come variabile di tipo Puntatore al tipo di dato di un'altra Struttura.

L'argomento in questione è affrontato nella seguente pagina della Wiki:
Utilizzare come Struttura in Gambas una Struttura esterna dichiarata, come proprio membro di tipo Puntatore, da una Struttura principale esterna


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.


Allineamento dei membri nell'area di memoria allocata di una Struttura esterna scritta in C

Quando la quantità di byte, occupati dal tipo di dati di un membro, successivo al primo/span>, di una Struttura, è superiore alla quantità occupata dal tipo di dati del membro precedente, esso è posto al numero d'indice (offset ) uguale alla quantità di byte occupati dal tipo di dati di appartenenza e in ogni caso mai superiore a 8 byte. Se il numero d'indice è già occupato, allora la collocazione del membro scorre al numero d'indice uguale al multiplo della predetta quantità di byte, occupati da suo tipo di dati, immediatamente successivo purché libero.

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.

Così in C, se una Struttura è composta ad esempio da un membro di tipo "char" e da un membro di tipo "int", la quantità di memoria, occupata dal titpo di dati ("int" = 4 byte) del secondo membro, è superiore alla quantità occupata dal tipo di dati del primo ("char" = 1 byte), l'allineamento dei due membri all'interno dell'area di memoria riservata della Struttura sarà il seguente:

struct STRUTTURA {
   char c; /* Posizionato al numero d'indice 0; occupa un solo byte */
   int i;  /* Poiché il 2° membro occupa 4 byte, allora esso è collocato al numero d'indice 4, atteso che è libero */
};

Pertanto, la quantità complessiva di memoria occupata dai membri da questa Struttura è pari a 8 byte.

In quest'altra Struttura invece avremo:

struct STRUTTURA {
/* Il 1° membro è posizionato al numero d'indice 0 e occupa un solo byte */
   char c;
/* Poiché il 2° membro occupa 4 byte (numero superiore a quello del primo membro), allora esso è collocato al numero d'indice 4, atteso che è libero */
   int i;
/* Poiché la quantità occupata dal 3° membro è inferiore a quella del membro precedente, esso è posto al numero d'indice immediatamente libero dopo il membro precedente: ossia 8 */
   short int s;
/* Poiché il 4° membro occupa 4 byte (numero superiore a quello del membro immediatamente precedente), allora esso dovrebbe essereè collocato al numero d'indice 4. Essendo però tale numero d'indice già occupato, lo si porrà al primo numero d'indice multiplo di 4 libero: in questo caso 12 */
   int n;
/* Poiché questo ultimo membro occupa 8 byte, ossia una quantità superiore a quella occupata dal membro precedente, esso dovrebbe essere collocato al numero d'indice 8, ma essendo già occupato, si porrà al primo numero d'indice multiplo di 8 libero: in uesto caso 16 */
   long int l;
};

Pertanto, la quantità complessiva di memoria occupata dai membri da questa Struttura è pari a 24 byte.

Riassunto esemplificativo

Di seguito mostriamo una Struttura principale in linguaggio C che contiene in sé membri che riassumono i casi sopra esposti.
Nei commenti al codice sono specificati i numeri d'indice offset di inizio collocazione in memoria di ciascun membro della Struttura e la quantità di byte di memoria da ognuno occupata, tenendo anche in debito conto degli allineamenti dovuti.

#include <stdio.h>


struct Secondaria {   ' (Questa "Struttura" occupa 16 byte di memoria, considerando anche il necessario allineamento)
  char a;
  int i;
  char b[4];
  char c;
};

struct PRINCIPALE {
  char a;   ' Inizio indice 0; 1 byte di memoria occupato
            ' Essendo il prossimo membro un Intero, che occupa 4 byte, è richiesto un allineamento, affinché esso inizi a un indice pari o multiplo della quantità di memoria occupata dal tipo di dati "int"
  int i;    ' Inizio indice 4; 4 byte di memoria occupati

  char cc[4];   ' Vettore di tipo "char" con 4 elementi; inizio indice 12; 3x1 byte di memoria occupati
  char *c;      ' Puntatore a un tipo "char"; inizio indice 16; 8 byte di memoria occupati
  char *ccc[4]; ' Vettore di Puntatori a un tipo "char" con 4 elementi; inizio indice 24; 4x8 byte di memoria occupati

  struct Secondaria s;   ' Membro del tipo "struct Secondaria"; inizio indice 56; 16 byte di memoria occupati
  struct Secondaria *sp; ' Puntatore a un tipo "struct Secondaria"; inizio indice 72; 8 byte di memoria occupati
  struct Secondaria sa[4]; ' Vettore di tipo "struct Secondaria" con 4 elementi; inizio indice 80; 16x4 byte di memoria occupati
  struct Secondaria *spa[4]; ' Vettore di tipo Puntatore a un tipo "struct Secondaria"; inizio indice 144; 8x4 byte di memoria occupati

  struct INNESTATA {
     char ina;
     int ini;
     char inb[4];
     char inc;
  } inn;   ' Membro con Struttura innestata del tipo "struct INNESTATA"; inizio indice 176; 16 byte di memoria occupati

  union UNIONE {
     char unc; 
     short int uns;
     long int unl;
  } un;    ' Membro con una "Union" innestata ; inizio indice 192; 8 byte di memoria occupati
};


int main() {

  printf("%ld\n", sizeof(struct PRINCIPALE));

  return (0);

}

La quantità di memoria comlessiva, occupata dalla descritta Struttura principale, è pari a 200 byte.


Gestione delle "Union"

Una "Unione" è un'area di memoria allocata che può contenere - in momenti diversi - dati di diversa dimensione e tipo, comunque condividendo sempre il medesimo spazio di memoria. In tal senso lo spazio riservato per una "Union" corrisponde soltanto a quello necessario per il suo membro più grande. Pertanto la Union contiene il dato di un solo membro per volta.
Così se abbiamo questa "Unione":

union UNIONE {
  char c; 
  short int s;
  long int l;
}

la quantità di memoria occupata da tale esempio di Union è uguale a 8, ossia alla quantità di memoria occupata dal membro più grande (in tal caso quello di tipo "long int"). Inoltre, dovendosi potrà contenere ogni qual volta deve essere valorizzato un suo membro, il dato di uno solo dei suoi tre membri.

Gestione in Gambas di una "Union" emulandola con una "Struttura" avente un unico membro

In Gambas una "Union" può essere ricreata attraverso la usuale Classe "Struct", avendo l'accortezza di occupare una quantità di memoria pari alla totale quantità di memoria allocata per la "Union" della libreria esterna scritta in C.
Nel caso della "Union" precedente, potremo definire in Gambas:

Public Struct UNIONE
  l As Long
End Struct

Poniamo l'esempio di una "Union" restituita per "Valore" da una funzione esterna contenuta in una libreria condivisa da noi creata. Sebbene venga valorizzato il membro del tipo "int", che corrisponde in Gambas al tipo di dato "Integer", andremo a leggere l'unico membro di dimensioni maggiori che costituisce la "Struttura" che emula la "Union".

Public Struct Simula_Union
  l As Long
End Struct

' union UNIONE * funzione()
Private Extern funzione() As Simula_Union In "/tmp/libadhoc"


Public Sub Main()

 Dim un As Simula_Union

 Creaso()

 un = funzione()

 Print CInt(un.l)

End

Private Procedure Creaso()
 
 File.Save("/tmp/libadhoc.c", "#include <stdlib.h>\n\n" &
                              "union UNIONE {\n" &
                              "   int i;\n" &
                              "   long int l;\n" &
                              "   short int s;\n};\n\n" &
                              "union UNIONE *funzione() {\n" &
                              "   union UNIONE *un = malloc(sizeof(union UNIONE));\n" &
                              "   un->i = 4660;\n" &
                              "   return un;\n}")
 
 Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared" Wait
 
End

Se invece la "Union" è passata e restituita per "Indirizzo", allora avremo:

Public Struct Simula_Union
  l As Long
End Struct

' void funzione(union UNIONE *un)
Private Extern funzione(un As Simula_Union) In "/tmp/libadhoc"


Public Sub Main()

 Dim sim_uni As New Simula_Union

 Creaso()

 funzione(sim_uni)

 Print CInt(sim_uni.l)

End

Private Procedure Creaso()
 
 File.Save("/tmp/libadhoc.c",   "union UNIONE {\n" &
                              "   int i;\n" &
                              "   long int l;\n" &
                              "   short int s;\n};\n\n" &
                              "void funzione(union UNIONE *un) {\n" &
                              "   un->i = 4660;\n}")
 
 Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared" Wait
 
End

Gestione in Gambas di una "Union" emulandola con un Puntatore

La "Union" altresì può essere gestita mediante una variabile di tipo Puntatore.
Poniamo l'esempio di una "Union" restituita per "Valore" da una funzione esterna contenuta in una libreria condivisa da noi creata. Poiché viene valorizzato il membro del tipo "int", che corrisponde in Gambas al tipo di dato "Integer", otterremo il valore usando la funzione propria di Gambas per di dereferenziazione "Int@()".

' union UNIONE * funzione()
Private Extern funzione() As Pointer In "/tmp/libadhoc"


Public Sub Main()

 Dim p As Pointer
 Dim i As Integer
 
 Creaso()
 
 p = funzione()

 Print Int@(p)

End

Private Procedure Creaso()
 
 File.Save("/tmp/libadhoc.c", "union UNIONE {\n" &
                              "   int i;\n" &
                              "   long int l;\n" &
                              "   short int s;\n};\n\n" &
                              "union UNIONE un;\n\n" &
                              "union UNIONE *funzione() {\n" &
                              "   un.i = 4660;\n" &
                              "   return &un;\n}")
 
 Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared -fPIC" Wait
 
End

oppure anche:

Private Procedure Creaso()
 
 File.Save("/tmp/libadhoc.c", "#include <stdlib.h>\n\n" &
                              "union UNIONE {\n" &
                              "   int i;\n" &
                              "   long int l;\n" &
                              "   short int s;\n};\n\n" &
                              "union UNIONE *funzione() {\n" &
                              "   union UNIONE *un = malloc(sizeof(union UNIONE));\n" &
                              "   un->i = 4660;\n" &
                              "   return un;\n}")
 
 Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared" Wait
 
End

Gestione dei Campi bit di una Struttura

Gambas non prevede nella gestione delle proprie Strutture l'uso dei "Campi" che fanno riferimento ai singoli bit.
Ad ogni modo, poiché in C ogni insieme di "Campi" occupa una quantità di memoria pari a un "Intero", in Gambas si dovrà provare ad utilizzare un membro di tipo di dato "Integer", assegnandogli un valore tale che i suoi bit siano posti a 1 e/o a 0, come dovuto. Nel fare ciò, bisognerà tenere conto che il primo identificatore di bit di "Campo", all'interno di una Struttura esterna scritta in C, corrisponde al bit meno significativo del tipo "Integer" (ossia quello più a destra nella rappresentazione binaria di un numero).

Vediamo di seguito un esempio pratico di utilizzo dei Campi con Gambas, per il quale sarà necessario creare un'apposita libreria esterna:

Private Extern campi(cmp As Pointer) As Pointer In "/tmp/libcampi"


Public Sub Main()

' I bit che saranno elevati a 1, partendo dal 2° bit (dato che il flag del primo bit effettivo è stato posto a zero nell'array "bb" e qui contrassegnato in rosso) come fosse il primo identificato con il valore 1 (il 3° bit con valore 2 e così via).
' In questo caso, cominciando dal 2° bit da destra), il valore 11 è dato da 3 bit:
'  1 (che corrisponde al 2° bit da destra avente valore  2) +
'  2 (che corrisponde al 3° bit da destra avente valore  4) +
'  8 (che corrisponde al 4° bit da destra avente valore 16) =
' 11                                                    22  +
'  1 (che corrisponde al 6° bit da destra avente valore 32) =
'                                                       54  +
'  1 (che corrisponde al 10° bit da destra con valore  512) =
'                                             totale:  566
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Il 1° bit da destra 
 Dim bb As Byte[] = [0, 11, 1, 0, 0, 0, 1, 0]

 Dim p As Pointer

 Creaso()

 p = campi(bb.Data)
 If p == 0 Then Error.Raise("Errore !")

 Print CInt(p), "&h"; CStr(p)

End

Private Procedure Creaso()
 
 File.Save("/tmp/libcampi.c", "struct STRUTTURA {\n" &
"/* Numero di bit necessari da coinvolgere per ciascun Campo a partire dal meno-significativo:*/\n" &
           "   unsigned a: 1;\n" &  ' occupa un solo bit a cominciare dal 1° bit da destra
           "   unsigned b: 4;\n" &  ' occupa 4 bit a cominciare dal 2° bit da destra
           "   unsigned c: 1;\n" &  ' occupa un bit a cominciare dal 6° bit da destra (valore-byte 32)
           "   unsigned d: 1;\n" &  ' etc...
           "   unsigned e: 1;\n" &
           "   unsigned f: 1;\n" &
           "   unsigned g: 1;\n};\n\n" &
           "struct STRUTTURA st;\n\n" &
           "struct STRUTTURA campi(char *cc) {\n" &
           "   st.a = cc[0];\n" &
           "   st.b = cc[1];\n" &
           "   st.c = cc[2];\n" &
           "   st.d = cc[3];\n" &
           "   st.e = cc[4];\n" &
           "   st.f = cc[5];\n" &
           "   st.g = cc[6];\n" &
           "   return st;\n}")

 Shell "gcc -o /tmp/libcampi.so /tmp/libcampi.c -shared -fPIC" Wait
 
End


Note

[1] Si veda anche questa pagina: Gestire con un Puntatore le Strutture esterne

[2] Per un approccio diverso alle Strutture esterne, si veda la seguente pagina: Gestire con sicurezza le Strutture esterne