Utilizzo di array standard in C con decadimento del tipo naturale da array a ptr
@Bo Persson afferma correttamente nella sua ottima risposta qui :
Quando si passa un array come parametro, this
void arraytest(int a[])
significa esattamente lo stesso di
void arraytest(int *a)
Tuttavia, lasciatemi aggiungere anche che le due forme precedenti anche:
significa esattamente lo stesso di
void arraytest(int a[0])
che significa esattamente lo stesso di
void arraytest(int a[1])
che significa esattamente lo stesso di
void arraytest(int a[2])
che significa esattamente lo stesso di
void arraytest(int a[1000])
eccetera.
In ognuno degli esempi di array sopra, il tipo di parametro di input decade in unint *
e può essere chiamato senza avvisi e senza errori, anche con le opzioni di compilazione -Wall -Wextra -Werror
attivate (vedi il mio repository qui per i dettagli su queste 3 opzioni di compilazione), come Questo:
int array1[2];
int * array2 = array1;
arraytest(array1);
arraytest(array2);
È un dato di fatto, il valore "dimensioni" ( [0]
, [1]
, [2]
, [1000]
, etc.) all'interno del parametro array qui è apparentemente solo per scopi di / estetica auto-documentazione, e può essere un qualsiasi numero intero positivo ( size_t
tipo credo) che si desidera!
In pratica, tuttavia, dovresti usarlo per specificare la dimensione minima dell'array che ti aspetti che la funzione riceva, in modo che durante la scrittura del codice sia facile per te tracciare e verificare. Lo standard MISRA-C-2012 ( acquista / scarica il PDF della versione 236-pg 2012 dello standard per £ 15,00 qui ) arriva fino a dichiarare (enfasi aggiunta):
Regola 17.5 L'argomento della funzione corrispondente a un parametro dichiarato di avere un tipo array deve avere un numero appropriato di elementi.
...
Se un parametro viene dichiarato come un array con una dimensione specificata, l'argomento corrispondente in ciascuna chiamata di funzione dovrebbe puntare a un oggetto che ha almeno tanti elementi quanti sono l'array.
...
L'uso di un dichiaratore di matrice per un parametro di funzione specifica l'interfaccia della funzione in modo più chiaro rispetto all'utilizzo di un puntatore. Il numero minimo di elementi attesi dalla funzione è dichiarato esplicitamente, mentre ciò non è possibile con un puntatore.
In altre parole, raccomandano di utilizzare il formato di dimensione esplicito, anche se lo standard C tecnicamente non lo impone - almeno aiuta a chiarire a te come sviluppatore e agli altri che usano il codice, quale matrice di dimensioni si aspetta la funzione di passare.
Forzare l'indipendenza dai tipi sugli array in C
Come sottolinea @Winger Sendon in un commento sotto la mia risposta, possiamo forzare C a considerare un tipo di array diverso in base alla dimensione dell'array !
Innanzitutto, devi riconoscere che nel mio esempio appena sopra, usando int array1[2];
così: arraytest(array1);
fa array1
decadere automaticamente in un file int *
. TUTTAVIA, se prendi l' indirizzo di array1
invece e chiami arraytest(&array1)
, ottieni un comportamento completamente diverso! Ora, NON decade in un int *
! Invece, il tipo di &array1
è int (*)[2]
, che significa "puntatore a un array di dimensione 2 di int" o "puntatore a un array di dimensione 2 di tipo int" . Quindi, puoi FORZA C per verificare l'indipendenza dai tipi su un array, in questo modo:
void arraytest(int (*a)[2])
{
}
Questa sintassi è difficile da leggere, ma simile a quella di un puntatore a funzione . Lo strumento online, cdecl , ci dice che int (*a)[2]
significa: "dichiara a come puntatore all'array 2 di int" (puntatore all'array di 2 int
s). NON confondere questo con la versione senza parentesi:, int * a[2]
che significa: "dichiara a come array 2 di puntatore a int" (array di 2 puntatori a int
).
Ora, questa funzione RICHIEDE che tu la chiami con l'operatore indirizzo ( &
) in questo modo, utilizzando come parametro di input un PUNTATORE AD UNA SERIE DELLA DIMENSIONE CORRETTA !:
int array1[2];
arraytest(&array1);
Questo, tuttavia, produrrà un avviso:
int array1[2];
arraytest(array1);
Puoi provare questo codice qui .
Per forzare il compilatore C a trasformare questo avviso in un errore, in modo che DEVI sempre chiamare arraytest(&array1);
utilizzando solo un array di input della dimensione e del tipo corretti ( int array1[2];
in questo caso), aggiungi -Werror
alle opzioni di compilazione. Se esegui il codice di prova sopra su onlinegdb.com, fallo facendo clic sull'icona dell'ingranaggio in alto a destra e fai clic su "Extra Compiler Flags" per digitare questa opzione. Ora, questo avviso:
main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
si trasformerà in questo errore di build:
main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
arraytest(array1);
^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
void arraytest(int (*a)[2])
^~~~~~~~~
cc1: all warnings being treated as errors
Nota che puoi anche creare puntatori "type safe" ad array di una data dimensione, in questo modo:
int array[2];
int (*array_p)[2] = &array;
... ma non lo consiglio necessariamente , poiché mi ricorda molte delle buffonate C ++ utilizzate per forzare la sicurezza dei tipi ovunque, al costo eccezionalmente alto della complessità della sintassi del linguaggio, della verbosità e della difficoltà di architettura del codice, e che non mi piace e ho inveito molte volte in passato (es: vedi "I miei pensieri su C ++" qui ).
Per ulteriori test e sperimentazioni, vedere anche il collegamento appena sotto.
Riferimenti
Vedi i link sopra. Anche:
- La mia sperimentazione di codice online: https://onlinegdb.com/B1RsrBDFD