Pourquoi argc n'est-il pas une constante?

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

Comme l' article n ° 3 du C ++ efficace déclare "Utiliser const chaque fois que possible", je commence à penser "pourquoi ne pas rendre ces paramètres const" constants "?.

Existe-t-il un scénario dans lequel la valeur de argcest modifiée dans un programme?

Dinushan
la source
38
Vous êtes libre de déclarer argccomme const.
Oliver Charlesworth
14
J'ai vu de nombreux codes de qualité de production qui font cela:--argc
Aniket Inge
2
Je pense que cela a à voir avec l'héritage C, mais je ne sais pas comment.
Luis Machuca
13
Je soupçonne que "Utiliser const autant que possible" se réfère aux choses passées par référence, où toute modification dans la fonction persiste une fois que la fonction est sortie. Ce n'est pas vrai avec les choses passées par valeur, comme un entier, donc il n'y a rien à gagner à le faire const; en effet, passer argccomme un const intmoyen que vous ne pouvez pas utiliser ensuite argccomme un compteur à l'intérieur de la fonction.
Steve Melnikoff
4
@SteveMelnikoff: Je ne suis pas d'accord pour dire qu'il n'y a rien à gagner à faire constun paramètre passe-par-valeur. Voir par exemple stackoverflow.com/a/8714278/277304 et stackoverflow.com/a/117557/277304
leonbloy

Réponses:

114

Dans ce cas, l'histoire est un facteur. C a défini ces entrées comme "non constantes", et la compatibilité avec (une bonne partie du) code C existant était l'un des premiers objectifs de C ++.

Certaines API UNIX, telles que getopt, manipulent réellement argv[], donc cela ne peut pas être fait constpour cette raison également.

(A part: Fait intéressant, bien que getoptle prototype suggère qu'il ne modifiera pas argv[]mais peut modifier les chaînes pointées, la page de manuel Linux indique que getoptpermute ses arguments, et il semble qu'ils savent qu'ils sont vilains . La page de manuel à l'Open Group ne mentionne pas cette permutation.)

La mise constsur argcet argvne serait pas acheter beaucoup, et il invaliderait certaines pratiques de programmation de la vieille école, tels que:

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

J'ai écrit de tels programmes en C et je sais que je ne suis pas seul. J'ai copié l'exemple de quelque part .

Joe Z
la source
1
-1 Re "Certaines API UNIX, telles que getopt, manipulent en fait argv [], donc il ne peut pas être rendu const pour cette raison également.". On peut librement ajouter un niveau supérieur constà argv, sans affecter ce qu'une fonction C peut faire. En outre, getoptest déclaré comme int getopt(int argc, char * const argv[], const char *optstring);. Et ici, ce constn'est pas le niveau le plus élevé, mais déclare que les pointeurs sont const, une promesse de ne pas les modifier (bien que les chaînes sur lesquelles ils pointent pourraient éventuellement être modifiées).
Acclamations et hth. - Alf
2
Je suis désolé, les documents que j'ai consultés sont incohérents: la signature ne permet pas une telle permutation. Voir exemple . Cependant, le texte suivant dit que cela permute. Donc, je supprime le vote défavorable.
Acclamations et hth. - Alf
1
Autrement dit, vous devrez faire quelques modifications pour «déverrouiller» afin que je puisse supprimer le vote défavorable. Et non, vous ne comprenez pas le prototype. Regardez l'exemple que j'ai fourni et l'erreur de compilation, "affectation d'un emplacement en lecture seule".
Acclamations et hth. - Alf
1
@ Cheersandhth.-Alf: C'est intéressant ... J'ai recherché le prototype dans l'en-tête, et il est char* const* ___argvlà, mais l'interface adhère en fait const char **. Une telle confusion. J'avais confondu la préséance du []et du *par rapport au const. Mes excuses.
Joe Z
1
FYI: Le GNU getopt()sait qu'il ne suit pas la norme car il fait attention à la POSIXLY_CORRECTvariable d'environnement pour qu'il se comporte correctement. En particulier, POSIX ne permute pas les arguments et ne recherche pas les options après le premier argument sans option.
Jonathan Leffler
36

