web-dev-qa-db-fra.com

Argc peut-il être zéro sur un système POSIX?

Compte tenu de la définition standard du programme principal:

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

Dans quelles circonstances argc peut-il être zéro sur un système POSIX?

72
Sylvain Leroux

Oui c'est possible. Si vous appelez votre programme comme suit:

execl("./myprog", NULL, (char *)NULL);

Ou alternativement:

char *args[] = { NULL };
execv("./myprog", args);

Ensuite, dans "myprog", argc sera 0.

La norme autorise également spécifiquement un 0 argc comme indiqué dans la section 5.1.2.2.1 en ce qui concerne le démarrage du programme dans un environnement hébergé:

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

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

ou avec deux paramètres (appelés ici argc et 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; ou d'une autre manière définie par la mise en œuvre.

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

  • La valeur de argc ne doit pas être négative.
  • argv[argc] doit être un pointeur nul.

...

Notez également que cela signifie que si argc est 0, alors argv[0] est garanti NULL. Comment printf traite un pointeur NULL lorsqu'il est utilisé en tant qu'argument d'un %s _ Le spécificateur n’est pas précisé dans la norme. De nombreuses implémentations produiront "(null)" dans ce cas mais je ne crois pas que cela soit garanti.

89
dbush

Pour ajouter aux autres réponses, rien dans C (POSIX ou non) n'empêche main () d'être appelée en tant que fonction dans le programme.

int main(int argc, int argv[]) {
    if (argc == 0) printf("Hey!\n");
    else main(0,NULL);

    return 0;
}
22
Possum

Oui, cela peut être zéro, ce qui signifie que argv[0] == NULL.

C'est une convention que argv[0] est le nom du programme. Vous pouvez avoir argc == 0 _ si vous lancez vous-même le binaire, comme avec execve famille et ne donnez aucun argument. Vous pouvez même donner une chaîne qui est loin d'être le nom du programme. C'est pourquoi en utilisant argv[0] obtenir le nom du programme n’est pas entièrement fiable.

En général, le shell dans lequel vous tapez votre ligne de commande ajoute toujours le nom du programme en premier argument, mais là encore, il s’agit d’une convention. Si argv[0] == "--help" et vous utilisez getopt pour analyser l’option, vous ne la détecterez pas car optind est initialisé à 1, mais vous pouvez définir optind à 0, utilisez getopt et l'option 'help' longue apparaîtra.

longue histoire courte: Il est parfaitement possible d'avoir argc == 0 (argv[0] n'est pas vraiment spécial en soi). Cela arrive quand le lanceur ne donne pas du tout l'argument.

19
Tom's

Les premières propositions exigeaient que la valeur de argc transmise à main () soit "un ou plus". Ceci était motivé par les mêmes exigences dans les projets de norme ISO C. En fait, les implémentations historiques ont dépassé la valeur zéro lorsqu'aucun argument n'a été fourni à l'appelant des fonctions exec. Cette exigence a été supprimée de la norme ISO C, puis de ce volume de POSIX.1-2017. Le libellé, en particulier l'utilisation de Word, requiert qu'une application POSIX strictement conforme passe au moins un argument à la fonction exec, garantissant ainsi que l'argc est égal à un ou plus lorsqu'il est appelé par une telle application. En fait, c'est une bonne pratique, car de nombreuses applications existantes font référence à argv [0] sans d'abord vérifier la valeur de argc.

La condition requise pour une application POSIX strictement conforme indique également que la valeur transmise comme premier argument est une chaîne de nom de fichier associée au processus en cours de démarrage. Bien que certaines applications existantes transmettent un nom de chemin plutôt qu'une chaîne de nom de fichier dans certaines circonstances, une chaîne de nom de fichier est généralement utile, car l'utilisation courante de argv [0] est l'impression de diagnostics. Dans certains cas, le nom de fichier transmis n'est pas le nom de fichier réel du fichier; Par exemple, de nombreuses implémentations de l'utilitaire de connexion utilisent une convention consistant à préfixer un ('-') au nom de fichier réel, ce qui indique à l'interpréteur de commande appelé qu'il s'agit d'un "shell de connexion".

