web-dev-qa-db-fra.com

Pourquoi l'argv principal C / C ++ est-il déclaré comme "char * argv []" plutôt que simplement "char * argv"?

Pourquoi argv est-il déclaré comme "un pointeur vers un pointeur vers le premier index du tableau", plutôt que d'être simplement "un pointeur vers le premier index du tableau" (char* argv)?

Pourquoi la notion de "pointeur à pointeur" est-elle requise ici?

21
a user

Argv est essentiellement comme ceci:

enter image description here

À gauche se trouve l'argument lui-même - ce qui est réellement passé comme argument à main. Cela contient l'adresse d'un tableau de pointeurs. Chacun de ces points pointe vers un endroit en mémoire contenant le texte de l'argument correspondant qui a été passé sur la ligne de commande. Ensuite, à la fin de ce tableau, il est garanti qu'il y a un pointeur nul.

Notez que le stockage réel pour les arguments individuels est au moins potentiellement alloué séparément les uns des autres, de sorte que leurs adresses en mémoire peuvent être organisées de manière assez aléatoire (mais selon la façon dont les choses se trouvent écrites, elles peuvent également être dans un seul bloc contigu de mémoire - vous ne savez tout simplement pas et ne devriez pas vous en soucier).

60
Jerry Coffin

Parce que c'est ce que fournit le système d'exploitation :-)

Votre question est un peu un problème d'inversion poulet/œuf. Le problème n'est pas de choisir ce que vous voulez en C++, le problème est de savoir comment vous dites en C++ ce que le système d'exploitation vous donne.

Unix transmet un tableau de "chaînes", chaque chaîne étant un argument de commande. En C/C++, une chaîne est un "char *", donc un tableau de chaînes est char * argv [], ou char ** argv, selon le goût.

23
passer-by

Tout d'abord, en tant que déclaration de paramètre, char **argv est le même que char *argv[]; ils impliquent tous les deux un pointeur vers (un tableau ou un ensemble d'un ou plusieurs possibles) pointeur (s) vers des chaînes.

Ensuite, si vous n'avez que "pointer to char" - par exemple juste char * - puis pour accéder au nième élément, vous devrez scanner les n-1 premiers éléments pour trouver le début du nième élément. (Et cela imposerait également l'exigence que chacune des chaînes soit stockée de manière contiguë.)

Avec le tableau de pointeurs, vous pouvez indexer directement le nième élément - donc (bien que pas strictement nécessaire - en supposant que les chaînes sont contiguës), il est généralement beaucoup plus pratique.

Pour illustrer:

./programme bonjour le monde

argc = 3
argv[0] --> "./program\0"
argv[1] --> "hello\0"
argv[2] --> "world\0"

Il est possible que, dans un tableau de caractères fourni par le système d'exploitation:

            "./program\0hello\0world\0"
argv[0]      ^
argv[1]                 ^
argv[2]                        ^

si argv n'était qu'un "pointeur vers char", vous pourriez voir

       "./program\0hello\0world\0"
argv    ^

Cependant (bien que probable par la conception de l'os), il n'y a aucune garantie réelle que les trois chaînes "./program", "hello" et "world" sont contiguës. En outre, ce type de "pointeur unique vers plusieurs chaînes contiguës" est une construction de type de données plus inhabituelle (pour C), en particulier par rapport au tableau de pointeurs vers la chaîne.

15
Erik Eidt

Pourquoi l'argv principal C/C++ est déclaré comme "char * argv []"

Une réponse possible est que la norme C11 n157 (in §5.1.2.2.1 Démarrage du programme =) et la norme C++ 11 n3337 (dans §3.6.1 fonction principale ) nécessite que pour les environnements hébergés (mais notez que la norme C mentionne également §5.1 .2.1 environnements autonomes ) Voir aussi this .

La question suivante est pourquoi les normes C et C++ ont-elles choisi main pour avoir une telle signature int main(int argc, char**argv)? L'explication est largement historique: C a été inventé avec nix , qui a un Shell qui fait - globbing avant de faire fork (qui est un appel système pour créer un processus) et execve (qui est l'appel système pour exécuter un programme), et que execve transmet un tableau d'arguments de programme de chaîne et est lié au main du programme exécuté. En savoir plus sur la philosophie Unix et sur ABI s.

Et C++ s'est efforcé de suivre les conventions de C et d'être compatible avec lui. Il ne pouvait pas définir main comme incompatible avec les traditions C.

Si vous avez conçu un système d'exploitation à partir de zéro (ayant toujours une interface de ligne de commande) et un langage de programmation pour celui-ci à partir de zéro, vous serez libre d'inventer différentes conventions de démarrage de programme. Et d'autres langages de programmation (par exemple LISP commun ou Ocaml ou Go) ont des conventions de démarrage de programme différentes.

En pratique, main est invoqué par du code crt . Notez que sous Windows, la globalisation peut être effectuée par chaque programme dans l'équivalent de crt0, et certains programmes Windows peuvent démarrer via le non standard point d'entrée WinMain . Sous Unix, la globalisation est effectuée par le shell (et crt0 adapte l'ABI, et la disposition initiale de la pile d'appels qu'il a spécifiée, aux conventions d'appel de votre implémentation C).

13

Plutôt que de le considérer comme un "pointeur à pointeur", il est utile de le considérer comme un "tableau de chaînes", avec [] désignant un tableau et char* désignant une chaîne. Lorsque vous exécutez un programme, vous pouvez lui passer un ou plusieurs arguments de ligne de commande et ceux-ci sont reflétés dans les arguments à main: argc est le nombre d'arguments et argv vous permet d'accéder à des arguments individuels.

12
casablanca

Dans de nombreux cas, la réponse est "parce que c'est une norme". Pour citer norme C99 :

- Si la valeur d'argc est supérieure à zéro, les membres du tableau argv [0] à argv [argc-1] inclus doivent contenir pointe vers des chaînes , auxquelles l'environnement hôte a donné des valeurs définies par l'implémentation avant le démarrage du programme.

Bien sûr, avant qu'il ne soit standardisé, il était déjà utilisé par K&R C dans les premières implémentations Unix, dans le but de stocker des paramètres de ligne de commande (quelque chose que vous devez faire attention dans Unix Shell comme /bin/bash ou /bin/sh mais pas dans les systèmes embarqués). Pour citer f première édition de K & R "The C Programming Language" (p. 110) :

Le premier (appelé conventionnellement argc ) est le nombre d'arguments de ligne de commande avec lesquels le programme a été appelé; le second ( argv ) est un pointeur vers un tableau de chaînes de caractères contenant les arguments, un par chaîne.

1
Sergiy Kolodyazhnyy