La norme C (ISO / CEI 9899: 2011) dit:

5.1.2.2.1 Démarrage du programme

¶1 La fonction appelée au démarrage du programme est nommée main. L'implémentation ne déclare aucun prototype pour cette fonction. Il doit être défini avec un type de retour int et sans paramètres:

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

ou avec deux paramètres (appelés ici argcet argv, bien que tous les noms puissent être utilisés, car ils sont locaux à la fonction dans laquelle ils sont déclarés):

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

ou équivalent; 10) ou d'une autre manière définie par l'implémentation.

¶2 S'ils sont déclarés, les paramètres de la mainfonction doivent obéir aux contraintes suivantes:

  • La valeur de argcest non négative.
  • argv[argc] doit être un pointeur nul.
  • Si la valeur de argcest supérieure à zéro, les membres du tableau argv[0]à argv[argc-1]inclusif doivent contenir des pointeurs vers des chaînes, qui reçoivent des valeurs définies par l'implémentation par l'environnement hôte avant le démarrage du programme. L'intention est de fournir au programme les informations déterminées avant le démarrage du programme à partir d'un autre endroit de l'environnement hébergé. Si l'environnement hôte n'est pas capable de fournir des chaînes avec des lettres à la fois en majuscules et en minuscules, l'implémentation doit garantir que les chaînes sont reçues en minuscules.
  • Si la valeur de argcest supérieure à zéro, la chaîne pointée par argv[0] représente le nom du programme; argv[0][0]doit être le caractère nul si le nom du programme n'est pas disponible à partir de l'environnement hôte. Si la valeur de argcest supérieure à un, les chaînes pointées par argv[1]through argv[argc-1] représentent les paramètres du programme.
  • Les paramètres argcet argvet les chaînes pointés par le argvtableau doivent être modifiables par le programme et conserver leurs dernières valeurs stockées entre le démarrage du programme et la fin du programme.

10) Ainsi, intpeut être remplacé par un nom typedef défini comme int, ou le type de argvpeut être écrit comme char **argv, et ainsi de suite.

Notez le dernier point. Il dit que les deux argcet argvdevraient être modifiables. Ils ne doivent pas être modifiés, mais ils peuvent être modifiés.

Jonathan Leffler
la source
Intéressant. Sur une note semi-liée, j'ai rencontré un bogue provoquant un crash qui s'est avéré être dû au fait que le QApplication(argc,argv)constructeur de Qt était défini avec argcpar référence . Cela m'a surpris.
HostileFork dit de ne pas faire confiance au SE
23

argcn'est normalement pas une constante car la signature de fonction pour main()les dates antérieures const.

Puisque argc est une variable de pile, sa modification n'affectera rien d'autre que votre propre traitement en ligne de commande.

Vous êtes bien entendu libre de le déclarer constsi vous le souhaitez.

raser
la source
8

Un niveau supérieur constsur un argument formel ne fait pas partie du type de fonction. Vous pouvez l'ajouter ou le supprimer à votre guise: cela n'affecte que ce que vous pouvez faire avec l'argument dans l'implémentation de la fonction.

Donc, pour argcvous pouvez ajouter librement ajouter un fichier const.

Mais pour argvvous ne pouvez pas faire les données de caractères constsans pour autant changer la signature de la fonction. Ce qui signifie qu'il ne s'agit alors pas de l'une maindes signatures de fonction standard et qu'il n'aura pas à être reconnu comme une mainfonction. Donc, ce n'est pas une bonne idée.


Une bonne raison pour ne pas utiliser les mainarguments standard dans les programmes non-jouets est que dans Windows, ils ne sont pas capables de représenter les arguments de programme réels tels que les noms de fichiers avec des caractères internationaux. C'est parce que dans Windows, ils sont codés par une convention très forte comme Windows ANSI. Dans Windows, vous pouvez implémenter une fonction d'accès aux arguments plus portable en termes de GetCommandLinefonction API.


