Differenze tra le versioni di "Gestione delle Strutture esterne"

Da Gambas-it.org - Wikipedia.
Riga 106: Riga 106:
  
  
==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 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.
 
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 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.
  

Versione delle 09:18, 14 ott 2022

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

Paragrafo in costruzione !


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 sarà posizionato al byte pari al numero d'indice corrispondente alla quantità di memria occupata, se libero, e comunque non superiore a 8. La memria 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);

}

In Gambas le due Strutture 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".


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

Paragrafo in costruzione !


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 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.

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.
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".
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 membro (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 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 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 mebro 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 dei Campi bit di una Struttura

Paragrafo in costruzione !


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