Warum ist argc keine Konstante?

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

Da in Effective C ++ Item # 3 "Const wann immer möglich verwenden" angegeben ist, denke ich: "Warum nicht diese 'konstanten' Parameter constfestlegen?".

Gibt es ein Szenario, in dem der Wert von argcin einem Programm geändert wird?

Dinushan
quelle
38
Sie können sich argcals deklarieren const.
Oliver Charlesworth
14
Ich habe viele Produktionsqualitätscodes gesehen, die dies tun:--argc
Aniket Inge
2
Ich denke, es hat mit C-Erbe zu tun, würde aber nicht wissen, wie.
Luis Machuca
13
Ich vermute, dass "Verwenden Sie const, wann immer möglich" sich auf Dinge bezieht, die als Referenz übergeben werden, wobei jede Änderung innerhalb der Funktion nach dem Beenden der Funktion bestehen bleibt. Dies gilt nicht für Dinge, die als Wert übergeben werden, wie z. B. eine Ganzzahl. Es gibt also nichts zu gewinnen, wenn Sie sie erstellen const. Übergeben argcals const intMittel, das Sie dann argcbeispielsweise nicht als Zähler innerhalb der Funktion verwenden können.
Steve Melnikoff
4
@SteveMelnikoff: Ich stimme nicht zu, dass es nichts zu gewinnen gibt, wenn constein Parameter zum Übergeben von Werten erstellt wird. Siehe z. B. stackoverflow.com/a/8714278/277304 und stackoverflow.com/a/117557/277304
leonbloy

Antworten:

114

In diesem Fall ist die Geschichte ein Faktor. C definierte diese Eingaben als "nicht konstant", und die Kompatibilität mit (einem guten Teil) des vorhandenen C-Codes war ein frühes Ziel von C ++.

Einige UNIX-APIs, wie z. B. getopt, manipulieren tatsächlich argv[], sodass sie auch aus constdiesem Grund nicht erstellt werden können .

(Nebenbei: Interessanterweise gibt die Linux-Manpage an, dass ihre Argumente durchlässig sind , obwohl getoptder Prototyp vorschlägt, dass sie nicht geändert werden, argv[]aber möglicherweise die Zeichenfolgen modifiziert, auf die verwiesen wird getopt, und es scheint, dass sie wissen, dass sie ungezogen sind . Die Manpage beim Öffnen Die Gruppe erwähnt diese Permutation nicht.)

Putting constauf argcund argvwürde sich nicht viel kaufen, und es würde einige der alten Schule Programmierpraktiken ungültig machen , wie zum Beispiel:

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

Ich habe solche Programme in C geschrieben und weiß, dass ich nicht allein bin. Ich habe das Beispiel von irgendwoher kopiert .

Joe Z.
quelle
1
-1 Re "Einige UNIX-APIs, wie z. B. getopt, manipulieren argv [] tatsächlich, sodass es auch aus diesem Grund nicht als const festgelegt werden kann." Man kann sich frei eine Top-Level hinzufügen constzu argv, ohne dass dies zu Auswirkungen , was jede C - Funktion tun. Auch getoptwird als deklariert int getopt(int argc, char * const argv[], const char *optstring);. Und hier constist das nicht die oberste Ebene, sondern deklariert die Zeiger als constVersprechen, sie nicht zu ändern (obwohl die Zeichenfolgen, auf die sie zeigen, möglicherweise geändert werden).
Prost und hth. - Alf
@ Cheersandhth.-Alf: getopt Permutiert das argv[]Array standardmäßig, ändert jedoch nicht die Zeichenfolgen. (Das Wort permute stammt direkt von der Manpage.) Das bedeutet, dass das argvArray selbst nicht consteinmal die Zeichenfolgen enthält, auf die es zeigt. Wie manipuliert das Permutieren des argv[]Arrays es nicht ?
Joe Z
1
Es tut mir leid, die Dokumente , die ich mir angesehen habe, sind inkonsistent: Die Signatur erlaubt keine solche Permutation. Siehe Beispiel . Der folgende Text sagt jedoch, dass es permutiert. Also entferne ich die Ablehnung.
Prost und hth. - Alf
1
Das heißt, Sie müssen einige Änderungen vornehmen, um "freizuschalten", damit ich die Downvote entfernen kann. Und nein, Sie verstehen den Prototyp falsch. Schauen Sie sich das von mir bereitgestellte Beispiel und den Kompilierungsfehler "Zuweisung des schreibgeschützten Speicherorts" an.
Prost und hth. - Alf
1
@ Cheersandhth.-Alf: Es ist interessant ... Ich habe den Prototyp in der Kopfzeile nachgeschlagen, und er ist char* const* ___argvdort, aber die Schnittstelle haftet tatsächlich const char **. Solche Verwirrung. Ich hatte den Vorrang des []und des *in Bezug auf das verwechselt const. Entschuldigen Sie.
Joe Z
36