Notez également que les utilitaires de test et [nécessitent des chaînes spécifiques pour que l'argument argv [0] ait un comportement déterministe dans toutes les implémentations.

Source


Argc peut-il être zéro sur un système POSIX?

Oui, mais cela ne serait pas strictement conforme à POSIX.

15
Stargateur

chaque fois que vous voulez exécuter un exécutable comme ./a.out il aura un argument qui est le program name. Mais il est possible d'exécuter un programme avec argc comme zero sous Linux, en l'exécutant depuis un autre programme qui appelle execv avec un empty argument list.

par exemple

int main() {
    char *buf[] = { NULL };
    execv("./exe", buf); /* exe is binary which it run with 0 argument */
    return 0;
}
3
Achal

TL; DR: Oui, argv[0] Peut être NULL, mais pas pour une raison bonne/raisonnable que je connaisse. Cependant, il y a des raisons de ne pas se soucier de savoir si argv[0] Est NULL et d'autoriser spécifiquement le processus à se bloquer si c'est le cas.


Oui, argv[0] Peut être NULL sur un système POSIX, si et seulement s'il a été exécuté sans aucun argument.

La question pratique la plus intéressante est, votre programme devrait-il s'en préoccuper.

La réponse à cette question est "Non, votre programme peut assumerargv[0] n'est pas NULL", car certains utilitaires du système (utilitaires de ligne de commande ) ne travaillent pas ou ne travaillent pas de manière non déterministe, lorsque argv[0] == NULL, mais plus important encore, il n’ya aucune bonne raison (autre que la stupidité ou des fins néfastes) pour laquelle un processus le ferait. (Je ne suis pas sûr si l'utilisation standard de getopt() échoue également, mais je ne m'attendrais pas à ce que cela fonctionne.)

Beaucoup de code, et même la plupart des exemples et utilitaires que j’écris, commencent par l’équivalent de

int main(int argc, char *argv[])
{
    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        printf("Usage: %s [ -h | --help ]\n", argv[0]);
        /* ... print usage ... */
        return EXIT_SUCCESS;
    }

et cela est raisonnable et acceptable, car il n’existe aucune raison valable pour un processus d’exécuter un autre processus sans fournir au moins le chemin de commande en cours d’exécution, c’est-à-dire execlp(cmd, cmd, NULL) plutôt que execlp(cmd, NULL).

(Cependant, je peux penser à quelques raisons néfastes, telles que l’exploitation de fenêtres de course associées à des commandes pipe ou socket: un processus pervers envoie une requête maléfique via une socket de domaine Unix établie, puis se remplace immédiatement par une commande victime autorisée (run). sans aucun argument, afin de garantir un temps de démarrage minimum), de sorte que, lorsque le service recevant la demande vérifie les informations d'identification de l'homologue, il voit la commande victime au lieu du processus pervers d'origine. Il est, à mon avis, préférable pour cette victime commandes pour planter dur et vite (SIGSEGV, en déréférencant un pointeur NULL), plutôt que d'essayer de se comporter "gentiment", donnant au processus diabolique une fenêtre temporelle plus longue.)

En d’autres termes, bien qu’il soit possible pour un processus de se remplacer lui-même par un autre, mais sans aucun argument, ce qui fait que argc est nul, un tel comportement est déraisonnable, au sens strict il n'y a aucune raison non-néfaste connue de le faire.

À cause de cela, et du fait que j'aime rendre la vie difficile aux programmeurs néfastes et indifférents et à leurs programmes, je n’ajouterai jamais le chèque trivial, semblable à

static int usage(const char *argv0)
{
    /* Print usage using argv0 as if it was argv[0] */
    return EXIT_SUCCESS;
}

int main(int argc, char *argv[])
{
    if (argc < 1)
        return usage("(this)");
    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
        return usage(argv[0]);

    /* argv[0] and argv[1] are non-NULL, argc >= 2 */

sauf si cela est demandé par une personne ayant à l’esprit un cas d’utilisation existant. Et même alors, je serais un peu méfiant, voulant vérifier moi-même le cas d'utilisation en premier.

3
Nominal Animal