2009-03-19 13 views
5

Inizierò presto a utilizzare C per un corso sui sistemi operativi e sto leggendo le migliori pratiche sull'uso di C in modo che i mal di testa si riducano in seguito.È consigliabile archiviare gli array e la loro variabile di lunghezza in una struttura in C?

Questa è sempre stata una delle mie prime domande sugli array in quanto sono facili da rovinare.

È una pratica comune là fuori per raggruppare una matrice e la sua variabile associata che contiene la sua lunghezza in una struttura?

Non l'ho mai visto nei libri e di solito tengono sempre separati i due o usano qualcosa come sizeof (array []/array [1]) tipo di accordo.

Ma con il wrapping dei due in una struct, sarete in grado di passare la struct sia per valore che per riferimento che non potete realmente fare con gli array se non usando i puntatori, nel qual caso dovete tenere di nuovo traccia della lunghezza dell'array.

Sto iniziando a usare C quindi quanto sopra potrebbe essere orribilmente sbagliato, sono ancora uno studente.

Cheers, Kai.

+0

Grazie per le risposte a tutti! Nota: la mia dimensione d'uso era sbagliata, grazie allo svolgimento per averlo indicato e anche per l'esempio reale di GArray in glib. – Kai

risposta

3

Certo, puoi farlo. Non sono sicuro se lo definirei una best practice, ma è certamente una buona idea rendere gli array piuttosto rudimentali di C un po 'più gestibili. Se hai bisogno di array dinamici, è quasi obbligatorio raggruppare i vari campi necessari per la contabilità insieme.

A volte si hanno due dimensioni in questo caso: una corrente e una assegnata. Questo è un compromesso in cui si scambiano meno allocazioni per una certa velocità, pagando con un po 'di overhead di memoria.

Molte volte gli array vengono utilizzati solo localmente e sono di dimensioni statiche, motivo per cui l'operatore sizeof è così comodo da determinare il numero di elementi. La sintassi è leggermente fuori con quello, a proposito, ecco come appare di solito:

int array[4711]; 
int i; 

for(i = 0; i < sizeof array/sizeof *array; i++) 
{ 
    /* Do stuff with each element. */ 
} 

Ricordate che sizeof non è una funzione, la parentesi non sono sempre necessari.

EDIT: Un esempio reale di un involucro esattamente come quella che descrivi è il tipo GArray fornito da glib. La parte visibile all'utente della dichiarazione è esattamente ciò che si descrive:

typedef struct { 
    gchar *data; 
    guint len; 
} GArray; 

programmi sono tenuti a utilizzare l'API per accedere alla matrice quando possibile, non colpire direttamente questi campi.

1

Non vedo nulla di sbagliato nel farlo, ma penso che la ragione per cui ciò non viene solitamente fatto è a causa del sovraccarico sostenuto da tale struttura. La maggior parte di C è un codice bare metal per motivi di prestazioni e, come tale, le astrazioni vengono spesso evitate.

+0

Hmmm ... non è un po 'di generalizzazione? –

5

Sì, questa è un'ottima pratica da avere in C. È completamente logico racchiudere i valori correlati in una struttura contenente.

Vorrei andare sempre più lontano. Servirebbe anche allo scopo di non modificare questi valori direttamente. Invece scrivi funzioni che agiscono su questi valori come una coppia all'interno della tua struttura per modificare la lunghezza e alterare i dati. In questo modo puoi aggiungere controlli invarianti e anche rendere molto facile il test.

0

Non l'ho mai visto fare in quel modo, ma non ho fatto il lavoro a livello di OS in oltre un decennio ... :-) Sembra un approccio ragionevole a prima vista. Unica preoccupazione sarebbe quella di assicurarsi che le dimensioni in qualche modo rimangano corrette ... Il calcolo secondo necessità non ha questa preoccupazione.

1

Non l'ho visto nemmeno nei libri, ma ho fatto la stessa cosa per un po 'di tempo. Sembra avere senso "confezionare" quelle cose insieme. Lo trovo particolarmente utile se è necessario restituire un array assegnato da un metodo, ad esempio.

0

considerando che è possibile calcolare la lunghezza dell'array (in byte, cioè non # di elementi) e il compilatore sostituirà le chiamate sizeof() con il valore effettivo (non è una funzione, le chiamate vengono sostituite dal compilatore con il valore 'restituisce'), quindi l'unica ragione per cui si desidera racchiuderlo in una struttura è la leggibilità.

Non è una pratica comune, se l'hai fatto, qualcuno guardando il tuo codice assumerebbe che il campo della lunghezza fosse un valore speciale e non solo la dimensione dell'array. Questo è un pericolo in agguato nella tua proposta, quindi dovresti fare attenzione a commentare correttamente.

+0

Il problema di fare affidamento su sizeof è che non è di aiuto con gli array allocati dinamicamente. –

+0

... o quando si passa la matrice a una funzione con un puntatore. –

3

