Est-il possible de changer d’argv ou dois-je en créer une copie ajustée?

Mon application a potentiellement un très grand nombre d’arguments passés et je veux éviter que la mémoire de frappe duplique les arguments dans une liste filtrée. Je voudrais les filtrer sur place, mais je suis à peu près sûr que s’en prendre au tableau argv lui-même, ou aux données qu’il pointe, n’est probablement pas conseillé. Aucune suggestion?

Une fois que argv a été passé dans la méthode principale, vous pouvez le traiter comme n’importe quel autre tableau C – changez-le à votre guise, soyez simplement conscient de ce que vous en faites. Le contenu du tableau n’a pas d’incidence sur le code de retour ni sur l’exécution du programme autre que ce que vous en faites explicitement dans le code. Je ne vois aucune raison pour laquelle il ne serait “pas conseillé” de le traiter spécialement.

Bien sûr, vous devez toujours faire attention à accéder accidentellement à la mémoire au-delà des limites de argv. Le revers de la médaille étant accessible comme un tableau C normal, c’est qu’il est également sujet aux erreurs d’access, comme tout autre tableau C normal. (Merci à tous ceux qui l’ont signalé dans leurs commentaires et autres réponses!)

La norme C99 dit ceci à propos de la modification de argv (et argc ):

Les parameters argc et argv et les chaînes pointées par le tableau argv doivent être modifiables par le programme et conserver leurs dernières valeurs stockées entre le démarrage et la fin du programme.

La dernière version de la norme C (N1256) indique qu’il existe deux formes autorisées pour la fonction main :

 int main (void); int main (int argc, char* argv[]); 

mais l’essentiel est la clause “ou d’une autre manière définie par l’implémentation”. Cela me semble être une faille dans la norme suffisamment grande pour faire passer une semi-remorque.

Certaines personnes utilisent spécifiquement "const char *" pour que l’ argv interdise les modifications apscopes aux arguments. Si votre fonction principale est définie de cette façon, vous n’êtes pas autorisé à modifier les caractères sur lesquels pointe argv[] , comme en témoigne le programme suivant:

 pax> cat qq.c #include  int main (int c, const char *v[]) { *v[1] = 'X'; printf ("[%s]\n", v[1]); return 0; } pax> gcc -o qq qq.c qq.c: In function `main': qq.c:3: error: assignment of read-only location 

Cependant, si vous supprimez le "const" , cela fonctionne bien:

 pax> cat qq2.c #include  int main (int c, char *v[]) { *v[1] = 'X'; printf ("[%s]\n", v[1]); return 0; } pax> gcc -o qq2 qq2.c ; ./qq2 [Xello] 

Je pense que c’est également le cas pour C ++. Le projet actuel stipule:

 All implementations shall allow both of the following definitions of main: int main(); int main(int argc, char* argv[]); 

mais cela n’interdit pas spécifiquement les autres variantes, vous pouvez donc probablement accepter une version "const" en C ++ (et, en fait, g ++ le fait).

La seule chose à laquelle vous devez faire attention est d’essayer d’augmenter la taille de l’un des éléments. Les normes n’exigent pas la façon dont elles sont stockées, donc étendre un argument peut (probablement) affecter d’autres ou d’autres données non liées.

De manière empirique, des fonctions telles que GNU getopt () permutent la liste des arguments sans causer de problèmes. Comme @Tim le dit, tant que vous jouez avec discernement, vous pouvez manipuler le tableau de pointeurs et même des chaînes individuelles. Il suffit de ne pas dépasser les limites du tableau implicite.

Le système d’exploitation place les arguments argv et argc dans la stack d’applications avant de l’exécuter, et vous pouvez les traiter comme toutes les autres variables de la stack.

La seule fois où je dirais que manipuler directement argv est une mauvaise idée, ce serait lorsqu’une application change de comportement en fonction du contenu de argv [0].

Cependant, changer le comportement d’un programme en fonction de argv [0] est en soi une très mauvaise idée lorsque la portabilité est une préoccupation.

Sinon, vous pouvez le traiter comme n’importe quel autre tableau. Comme Jonathan l’a dit, getopt () GNU permute la liste des arguments de manière non destructive, j’ai déjà vu d’autres implémentations de getopt () allant jusqu’à la sérialisation et même le hachage des arguments (utile lorsqu’un programme s’apparente à ARG_MAX).

Soyez juste prudent avec votre arithmétique de pointeur.

Certaines bibliothèques le font!

La méthode d’initialisation fournie par la bibliothèque glut opengl (GlutInit) recherche les arguments liés à la glut et les élimine en déplaçant les éléments suivants dans argv (déplacement des pointeurs et non des chaînes réelles) et décrémentation de l’argc

2.1

glutInit glutInit est utilisé pour initialiser la bibliothèque GLUT.

Usage

void glutInit (int * argcp, char ** argv);

argcp

Un pointeur sur la variable argc non modifiée du programme à partir de main. Au retour, la valeur indiquée par argcp sera mise à jour, car glutInit extrait toutes les options de ligne de commande destinées à la bibliothèque GLUT.

argv

La variable argv non modifiée du programme à partir de main. Comme argcp, les données pour argv seront mises à jour car glutInit extrait toutes les options de ligne de commande comsockets par la bibliothèque GLUT.

L’allocation d’origine de argv est laissée comme choix du compilateur / runtime. Donc, il peut ne pas être sûr de le modifier bon gré mal gré. De nombreux systèmes le construisent sur la stack, il est donc auto-désalloué lors du retour principal. D’autres le construisent sur le tas, et le libèrent (ou pas) lorsque main retourne.

Il est prudent de modifier la valeur d’un argument tant que vous n’essayez pas de le rendre plus long (erreur de dépassement de la mémoire tampon). Il est prudent de mélanger l’ordre des arguments.

Pour supprimer les arguments que vous avez prétraités , quelque chose comme ceci fonctionnera:

(beaucoup de conditions d’erreur non vérifiées, “–special” autres que le premier argument non vérifié, etc. Ceci est, après tout, juste une démonstration du concept.)

 int main(int argc, char** argv) { bool doSpecial = false; // an assumption if (0 == strcmp(argv[1], "--special")) { doSpecial = true; // no longer an assumption // remove the "--special" argument // but do copy the NULL at the end. for(int i=1; i 

Mais voyez ceci pour une manipulation complète: (la partie de la bibliothèque libiberty utilisée pour manipuler les vecteurs de style argv)

http://www.opensource.apple.com/source/gcc/gcc-5666.3/libiberty/argv.c

Il est sous licence GNU LGPL.