Perché argc non è una costante?


104
int main( const int argc , const char[] const argv)

Poiché l' Effective C ++ Item # 3 afferma "Usa const ogni volta che è possibile", inizio a pensare "perché non rendere questi parametri 'costanti' const"?

C'è qualche scenario in cui il valore di argcviene modificato in un programma?


38
Sei libero di dichiarare argccome const.
Oliver Charlesworth

14
Ho visto molti codici di qualità della produzione che fanno questo:--argc
Aniket Inge

2
Penso che abbia a che fare con l'eredità del C, ma non saprei come.
Luis Machuca

13
Sospetto che "Usa const ogni volta che è possibile" si riferisce a cose passate per riferimento, dove qualsiasi modifica all'interno della funzione persiste una volta che la funzione è uscita. Questo non è vero con le cose passate per valore, come un numero intero, quindi non c'è niente da guadagnare facendolo const; anzi, passando argccome un const intmezzo che non puoi quindi usare argccome, diciamo, un contatore all'interno della funzione.
Steve Melnikoff

4
@SteveMelnikoff: Non sono d'accordo sul fatto che non ci sia nulla da guadagnare creando constun parametro di passaggio. Si veda ad esempio stackoverflow.com/a/8714278/277304 e stackoverflow.com/a/117557/277304
leonbloy

Risposte:


114

In questo caso, la storia è un fattore. Il C ha definito questi input come "non costanti" e la compatibilità con (una buona parte del) codice C esistente era uno degli obiettivi iniziali di C ++.

Alcune API UNIX, come getopt, effettivamente manipolano argv[], quindi non possono essere create anche constper questo motivo.

(A parte: è interessante notare che, sebbene getoptil prototipo di s suggerisca che non modificherà argv[]ma potrebbe modificare le stringhe a cui punta, la pagina man di Linux indica che getoptpermuta i suoi argomenti e sembra che sappiano che sono cattivi . La pagina man su Open Il gruppo non menziona questa permutazione.)

Mettere constsu argce argvnon sarebbe comprare molto, e sarebbe invalidare qualche vecchia scuola pratiche, come ad esempio la programmazione:

// print out all the arguments:
while (--argc)
    std::cout << *++argv << std::endl;

Ho scritto programmi del genere in C e so di non essere solo. Ho copiato l'esempio da qualche parte .


1
-1 Re "Alcune API UNIX, come getopt, manipolano effettivamente argv [], quindi non può essere reso const anche per questo motivo.". È possibile aggiungere liberamente un livello superiore consta argv, senza influire su ciò che può fare qualsiasi funzione C. Inoltre, getoptè dichiarato come int getopt(int argc, char * const argv[], const char *optstring);. E qui il constnon è di livello superiore, ma dichiara che i puntatori lo sono const, una promessa di non modificarli (sebbene le stringhe a cui puntano potrebbero essere modificate).
Saluti e salute. - Alf

2
Mi dispiace, i documenti che ho guardato non sono coerenti: la firma non permette tale permutazione. Vedi esempio . Tuttavia, il testo seguente dice che permuta. Quindi, rimuovo il voto negativo.
Saluti e salute. - Alf

1
Cioè, dovrai fare qualche modifica per "sbloccare" così posso rimuovere il downvote. E no, stai fraintendendo il prototipo. Guarda l'esempio che ho fornito e l'errore di compilazione, "assegnazione di posizione di sola lettura".
Saluti e salute. - Alf

1
@ Cheersandhth.-Alf: È interessante ... Ho cercato il prototipo nell'intestazione, e char* const* ___argvc'è, ma l'interfaccia effettivamente aderisce const char **. Tale confusione. Avevo confuso la precedenza di []e *di rispetto a const. Mie scuse.
Joe Z

1
FYI: GNU getopt()sa di non seguire lo standard perché presta attenzione alla POSIXLY_CORRECTvariabile d'ambiente per farla funzionare correttamente. In particolare, POSIX non permuta gli argomenti e non cerca le opzioni dopo il primo argomento non-opzione.
Jonathan Leffler

