Passando un array come argomento a una funzione in C

Passando un array come argomento a una funzione in C

Quando si passa un array come parametro, questo

void arraytest(int a[])

significa esattamente come

void arraytest(int *a)

quindi sei modificando i valori in main.

Per ragioni storiche, gli array non sono cittadini di prima classe e non possono essere passati per valore.


1. Utilizzo standard dell'array in C con decadimento del tipo naturale da array a ptr

@Bo Persson afferma correttamente nella sua ottima risposta qui:

Tuttavia, permettetemi di aggiungere anche che le due forme precedenti anche:

  1. significano esattamente come

     void arraytest(int a[0])
    
  2. che significa esattamente lo stesso di

     void arraytest(int a[1])
    
  3. che significa esattamente lo stesso di

     void arraytest(int a[2])
    
  4. che significa esattamente lo stesso di

     void arraytest(int a[1000])
    
  5. ecc.

In ogni singolo esempio di array sopra, e come mostrato nelle chiamate di esempio nel codice appena sotto, il tipo di parametro di input decade in un int * , e può essere chiamato senza avvisi e senza errori, anche con le opzioni di build -Wall -Wextra -Werror attivato (vedi il mio repository qui per i dettagli su queste 3 opzioni di build), in questo modo:

int array1[2];
int * array2 = array1;

// works fine because `array1` automatically decays from an array type
// to `int *`
arraytest(array1);
// works fine because `array2` is already an `int *` 
arraytest(array2);

Infatti, il valore "size" ([0] , [1] , [2] , [1000] , ecc.) all'interno del parametro array qui è apparentemente solo per scopi estetici/di autodocumentazione e può essere qualsiasi numero intero positivo (size_t digita penso) vuoi!

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 tracciare e verificare. Lo standard MISRA-C-2012 (acquista/scarica qui il PDF della versione 2012 236-pg dello standard per £ 15,00) arriva al punto di affermare (enfasi aggiunta):

In altre parole, consigliano 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 ad altri che usano il codice, quale matrice di dimensioni è la funzione si aspetta che tu accetti.

2. Forzare la sicurezza del tipo sugli array in C

(Non consigliato, ma possibile. Vedi il mio breve argomento contro il farlo alla fine.)

Come sottolinea @Winger Sendon in un commento sotto la mia risposta, possiamo forzare C a trattare un tipo di array essere diverso in base alla dimensione dell'array !

Innanzitutto, devi riconoscerlo nel mio esempio appena sopra, usando il int array1[2]; in questo modo:arraytest(array1); causa array1 per decadere automaticamente in un int * . TUTTAVIA, se prendi l'indirizzo di array1 invece e chiama arraytest(&array1) , ottieni un comportamento completamente diverso! Ora, NON decade in un int * ! Invece, il tipo di &array1 è int (*)[2] , che significa "puntatore a una matrice di dimensione 2 di int" o "puntatore a una matrice di dimensione 2 di tipo int" , o detto anche come "puntatore a un array di 2 int" . Quindi, puoi FORZARE C per verificare la sicurezza dei tipi su un array, come questo:

void arraytest(int (*a)[2])
{
    // my function here
}

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 confonderlo con la versione senza parentesi:int * a[2] , che significa:"dichiara a come array 2 del puntatore a int" (AKA:array di 2 puntatori a int , AKA:matrice di 2 int* s).

Ora, questa funzione RICHIEDE di chiamarla con l'operatore dell'indirizzo (& ) in questo modo, utilizzando come parametro di input un PUNTATORE A UN ARRAY DELLA DIMENSIONE CORRETTA!:

int array1[2];

// ok, since the type of `array1` is `int (*)[2]` (ptr to array of 
// 2 ints)
arraytest(&array1); // you must use the & operator here to prevent
                    // `array1` from otherwise automatically decaying
                    // into `int *`, which is the WRONG input type here!

Questo, tuttavia, produrrà un avviso:

int array1[2];

// WARNING! Wrong type since the type of `array1` decays to `int *`:
//      main.c:32:15: warning: passing argument 1 of ‘arraytest’ from 
//      incompatible pointer type [-Wincompatible-pointer-types]                                                            
//      main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
arraytest(array1); // (missing & operator)

Puoi testare 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 corretta e digita (int array1[2]; in questo caso), aggiungi -Werror alle tue opzioni di costruzione. Se esegui il codice di test sopra su onlinegdb.com, fallo facendo clic sull'icona a forma di ingranaggio in alto a destra e fai clic su "Bandiere del compilatore extra" per digitare questa opzione. Ora, questo avviso:

si trasformerà in questo errore di compilazione:

Tieni presente che puoi anche creare puntatori "di tipo sicuro" a matrici di una determinata dimensione, come questo:

int array[2];
// "type safe" ptr to array of size 2 of int:
int (*array_p)[2] = &array;

...ma NON lo faccio necessariamente consiglio questo (usando questi array "type safe" in C), poiché mi ricorda molte delle buffonate C++ usate per forzare la sicurezza dei tipi ovunque, al costo eccezionalmente alto della complessità della sintassi del linguaggio, della verbosità e della difficoltà di architettare il codice e che Non mi piace e ho parlato molte volte prima (es:vedi "I miei pensieri su C++" qui).

Per ulteriori test e sperimentazioni, vedere anche il link appena sotto.

Riferimenti

Vedi i link sopra. Inoltre:

  1. La mia sperimentazione del codice online:https://onlinegdb.com/B1RsrBDFD

Se vuoi passare un array a dimensione singola come argomento in una funzione , dovresti dichiarare un parametro formale in uno dei tre modi seguenti e tutti e tre i metodi di dichiarazione producono risultati simili perché ciascuno dice al compilatore che verrà ricevuto un puntatore intero .

int func(int arr[], ...){
    .
    .
    .
}

int func(int arr[SIZE], ...){
    .
    .
    .
}

int func(int* arr, ...){
    .
    .
    .
}

Quindi stai modificando i valori originali.

Grazie!!!