Quello che stai dicendo nel tuo post è assolutamente corretto. Direi che ogni sviluppatore C arriva esattamente alla stessa scoperta e alla stessa conclusione quando (se) raggiunge un certo livello di competenza con il linguaggio C.
Quando le specifiche dell'area di applicazione richiedono un array di dimensioni fisse specifiche (la dimensione dell'array è una costante del tempo di compilazione), l'unico modo corretto per passare tale array a una funzione è usare un parametro pointer-to-array
void foo(char (*p)[10]);
(nel linguaggio C ++ questo viene fatto anche con i riferimenti
void foo(char (&p)[10]);
).
Ciò abiliterà il controllo del tipo a livello di lingua, che assicurerà che l'array di dimensioni esattamente corrette sia fornito come argomento. Infatti in molti casi le persone usano questa tecnica implicitamente, senza nemmeno rendersene conto, nascondendo il tipo di array dietro un nome typedef
typedef int Vector3d[3];
void transform(Vector3d *vector);
/* equivalent to `void transform(int (*vector)[3])` */
...
Vector3d vec;
...
transform(&vec);
Nota inoltre che il codice sopra è invariante in relazione al Vector3d
tipo che è un array o un file struct
. Puoi cambiare la definizione di Vector3d
in qualsiasi momento da un array a un filestruct
e viceversa, e non dovrai cambiare la dichiarazione della funzione. In entrambi i casi le funzioni riceveranno un oggetto aggregato "per riferimento" (ci sono eccezioni a questo, ma nel contesto di questa discussione questo è vero).
Tuttavia, non vedrai questo metodo di passaggio di array usato esplicitamente troppo spesso, semplicemente perché troppe persone sono confuse da una sintassi piuttosto contorta e semplicemente non sono abbastanza a proprio agio con tali caratteristiche del linguaggio C per usarle correttamente. Per questo motivo, nella vita reale media, passare un array come puntatore al suo primo elemento è un approccio più popolare. Sembra solo "più semplice".
Ma in realtà, usare il puntatore al primo elemento per il passaggio di array è una tecnica molto di nicchia, un trucco, che ha uno scopo ben preciso: il suo unico scopo è quello di facilitare il passaggio di array di dimensioni diverse (es. Dimensione del runtime) . Se hai davvero bisogno di essere in grado di elaborare array di dimensioni runtime, il modo corretto per passare tale array è un puntatore al suo primo elemento con la dimensione concreta fornita da un parametro aggiuntivo
void foo(char p[], unsigned plen);
In realtà, in molti casi è molto utile essere in grado di elaborare array di dimensioni runtime, il che contribuisce anche alla popolarità del metodo. Molti sviluppatori C semplicemente non incontrano mai (o non riconoscono mai) la necessità di elaborare un array a dimensione fissa, rimanendo così ignari della corretta tecnica a dimensione fissa.
Tuttavia, se la dimensione dell'array è fissa, passarla come puntatore a un elemento
void foo(char p[])
è un grave errore a livello di tecnica, che purtroppo è piuttosto diffuso in questi giorni. Una tecnica pointer-to-array è un approccio molto migliore in questi casi.
Un altro motivo che potrebbe ostacolare l'adozione della tecnica di passaggio di array a dimensione fissa è il predominio dell'approccio ingenuo alla tipizzazione di array allocati dinamicamente. Ad esempio, se il programma richiede array di tipo fissi char[10]
(come nel tuo esempio), uno sviluppatore medio utilizzerà malloc
array come
char *p = malloc(10 * sizeof *p);
Questo array non può essere passato a una funzione dichiarata come
void foo(char (*p)[10]);
il che confonde lo sviluppatore medio e gli fa abbandonare la dichiarazione del parametro a dimensione fissa senza pensarci ulteriormente. In realtà, però, la radice del problema sta nell'approccio ingenuo malloc
. Il malloc
formato mostrato sopra dovrebbe essere riservato per array di dimensioni runtime. Se il tipo di array ha una dimensione in fase di compilazione, un modo migliore per malloc
ottenerlo sarebbe il seguente
char (*p)[10] = malloc(sizeof *p);
Questo, ovviamente, può essere facilmente passato a quanto sopra dichiarato foo
foo(p);
e il compilatore eseguirà il controllo del tipo appropriato. Ma ancora una volta, questo è eccessivamente confuso per uno sviluppatore C non preparato, motivo per cui non lo vedrai troppo spesso nel codice "tipico" medio di tutti i giorni.
10
può essere sostituito da qualsiasi variabile nell'ambito