Passare un array come argomento a una funzione in C


89

Ho scritto una funzione contenente array come argomento e la chiamo passando il valore di array come segue.

Quello che ho trovato è che anche se sto chiamando la arraytest()funzione passando i valori, la copia originale di int arr[]è cambiata.

Puoi spiegare perché?


1
Stai passando l'array per riferimento ma ne stai modificando il contenuto - ecco perché stai vedendo un cambiamento nei dati
Shaun Wilde

main()deve tornare int.
underscore_d

Risposte:


141

Quando si passa un array come parametro, this

significa esattamente lo stesso di

così si sta modificando i valori nella principale.

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


3
Quale notazione è migliore in quali circostanze?
Ramon Martinez

22
@ Ramon - Vorrei utilizzare la seconda opzione, poiché sembra meno confusa e indica meglio che non si ottiene una copia dell'array.
Bo Persson

2
Puoi spiegare le "ragioni storiche"? Suppongo che il passaggio di valori richiederebbe una copia e quindi uno spreco di memoria .. grazie
Jacquelyn.Marquardt

5
@lucapozzobon - Originariamente C non aveva alcun valore di passaggio, ad eccezione dei valori singoli. È stato solo quando è structstato aggiunto alla lingua che questo è stato cambiato. E poi è stato considerato troppo tardi per cambiare le regole per gli array. C'erano già 10 utenti. :-)
Bo Persson

1
... significa esattamente lo stesso di void arraytest(int a[1000])ecc. ecc. Risposta estesa qui: stackoverflow.com/a/51527502/4561887 .
Gabriel Staples

9

Se vuoi passare un array monodimensionale 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é ognuno dice al compilatore che sta andando un puntatore intero essere ricevuto .

Quindi, stai modificando i valori originali.

Grazie !!!


Stavo cercando il tuo secondo esempio, puoi elaborare quali sono i vantaggi di ciascun metodo?
Puck

8

Non stai passando l'array come copia. È solo un puntatore che punta all'indirizzo in cui è in memoria il primo elemento dell'array.


7

Stai passando l'indirizzo del primo elemento dell'array


7

Gli array in C vengono convertiti, nella maggior parte dei casi, in un puntatore al primo elemento dell'array stesso. E più in dettaglio, gli array passati alle funzioni vengono sempre convertiti in puntatori.

Ecco una citazione di K & R2nd :

Quando un nome di array viene passato a una funzione, ciò che viene passato è la posizione dell'elemento iniziale. All'interno della funzione chiamata, questo argomento è una variabile locale, quindi un parametro del nome di un array è un puntatore, cioè una variabile contenente un indirizzo.

Scrittura:

ha lo stesso significato della scrittura:

Quindi, nonostante tu non lo stia scrivendo esplicitamente, è come se stai passando un puntatore e quindi stai modificando i valori nel main.

Per di più suggerisco davvero di leggere questo .

Inoltre, puoi trovare altre risposte su SO qui


6

Stai passando il valore della posizione di memoria del primo membro della matrice.

Pertanto, quando inizi a modificare l'array all'interno della funzione, stai modificando l'array originale.

Ricorda che lo a[1]è *(a+1).


1
Suppongo che manchino () per * a + 1 dovrebbe essere * (a + 1)
ShinTakezou

@Shin Grazie, è passato un po 'di tempo da quando ho giocato con C.
alex

6

In C, ad eccezione di alcuni casi speciali, un riferimento a un array "decade" sempre a un puntatore al primo elemento dell'array. Pertanto, non è possibile passare un array "per valore". Un array in una chiamata di funzione verrà passato alla funzione come puntatore, il che è analogo al passaggio dell'array per riferimento.

EDIT: ci sono tre casi speciali in cui un array non decade in un puntatore al suo primo elemento:

  1. sizeof anon è lo stesso di sizeof (&a[0]).
  2. &anon è lo stesso di &(&a[0])(e non proprio lo stesso di &a[0]).
  3. char b[] = "foo"non è lo stesso di char b[] = &("foo").

Se passo un array a una funzione. Supponiamo, ad esempio, che abbia creato un array int a[10]e assegnato un valore casuale a ciascun elemento. Ora se passo questo array a una funzione usando int y[]o int y[10]o int *y. E poi in quella funzione uso sizeof(y)Risposta sarà il puntatore di byte è stato allocato. Quindi in questo caso, decadrà come un puntatore, sarebbe utile se includessi anche questo. Vedi questo postimg.org/image/prhleuezd
Suraj Jain

Se utilizzo sizeofla funzione nell'array originariamente definito, decadrà come array, ma se passo in un'altra funzione, l' sizeofoperatore usa decadrà come puntatore.
Suraj Jain,