Ci sono tre modi.

  1. Per matrice statica (non allocati dinamicamente e non passato come puntatore) formato è sa al momento della compilazione in modo da poter utilizzare sizeof operatore, in questo modo: sizeof(array)/sizeof(array[0])
  2. Uso terminatore (valore speciale per l'ultimo elemento di matrice che non può essere usato come valore di array normale), come stringhe con terminazione null
  3. Utilizzare un valore separato, come membro della struttura o variabile indipendente. Non ha molta importanza perché tutte le funzioni standard che funzionano con gli array prendono variabili di dimensione separate, tuttavia unire il puntatore e la dimensione dell'array in una struttura aumenterà la leggibilità del codice. Suggerisco di usare per avere un'interfaccia più pulita per le tue funzioni. Si noti che se si passa la struct in base al valore, la funzione chiamata sarà in grado di modificare la matrice, ma non la variabile delle dimensioni, quindi passare il puntatore della struttura sarebbe un'opzione migliore.
+0

sull'ultimo punto sulla modifica della matrice se si passa per valore. Vuoi dire che puoi cambiare l'array stesso o gli oggetti indicati? (Se si tratta di una schiera di puntatori.) Altrimenti, non vedo come è possibile cambiare uno ma non l'altro. – mmccoo

+0

La struttura id contiene il puntatore all'array e alla dimensione dell'array (come ho menzionato), la modifica dell'array non sarà un problema, perché il puntatore dell'array verrà passato per valore, non lo stesso array – qrdl

1

Se si utilizzano array statici, è possibile accedere alle dimensioni della matrice utilizzando l'operatore sizeof. Se lo metti in struct, puoi passarlo a funzione di valore, riferimento e puntatore. Passare argomento per riferimento e per puntatore è lo stesso a livello di assembly (ne sono quasi sicuro).

Ma se si utilizzano gli array dinamici, non si conosce la dimensione della matrice in fase di compilazione. Così è possibile memorizzare questo valore nel struct, ma vi sarà anche memorizzare solo un puntatore a matrice in struttura:

struct Foo { 
    int *myarray; 
    int size; 
}; 

in modo da poter passare tale struttura per valore, ma quello che davvero fa sta passando il puntatore a int (puntatore alla matrice) e int (dimensione della matrice).

Secondo me non ti aiuterà molto. L'unica cosa positiva è che le dimensioni e la matrice vengono archiviate in un unico punto ed è facile ottenere la dimensione dell'array. Se utilizzerai molti array dinamici, puoi farlo in questo modo. Ma se userete pochi array, sarà più semplice non usare le strutture.

+0

ANSI C non supporta il passaggio di variabili per riferimento . Forse stai pensando al C++? –

+0

Stavo pensando a C, ma è stato tanto tempo fa quando stavo scrivendo in C, quindi è un mio errore. – klew

+0

+1 per "L'unica cosa positiva è che si memorizzano le dimensioni e l'array in un unico punto ...". Ti salverà un nome di variabile e un parametro di funzione, ma nessuna memoria o velocità. – potrzebie

2

Per l'API pubblica, sceglierei l'array e il valore della dimensione separato. È così che viene gestito nella maggior parte (se non in tutti) della libreria c che conosco. Come lo gestisci internamente dipende completamente da te. Quindi usare una struttura più alcune funzioni di supporto/macro che fanno le parti difficili per te è una buona idea.Mi fa sempre mal di testa ripensare a come inserire un oggetto o rimuoverlo, quindi è una buona fonte di problemi. Risolvendola una volta e generica, ti aiuta a ottenere bug sin dall'inizio in basso. Una buona implementazione per array dinamici e generici è kvec.

+0

Non vorrei usare 'struct {void * ptr; size_t len;}; 'come parametro per * qualsiasi * funzione API, perché perdi la capacità di es. ordina solo parte di una lista passando un indirizzo diverso rispetto al primo elemento e/o passando una lunghezza diversa dall'intera lunghezza della matrice. – potrzebie

+0

... quindi se si desidera l'astrazione, ma è comunque necessario accedere a parti di matrici, una struttura arraySlice probabilmente andrebbe meglio. È possibile avere una variabile arraySlice che punta e copre l'intero array, e altre variabili/valori arraySlice che puntano e si estende sulle parti su cui si sta lavorando. – potrzebie

2

Direi che è una buona pratica. In effetti, è abbastanza buono che in C++ lo abbiano inserito nella libreria standard e lo abbiano chiamato vector. Ogni volta che parli di array in un forum C++, sarai sommerso da risposte che dicono di usare vector invece.

0

Penso questa parte della tua domanda è indietro:

"Ma con avvolgendo i due in una struct, si sarebbe in grado di passare lo struct sia per valore e per riferimento, che non si può davvero fare con gli array a meno che non si utilizzino i puntatori, nel qual caso è necessario tenere nuovamente traccia della lunghezza dell'array. "

L'utilizzo di puntatori quando si passano matrici è il comportamento predefinito; che non ti compra nulla in termini di passaggio dell'intero array in base al valore. Se si desidera copiare l'intero array anziché decadere su un puntatore, è necessario avvolgere l'array in una struttura. Vedere questa risposta per ulteriori informazioni:

Pass array by value to recursive function possible?

E qui è più sul comportamento speciale di array:

http://denniskubes.com/2012/08/20/is-c-pass-by-value-or-reference/

Problemi correlati