En résumé, rien ne vous empêche d'ajouter constà argc, mais l'utilité la plus utile constsur argvvous donnerait une fonction non standard main, très probablement non reconnue comme telle. Heureusement (d'une manière ironique) il y a de bonnes raisons de ne pas utiliser les mainarguments standards pour le code sérieux portable. Tout simplement, pour la pratique, ils ne prennent en charge que l'ancien ASCII, avec uniquement des lettres de l'alphabet anglais.

Bravo et hth. - Alf
la source
1
Si vous développez pour Windows avec cygwin ou une autre libc qui prend correctement en charge UTF-8 (pour autant que je sache, cygwin est le seul à l'heure actuelle mais j'ai quelqu'un qui travaille sur une autre option), la mainsignature standard fonctionne bien et vous pouvez recevoir des arguments Unicode arbitraires.
R .. GitHub STOP AIDER ICE
@R ils fonctionnent également très bien sur la plupart des plates-formes * nix. le problème n'est pas de savoir s'il existe des plates-formes sur lesquelles ils fonctionnent. mais, très belle initiative , et en l'
occurrence
4

La signature de mainest en quelque sorte un artefact historique de C. Historiquement Cn'a pas eu const.

Cependant, vous pouvez déclarer votre paramètre constcar les effets de const sont uniquement à la compilation.

George
la source
Quand vous dites "C historique", à quel point parlons-nous ici?
Joe Z
8
consta été ajouté à C89 / C90; avant cela, il ne faisait pas partie de C.
Jonathan Leffler
@JonathanLeffler: Vous savez, j'avais oublié ça. J'ai appris le C en 1992. Avant cela, j'étais un Pascal, BASIC et un assembleur hacker.
Joe Z
2

Parce que argcc'est une variable locale (et, en C ++, pas une référence ou quelque chose), et parce que la place spéciale de mainsignifie que les manigances de compatibilité ascendante lui accordent une énorme marge de manœuvre sans raison impérieuse de forcer les applications à le rendre const.

main() {}

int main() {}

main() { return 0; }

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

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

ces variantes et bien d'autres seront compilées sur une large gamme de compilateurs C et C ++.

Donc en fin de compte, ce n'est pas que argc ne soit pas const, mais que ce n'est pas nécessaire, mais cela peut l'être si vous le souhaitez.

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

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

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

main(const int argc) {}
kfsone
la source
Notez que les variantes sans type de retour explicite ne sont plus valides en C99, encore moins en C11, bien que les compilateurs continuent de les autoriser pour des raisons de rétrocompatibilité. Les compilateurs C ++ ne doivent pas accepter les variantes sans type de retour.
Jonathan Leffler
@JonathanLeffler Oui, mais le dernier exemple d' idéone ( ideone.com/m1qc9c ) est G ++ 4.8.2 avec -std=c++11. Clang l'accepte également mais donne warning: only one parameter on 'main' declaration [-Wmain], et MSVC ne proteste que contre le spécificateur de type de retour manquant et l'absence de référence à argc. Encore une fois, 'shenanigans' et '
leeway
1

Mis à part les raisons historiques, une bonne raison de garder argc et argv non- constest que l'implémentation du compilateur ne sait pas ce que vous allez faire avec les arguments de main, elle sait juste qu'elle doit vous donner ces arguments.

Lorsque vous définissez vos propres fonctions et les prototypes associés, vous savez quels paramètres vous pouvez créer constet ceux que votre fonction va modifier.

Poussé à l'extrême, vous pouvez déclarer que tous les paramètres de toutes les fonctions doivent être déclarés const, puis si vous avez une raison de les modifier (par exemple, décrémentez un index pour rechercher dans un tableau), vous devrez créer des non- constvariables locales et copiez les constvaleurs des arguments dans ces variables. Cela permet un travail chargé et un LOC supplémentaire sans réel avantage. Un analyseur statique décent détectera si vous ne modifiez pas la valeur d'un argument et vous recommandera de créer le paramètre const.

Sam Skuce
la source