So che questo è vecchio. Due domande se capita a qualcuno di vedere questo :) 1. @ThomSmith ha scritto che &anon è esattamente la stessa cosa di &a[0]quando aè un array. Come mai? Nel mio programma di test, entrambi mostrano di essere uguali, sia nella funzione in cui viene dichiarato l'array, sia quando vengono passati a una funzione diversa. 2. Lo scrittore scrive che " char b[] = "foo"non è lo stesso di char b[] = &("foo")". Per me, quest'ultimo non si compila nemmeno. Sono solo io?
Aviv Cohn

6

Passaggio di un array multidimensionale come argomento a una funzione. Passare un array uno dim come argomento è più o meno banale. Diamo uno sguardo al caso più interessante di passaggio di un array 2 dim. In C non puoi usare un puntatore al costrutto puntatore ( int **) invece di 2 array dim. Facciamo un esempio:

Qui ho specificato una funzione che prende come primo argomento un puntatore a un array di 5 numeri interi. Posso passare come argomento qualsiasi 2 array dim che ha 5 colonne:

Potresti avere l'idea di definire una funzione più generale che possa accettare qualsiasi 2 array dim e modificare la firma della funzione come segue:

Questo codice verrà compilato ma si riceverà un errore di runtime quando si tenta di assegnare i valori allo stesso modo della prima funzione. Quindi in C un array multidimensionale non è la stessa cosa dei puntatori a puntatori ... a puntatori. An int(*arr)[5]è un puntatore a un array di 5 elementi, an int(*arr)[6] è un puntatore a un array di 6 elementi e sono puntatori a diversi tipi!

Bene, come definire gli argomenti delle funzioni per dimensioni superiori? Semplice, seguiamo semplicemente lo schema! Ecco la stessa funzione regolata per prendere una matrice di 3 dimensioni:

Come ti aspetteresti, può prendere come argomento qualsiasi 3 array dim che hanno nella seconda dimensione 4 elementi e nella terza dimensione 5 elementi. Qualunque cosa del genere andrebbe bene:

Ma dobbiamo specificare tutte le dimensioni fino alla prima.


6

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

significa esattamente lo stesso di

Tuttavia, lasciatemi aggiungere anche che le due forme precedenti anche:

  1. significa esattamente lo stesso di

  2. che significa esattamente lo stesso di

  3. che significa esattamente lo stesso di

  4. che significa esattamente lo stesso di

  5. 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 -Werrorattivate (vedi il mio repository qui per i dettagli su queste 3 opzioni di compilazione), come Questo:

È 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_ttipo 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 array1decadere 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:

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 ints). 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 !:

Questo, tuttavia, produrrà un avviso:

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 -Werroralle 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:

si trasformerà in questo errore di build:


Nota che puoi anche creare puntatori "type safe" ad array di una data dimensione, in questo modo:

... 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:

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

2
void arraytest(int (*a)[1000])è meglio perché poi il compilatore sbaglierà se la dimensione è sbagliata.
wingerse

@WingerSendon, sapevo che c'erano alcune sottigliezze che dovevo verificare qui e che la sintassi è confusa (come una sintassi della funzione ptr è confusa), quindi mi sono preso il mio tempo e ho finalmente aggiornato la mia risposta con una nuova grande sezione intitolata Forcing type safety on arrays in C, che copre il tuo punto.
Gabriel Staples

@ GabrielStaples, grazie. La tua risposta è molto utile. Puoi indicarmi un riferimento per imparare il c avanzato in questo modo?
daryooosh

@daryooosh, sfortunatamente non posso. Non ho grandi referenze. L'ho raccolto un po 'qui, un po' là, scavando in profondità per molti anni. Il meglio che posso fare è dirti che di tanto in tanto metto parte di ciò che imparo in questo modo nel mio repository eRCaGuy_hello_world qui. Tieni presente però che il materiale di sicurezza di tipo C che ho usato sopra dovrebbe essere usato MOLTO con parsimonia. Complicherà il tuo codice e ridurrà notevolmente la leggibilità, e non ne vale la pena. Concentrati sulla sintassi semplice, ove possibile, e rendi le cose leggibili.
Gabriel Staples

Nota anche che il classico libro di testo in C canonico è questo libro K&R The C Programming Language : en.wikipedia.org/wiki/The_C_Programming_Language .
Gabriel Staples

0

Gli array vengono sempre passati per riferimento se si utilizza a[]o *a:


Sto upvoting questo. Non sono sicuro del motivo per cui è downvoted.
Gabriel Staples
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.