Ci sono alcuni approcci che è possibile eseguire, uno dei quali riguarda la memorizzazione di un void*
nell'ADT.
Ho sempre trovato che questo è un po 'un dolore in una lista collegata dal momento che devi gestire la sua allocazione separatamente alla lista stessa. In altre parole, per allocare un nodo, è necessario posizionare separatamente sia il nodo che il suo payload (e ricordarsi di eliminarli entrambi alla cancellazione).
Un approccio che ho usato in passato è quello di avere una struttura 'di dimensione variabile' come:
typedef struct _tNode {
struct _tNode *prev;
struct _tNode *next;
char payload[1];
} tNode;
Ora questo non guardare variabile dimensioni ma cerchiamo di allocare una struttura così:
typedef struct {
char Name[30];
char Addr[50];
char Phone[20];
} tPerson;
tNode *node = malloc (sizeof (tNode) - 1 + sizeof (tPerson));
Ora avete un nodo che, per tutti gli effetti, si presenta così:
typedef struct _tNode {
struct _tNode *prev;
struct _tNode *next;
char Name[30];
char Addr[50];
char Phone[20];
} tNode;
o, in forma grafica (dove [n]
significa n
byte):
+------------+
| prev[4] |
+------------+
| next[4] |
+------------+ +-----------+
| payload[1] | | Name[30] | <- overlap
+------------+ +-----------+
| Addr[50] |
+-----------+
| Phone[20] |
+-----------+
vale a dire, a patto di saper come affrontare correttamente il carico utile. Questo può essere fatto come segue:
node->prev = NULL;
node->next = NULL;
tPerson *person = &(node->payload); // cast for easy changes to payload.
strcpy (person->Name, "Richard Cranium");
strcpy (person->Addr, "10 Smith St");
strcpy (person->Phone, "555-5555");
che gettano linea getta semplicemente l'indirizzo del personaggio payload
(nel tipo tNode
) per essere un indirizzo del tipo tPerson
carico utile effettivo.
Utilizzando questo metodo, è possibile trasportare qualsiasi tipo di carico utile che si desidera in un nodo, anche diversi tipi di payload in ogni nodo, se si effettua la struttura più simile:
typedef struct _tNode {
struct _tNode *prev;
struct _tNode *next;
int payloadType; // Allows different payload type at each node.
char payload[1];
} tNode;
e utilizzare payloadType
per memorizzare un indicatore di ciò che il carico utile è in realtà.
Questo ha il vantaggio rispetto un'unione nel senso che non spreca spazio, come si può vedere con il seguente:
union {
int fourBytes;
char oneHundredBytes[100];
} u;
cui 96 byte sono sprecati ogni volta che si memorizza un tipo intero nella lista (per un intero di 4 byte).
Il tipo di payload nel tNode
consente di rilevare facilmente il tipo di payload che trasporta questo nodo, in modo che il codice possa decidere come elaborarlo. È possibile utilizzare qualcosa sulla falsariga di:
#define PAYLOAD_UNKNOWN 0
#define PAYLOAD_MANAGER 1
#define PAYLOAD_EMPLOYEE 2
#define PAYLOAD_CONTRACTOR 3
o (probabilmente migliore):
typedef enum {
PAYLOAD_UNKNOWN,
PAYLOAD_MANAGER,
PAYLOAD_EMPLOYEE,
PAYLOAD_CONTRACTOR
} tPayLoad;
L'unica cosa che dovete guardare fuori per è quello di garantire che l'allineamento del carico utile è corretta. Poiché sia il mio segnaposto payload che il payload sono tutti i tipi char
, non è un problema. Tuttavia, se il carico utile è costituito da tipi con requisiti di allineamento più stringenti (ad esempio qualcosa di più severo dei puntatori, potrebbe essere necessario regolarlo).
Anche se non ho mai visto un ambiente con allineamenti più rigidi dei puntatori, è possibile secondo lo standard ISO C.
Di solito è possibile ottenere l'allineamento richiesto semplicemente utilizzando un tipo di dati per il segnaposto payload, che ha l'obbligo di allineamento più rigoroso come ad esempio:
long payload;
Col senno di poi, mi viene in mente che probabilmente non necessario un array come segnaposto del payload. È abbastanza semplice avere solo qualcosa a cui puoi prendere l'indirizzo. Sospetto che quel particolare idioma dei miei ricordi risalga ai giorni in cui ho appena archiviato una serie di caratteri (piuttosto che una struttura) e li ho citati direttamente. In tal caso, è possibile utilizzare payload[]
da solo senza eseguire il casting in un altro tipo.
Personalmente uso 'char payload [0]', quindi 'sizeof' rappresenta l'intestazione e niente più. – strager
Spiega che è necessario eseguire il cast del payload, ma è semplicemente dereferenziato nel tuo esempio. – strager
I tipi di vettore a 128 bit su x86 (utilizzati dal set di istruzioni SSE) richiedono un allineamento di 16 byte, ad esempio. – zvrba