36

Lo standard C (ISO / IEC 9899: 2011) dice:

5.1.2.2.1 Avvio del programma

¶1 Viene denominata la funzione chiamata all'avvio del programma main. L'implementazione non dichiara alcun prototipo per questa funzione. Deve essere definito con un tipo di ritorno int e senza parametri:

int main(void) { /* ... */ }

o con due parametri (qui indicati come argce argv, sebbene sia possibile utilizzare qualsiasi nome, poiché sono locali rispetto alla funzione in cui sono dichiarati):

int main(int argc, char *argv[]) { /* ... */ }

o equivalente; 10) o in un altro modo definito dall'implementazione.

¶ 2 Se vengono dichiarati, i parametri della mainfunzione devono obbedire ai seguenti vincoli:

  • Il valore di argcnon sarà negativo.
  • argv[argc] deve essere un puntatore nullo.
  • Se il valore di argcè maggiore di zero, i membri dell'array argv[0]tramite argv[argc-1]inclusivo conterranno puntatori a stringhe, a cui vengono forniti valori definiti dall'implementazione dall'ambiente host prima dell'avvio del programma. L'intento è fornire al programma le informazioni determinate prima dell'avvio del programma da altre parti dell'ambiente ospitato. Se l'ambiente host non è in grado di fornire stringhe con lettere sia in maiuscolo che in minuscolo, l'implementazione deve garantire che le stringhe vengano ricevute in minuscolo.
  • Se il valore di argcè maggiore di zero, la stringa puntata da argv[0] rappresenta il nome del programma; argv[0][0]deve essere il carattere null se il nome del programma non è disponibile dall'ambiente host. Se il valore di argcè maggiore di uno, le stringhe puntate da argv[1]tramite argv[argc-1] rappresentano i parametri del programma.
  • I parametri argce le argve le corde a cui punta la argvmatrice devono essere modificabili dal programma, e mantenere i loro valori dell'ultimo memorizzati tra all'avvio del programma e la terminazione del programma.

10) Quindi, intpuò essere sostituito da un nome typedef definito come int, oppure il tipo di argvpuò essere scritto come char **argv, e così via.

Nota l'ultimo punto elenco. Dice che entrambi argce argvdovrebbero essere modificabili. Non devono essere modificati, ma possono essere modificati.


Interessante. In una nota semi-correlata, mi sono imbattuto in un bug che causava un arresto anomalo che si è rivelato essere dovuto al fatto che il QApplication(argc,argv)costruttore di Qt è stato definito con argcper riferimento . Questo mi ha sorpreso.
HostileFork dice di non fidarsi di SE

23

argcnormalmente non è una costante perché la firma della funzione per main()le date precedenti const.

Poiché argc è una variabile di stack, modificarla non influenzerà nient'altro che l'elaborazione della riga di comando.

Naturalmente sei libero di dichiararlo constse lo desideri.


8

Un livello superiore constsu un argomento formale non fa parte del tipo di funzione. Puoi aggiungerlo o rimuoverlo come preferisci: influisce solo su ciò che puoi fare con l'argomento nell'implementazione della funzione.

Quindi per argcte puoi aggiungere liberamente un file const.

Ma argvnon è possibile creare i dati del carattere constsenza modificare la firma della funzione. Ciò significa che non è quindi una delle mainfirme delle funzioni standard e non dovrà essere riconosciuta come mainfunzione. Quindi, non è una buona idea.


Una buona ragione per non utilizzare gli mainargomenti standard in programmi non giocattolo è che in Windows non sono in grado di rappresentare argomenti di programma effettivi come nomi di file con caratteri internazionali. Questo perché in Windows sono codificati per convenzione molto forte come Windows ANSI. In Windows è possibile implementare una funzione di accesso agli argomenti più portabile in termini di GetCommandLinefunzione API.


