Dovrei usare char ** argv o char * argv []?


125

Sto solo imparando C e mi chiedevo quale di questi dovrei usare nel mio metodo principale. C'è qualche differenza? Quale è più comune?


2
Preferisco 'char ** argv', e quindi lo vedo più spesso, ma entrambi sono corretti e non cambierei una dichiarazione semplicemente perché era scritta 'char * argv []'.
Jonathan Leffler,

+1 perché i problemi più profondi di array vs. puntatore sono importanti per capire bene.
RBerteig,

2
Davvero un'ottima domanda. Grazie.
Frank V,

5
Leggi la sezione 6 delle FAQ di comp.lang.c ; è la migliore spiegazione della relazione tra matrici C e puntatori che ho visto. Due punti rilevanti: 1. char **argvequivale esattamente a char *argv[]come una dichiarazione di parametro (e solo come una dichiarazione di parametro). 2. le matrici non sono puntatori.
Keith Thompson,

Risposte:


160

Mentre stai solo imparando C, ti consiglio di provare davvero a capire prima le differenze tra array e puntatori anziché le cose comuni .

Nell'area dei parametri e delle matrici, ci sono alcune regole confuse che dovrebbero essere chiare prima di continuare. Innanzitutto, ciò che dichiari in un elenco di parametri viene considerato speciale. Ci sono situazioni in cui le cose non hanno senso come parametro di funzione in C. Queste sono

  • Funziona come parametri
  • Matrici come parametri

Matrici come parametri

Il secondo forse non è immediatamente chiaro. Ma diventa chiaro se si considera che la dimensione di una dimensione di matrice fa parte del tipo in C (e una matrice la cui dimensione di dimensione non viene fornita ha un tipo incompleto). Quindi, se si crea una funzione che accetta un array per valore (riceve una copia), potrebbe farlo solo per una dimensione! Inoltre, le matrici possono diventare grandi e C cerca di essere il più veloce possibile.

In C, per questi motivi, i valori di matrice non esistono. Se vuoi ottenere il valore di un array, quello che ottieni invece è un puntatore al primo elemento di quell'array. E qui in realtà giace la soluzione. Invece di disegnare un parametro di array non valido in anticipo, un compilatore C trasformerà il tipo del rispettivo parametro in un puntatore. Ricorda questo, è molto importante. Il parametro non sarà un array, ma sarà invece un puntatore al rispettivo tipo di elemento.

Ora, se si tenta di passare un array, ciò che viene passato è invece un puntatore al primo elemento degli array.

Escursione: funziona come parametro

Per il completamento, e poiché penso che questo ti aiuterà a capire meglio la questione, esaminiamo quale sia lo stato delle cose quando provi ad avere una funzione come parametro. Anzi, per prima cosa non avrà alcun senso. Come può un parametro essere una funzione? Eh, vogliamo una variabile in quel posto, ovviamente! Quindi, ciò che il compilatore fa quando ciò accade è, ancora una volta, trasformare la funzione in un puntatore a funzione . Tentare di passare una funzione passerà invece un puntatore a quella rispettiva funzione. Quindi, i seguenti sono gli stessi (analogo all'esempio dell'array):

void f(void g(void));
void f(void (*g)(void));

Si noti che *gsono necessarie le parentesi . Altrimenti, specifica una funzione di ritorno void*, anziché un puntatore a una funzione di ritorno void.

Torna agli array

Ora, all'inizio ho detto che le matrici possono avere un tipo incompleto, cosa che accade se non si specifica ancora una dimensione. Dato che abbiamo già capito che un parametro array non esiste ma invece qualsiasi parametro array è un puntatore, le dimensioni dell'array non contano. Ciò significa che il compilatore tradurrà tutto quanto segue e tutti sono la stessa cosa:

int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);

Certo, non ha molto senso essere in grado di mettere qualsiasi dimensione in esso, ed è appena buttato via. Per questo motivo, C99 ha trovato un nuovo significato per quei numeri e consente ad altre cose di apparire tra parentesi:

// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory. 
int main(int c, char *argv[static 5]);

// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);

// says the same as the previous one
int main(int c, char ** const argv);

Le ultime due righe dicono che non sarai in grado di cambiare "argv" all'interno della funzione - è diventato un puntatore const. Tuttavia, solo pochi compilatori C supportano quelle funzionalità C99. Ma queste caratteristiche chiariscono che la "matrice" non è in realtà una. È un puntatore.

Un avvertimento

Nota che tutto ciò che ho detto sopra è vero solo quando hai un array come parametro di una funzione. Se si lavora con array locali, un array non sarà un puntatore. Si comporterà come un puntatore, perché come spiegato in precedenza un array verrà convertito in un puntatore quando viene letto il suo valore. Ma non deve essere confuso con i puntatori.