Die C-Norm (ISO / IEC 9899: 2011) lautet:

5.1.2.2.1 Programmstart

¶1 Die beim Programmstart aufgerufene Funktion heißt main. Die Implementierung deklariert keinen Prototyp für diese Funktion. Es muss mit einem Rückgabetyp von int und ohne Parameter definiert werden:

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

oder mit zwei Parametern (hier als argcund bezeichnet argv, obwohl beliebige Namen verwendet werden können, da sie lokal für die Funktion sind, in der sie deklariert sind):

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

oder gleichwertig; 10) oder auf eine andere implementierungsdefinierte Weise.

¶2 Wenn sie deklariert sind, müssen die Parameter der mainFunktion die folgenden Einschränkungen erfüllen:

  • Der Wert von argcist nicht negativ.
  • argv[argc] soll ein Nullzeiger sein.
  • Wenn der Wert von argcgrößer als Null ist, müssen die Array-Mitglieder argv[0]bis argv[argc-1]einschließlich Zeiger auf Zeichenfolgen enthalten, die vor dem Programmstart von der Host-Umgebung implementierungsdefinierte Werte erhalten. Die Absicht besteht darin, dem Programm Informationen zu liefern, die vor dem Programmstart von einer anderen Stelle in der gehosteten Umgebung ermittelt wurden. Wenn die Host-Umgebung nicht in der Lage ist, Zeichenfolgen mit Buchstaben in Groß- und Kleinbuchstaben zu versehen, muss die Implementierung sicherstellen, dass die Zeichenfolgen in Kleinbuchstaben empfangen werden.
  • Wenn der Wert von argcgrößer als Null ist, argv[0] repräsentiert die Zeichenfolge, auf die durch zeigt , den Programmnamen. argv[0][0]muss das Nullzeichen sein, wenn der Programmname in der Hostumgebung nicht verfügbar ist. Wenn der Wert von argcgrößer als eins ist, werden die Zeichenfolgen durch argv[1]angezeigtargv[argc-1] repräsentieren die , die Programmparameter.
  • Die Parameter argcund argvund die Zeichenfolgen, auf die das argvArray zeigt, müssen vom Programm geändert werden können und ihre zuletzt gespeicherten Werte zwischen Programmstart und Programmbeendigung beibehalten.

10) Somit intkann durch einen Typedef-Namen ersetzt werden, der als definiert ist int, oder der Typ von argvkann geschrieben werden als char **argvund so weiter.

Beachten Sie den letzten Aufzählungspunkt. Es heißt, dass beide argcund argvmodifizierbar sein sollten. Sie müssen nicht geändert werden, können aber geändert werden.

Jonathan Leffler
quelle
Interessant. In einem halb verwandten Zusammenhang stieß ich auf einen Absturz verursachenden Fehler, der sich herausstellte, weil der QApplication(argc,argv)Konstruktor von Qt als Referenz definiert argcwurde . Das hat mich überrascht.
HostileFork sagt, vertraue SE
23

argcist normalerweise keine Konstante, da die Funktionssignatur für main()Pre-Dates const.

Da argc eine Stapelvariable ist, wirkt sich das Ändern nur auf Ihre eigene Befehlszeilenverarbeitung aus.

Es steht Ihnen natürlich frei, dies zu deklarieren, constwenn Sie möchten.

razeh
quelle
8

Eine oberste Ebene consteines formalen Arguments ist nicht Teil des Funktionstyps. Sie können es nach Belieben hinzufügen oder entfernen: Es wirkt sich nur darauf aus, was Sie mit dem Argument in der Funktionsimplementierung tun können.

So argckönnen Sie frei hinzufügen, fügen Sie ein const.

Aber für argvSie können Sie die Zeichendaten nicht erstellen, constohne dabei die Funktionssignatur zu ändern. Dies bedeutet, dass es sich dann nicht um eine der Standardfunktionssignaturen mainhandelt und nicht als mainFunktion erkannt werden muss. Also keine gute Idee.


Ein guter Grund dafür, die Standardargumente mainin Nicht-Spielzeug-Programmen nicht zu verwenden, ist, dass sie in Windows keine tatsächlichen Programmargumente wie Dateinamen mit internationalen Zeichen darstellen können. Das liegt daran, dass sie in Windows nach sehr starker Konvention als Windows ANSI codiert sind. In Windows können Sie in Bezug auf die GetCommandLineAPI-Funktion eine portablere Argumentzugriffsfunktion implementieren .