Riassumendo, nulla ti impedisce di aggiungere consta argc, ma l'utilità più utile constti argvdarebbe una funzione non standard main, molto probabilmente non riconosciuta come tale. Fortunatamente (in modo ironico) ci sono buone ragioni per non usare gli mainargomenti standard per il codice serio portatile. Molto semplicemente, per la pratica supportano solo il vecchio ASCII, con solo lettere dell'alfabeto inglese.


1
Se stai sviluppando per Windows con cygwin o un'altra libc che supporta correttamente UTF-8 (per quanto ne so, cygwin è l'unico al momento ma ho qualcuno che lavora su un'altra opzione), la mainfirma standard funziona bene e puoi ricevere argomenti Unicode arbitrari.
R .. GitHub SMETTA DI AIUTARE IL GHIACCIO

@R funzionano bene anche sulla maggior parte delle piattaforme * nix. il problema non è se esistono piattaforme su cui funzionano. ma, iniziativa molto bella , e come succede sto facendo lo stesso, più o meno seriamente
Saluti e hth. - Alf

4

La firma di mainè un po 'un manufatto storico da C. Storicamente Cnon ha avuto const.

Tuttavia, puoi dichiarare il tuo parametro constpoiché gli effetti di const sono solo in fase di compilazione.


Quando dici "C storico", quanto stiamo parlando di storico qui?
Joe Z

8
constè stato aggiunto a C89 / C90; prima di allora, non faceva parte di C.
Jonathan Leffler

@JonathanLeffler: Sai, l'avevo dimenticato. Ho imparato il C nel 1992. Prima ero un Pascal, BASIC e un assembly hacker.
Joe Z

2

Perché argcè una variabile locale (e, in C ++, non un riferimento o qualcosa del genere), e poiché la posizione speciale di mainsignifica che gli shenanigans della compatibilità con le versioni precedenti le garantiscono un enorme margine di manovra senza motivo convincente per forzare le applicazioni a renderlo const.

main() {}

int main() {}

main() { return 0; }

main(int argc, char* argv[]) { return 0; }

int main(const int argc, const char** argv) { /* no return*/ }

queste e molte altre variazioni verranno compilate su un'ampia gamma di compilatori C e C ++.

Quindi alla fine non è che argc non sia const, solo che non deve esserlo, ma può essere se vuoi che lo sia.

http://ideone.com/FKldHF , esempio C:

main(const int argc, const char* argv[]) { return 0; }

http://ideone.com/m1qc9c , esempio C ++

main(const int argc) {}

Si noti che le varianti senza un tipo di ritorno esplicito non sono più valide in C99, per non parlare di C11, sebbene i compilatori continuino a consentirle per motivi di compatibilità all'indietro. I compilatori C ++ non dovrebbero accettare le varianti senza un tipo restituito.
Jonathan Leffler

@JonathanLeffler Sì, ma l'ultimo esempio di ideone lì ( ideone.com/m1qc9c ) è G ++ 4.8.2 con -std=c++11. Anche Clang lo accetta ma dà warning: only one parameter on 'main' declaration [-Wmain], e MSVC protesta solo per lo specificatore del tipo restituito mancante e per la mancanza di un riferimento a argc. Di nuovo, "shenanigans" e "
leeway

1

A parte le ragioni storiche, una buona ragione per mantenere argc e argv non constè che l'implementazione del compilatore non sa cosa farai con gli argomenti di main, sa solo che deve darti quegli argomenti.

Quando si definiscono le proprie funzioni e i prototipi associati, si sa quali parametri è possibile creare conste quali verranno modificati dalla funzione.

Portato all'estremo, potresti dichiarare che tutti i parametri di tutte le funzioni dovrebbero essere dichiarati const, e quindi se avessi un motivo per cambiarli (es. Decrementare un indice per cercare in un array), dovresti rendere non constvariabili locali e copia i constvalori degli argomenti in quelle variabili. Ciò rende il lavoro impegnato e LOC extra senza alcun reale vantaggio. Un analizzatore statico decente rileverà se non stai modificando il valore di un argomento e ti consiglierà di creare il parametro const.

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.