Un esempio classico è il seguente:

char c[10]; 
char **c = &c; // does not work.

typedef char array[10];
array *pc = &c; // *does* work.

// same without typedef. Parens needed, because [...] has 
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;

2
Non avevo idea che questo esistesse: * argv [statico 1]
superlukas

12

Puoi usare entrambi. Sono completamente equivalenti. Vedi i commenti di litb e la sua risposta .

Dipende davvero da come si desidera utilizzarlo (e si può usare in entrambi i casi):

// echo-with-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char **argv)
{
  while (--argc > 0)
  {
    printf("%s ", *++argv);
  }
  printf("\n");
  return 0;
}

// echo-without-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char *argv[])
{
  int i;
  for (i=1; i<argc; i++)
  {
    printf("%s ", argv[i]);
  }
  printf("\n");
  return 0;
}

Per quanto riguarda ciò che è più comune - non importa. Qualsiasi programmatore C esperto che legge il tuo codice vedrà entrambi come intercambiabili (nelle giuste condizioni). Proprio come un oratore inglese esperto legge "sono" e "sono" ugualmente facilmente.

Più importante è che impari a leggerli e riconoscere quanto sono simili. Leggerai più codice di quello che scrivi e dovrai essere altrettanto a tuo agio con entrambi.


7
char * argv [] è equivalente al 100% a char ** argv quando utilizzato come tipo di parametro di una funzione. nessuna "const" coinvolta, anche non implicitamente. Entrambi sono puntatori a puntatori a caratteri. È diverso rispetto a ciò che dichiari. Ma il compilatore regola il tipo di parametro per essere un puntatore a un puntatore, anche se hai detto che è un array. Pertanto, i seguenti sono tutti uguali: void f (char * p [100]); void f (char * p []); void f (char ** p);
Johannes Schaub - litb

4
In C89 (che la maggior parte delle persone usa) non c'è nemmeno modo di trarre vantaggio dal fatto che lo hai dichiarato come un array (quindi semanticamente non importa se hai dichiarato un puntatore o un array lì - entrambi saranno presi come un puntatore). A partire da C99, puoi trarre vantaggio dal dichiararlo come un array. Quanto segue dice: "p è sempre non nullo e punta a una regione con almeno 100 byte": void f (char p [statico 100]); Si noti che in termini di tipo, tuttavia, p è ancora un puntatore.
Johannes Schaub - litb

5
(in particolare, & p ti darà un carattere **, ma non un carattere ( ) [100] che sarebbe il caso se p * fosse un array). Sono sorpreso che nessuno abbia ancora menzionato una risposta. Lo considero molto importante da capire.
Johannes Schaub - litb

Personalmente preferisco char**perché mi ricorda che non dovrebbe essere trattato come un vero array, come fare sizeof[arr] / sizeof[*arr].
raymai97,

9

Non fa differenza, ma lo uso char *argv[]perché mostra che è un array di dimensioni fisse di stringhe di lunghezza variabile (che di solito sono char *).



4

Non fa davvero la differenza, ma quest'ultimo è più leggibile. Quello che ti viene dato è un array di puntatori a caratteri, come dice la seconda versione. Tuttavia, può essere implicitamente convertito in un puntatore a doppio carattere come nella prima versione.


2

dovresti dichiararlo come char *argv[], a causa di tutti i molti modi equivalenti di dichiararlo, che si avvicina al suo significato intuitivo: una matrice di stringhe.


1

char ** → puntatore al puntatore carattere e char * argv [] significa matrice di puntatori carattere. Dato che possiamo usare il puntatore anziché un array, entrambi possono essere usati.


0

Non vedo alcun merito speciale nell'usare uno dei due approcci anziché l'altro - usa la convenzione più in linea con il resto del tuo codice.


-2

Se hai bisogno di un numero variabile o dinamico di stringhe, char ** potrebbe essere più facile da lavorare. Se il numero di stringhe è fisso, è preferibile char * var [].


-2

So che questo è obsoleto, ma se stai solo imparando il linguaggio di programmazione C e non stai facendo nulla di grave con esso, non utilizzare le opzioni della riga di comando.

Se non si utilizzano argomenti della riga di comando, non utilizzare neanche. Dichiara semplicemente la funzione principale come int main() Se tu

  • Desideri che l'utente del tuo programma sia in grado di trascinare un file sul tuo programma in modo da poter cambiare il risultato del tuo programma con esso o
  • Vuoi gestire le opzioni della riga di comando ( -help, /?o qualsiasi altra cosa che segue program namenel terminale o nel prompt dei comandi)

usa quello che ha più senso per te. Altrimenti, basta usare int main() Dopotutto, se si desidera aggiungere opzioni da riga di comando, è possibile modificarle facilmente in un secondo momento.


Questo non risponde alla domanda.
Lundin,
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.