Zusammengefasst, nichts hindert Sie daran , das Hinzufügen constzu argc, aber die nützlichste const-ness auf argvwürde Ihnen eine Nicht-Standard - mainFunktion, höchstwahrscheinlich nicht als solche erkannt. Glücklicherweise (auf ironische Weise) gibt es gute Gründe, den Standard nicht zu verwendenmainStandardargumente für tragbaren ernsthaften Code. Ganz einfach, für die Praxis unterstützen sie nur alte ASCII mit nur englischen Buchstaben.

Prost und hth. - Alf
quelle
1
Wenn Sie für Windows mit Cygwin oder einer anderen Bibliothek entwickeln, die UTF-8 ordnungsgemäß unterstützt (soweit ich weiß, ist Cygwin derzeit die einzige, aber ich habe jemanden, der an einer anderen Option arbeitet), mainfunktioniert die Standardsignatur einwandfrei und Sie können beliebige Unicode-Argumente empfangen.
R .. GitHub STOP HELPING ICE
@R Sie funktionieren auch gut auf den meisten * nix-Plattformen. Die Frage ist nicht, ob es Plattformen gibt, auf denen sie funktionieren. aber sehr nette Initiative , und wie es passiert, mache ich das gleiche, mehr oder weniger ernst
Prost und hth. - Alf
4

Die Signatur von mainist so etwas wie ein historisches Artefakt aus C. Historisch Cnicht hatte const.

Sie können Ihren Parameter jedoch deklarieren, constda die Auswirkungen von const nur die Kompilierungszeit sind.

George
quelle
Wenn Sie "historisches C" sagen, wie historisch sprechen wir hier?
Joe Z
8
constwurde zu C89 / C90 hinzugefügt; Zuvor war es nicht Teil von C.
Jonathan Leffler
@ JonathanLeffler: Weißt du, das hatte ich vergessen. Ich habe 1992 C gelernt. Davor war ich Pascal, BASIC und Assembly Hacker.
Joe Z
2

Weil argces sich um eine lokale Variable handelt (und in C ++ nicht um eine Referenz oder etwas mainanderes ) und weil der besondere Ort bedeutet, dass Abwärtskompatibilitäts-Spielereien ihr einen enormen Spielraum gewähren, ohne zwingenden Grund, Anwendungen zu zwingen , sie konstant zu machen.

main() {}

int main() {}

main() { return 0; }

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

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

Diese und viele andere Variationen werden auf einer Vielzahl von C- und C ++ - Compilern kompiliert.

Letztendlich ist es also nicht so, dass argc nicht const ist, nur dass es nicht sein muss, aber es kann sein, wenn Sie es wollen.

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

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

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

main(const int argc) {}
kfsone
quelle
Beachten Sie, dass die Varianten ohne expliziten Rückgabetyp in C99 nicht mehr gültig sind, geschweige denn in C11, obwohl Compiler sie aus Gründen der Abwärtskompatibilität weiterhin zulassen. C ++ - Compiler sollten die Varianten ohne Rückgabetyp nicht akzeptieren.
Jonathan Leffler
@ JonathanLeffler Ja, aber das letzte Ideone-Beispiel dort ( ideone.com/m1qc9c ) ist G ++ 4.8.2 mit -std=c++11. Clang akzeptiert es ebenfalls, gibt es aber warning: only one parameter on 'main' declaration [-Wmain]und MSVC protestiert nur gegen den fehlenden Rückgabetyp-Spezifizierer und das Fehlen eines Verweises auf argc. Wieder 'Shenanigans' und 'Spielraum' :)
kfsone
1

Abgesehen von den historischen Gründen ist ein guter Grund, argc und argv nicht beizubehalten, constdass die Compiler-Implementierung nicht weiß, was Sie mit den Argumenten für main tun werden. Sie weiß nur, dass sie Ihnen diese Argumente geben muss.

Wenn Sie Ihre eigenen Funktionen und zugehörigen Prototypen definieren, wissen Sie, welche Parameter Sie vornehmen können const und welche Ihre Funktion ändern wird.

Im Extremfall könnten Sie deklarieren, dass alle Parameter für alle Funktionen deklariert werden constsollen. Wenn Sie dann einen Grund hätten, sie zu ändern (z. B. einen Index dekrementieren, um ein Array zu durchsuchen), müssten Sie lokale Nichtvariablen consterstellen und kopieren Sie die constWerte der Argumente in diese Variablen. Das sorgt für viel Arbeit und zusätzliches LOC ohne wirklichen Nutzen. Ein anständiger statischer Analysator erkennt, wenn Sie den Wert eines Arguments nicht ändern, und empfiehlt, den Parameter festzulegen const.

Sam Skuce
quelle