Extraire le premier paramètre entré en ligne de commande

Le problème exposé dans ce sujet a été résolu.

Bonjour,

J’essaye d’écrire un programme qui, étant exécuté avec des paramètres, stocke le premier paramètre dans une variable (c’est le résultat recherché ici), et ensuite, l’affiche à l’écran (pour juste vérifier que c’est OK).

Par exemple, si j’appelle mon programme avec test.exe -param1 -param2 ..., le programme doit stocker dans une variable et afficher -param1.

Bien que le code fonctionne plus ou moins bien, j’ai l’impression de me compliquer la vie.
Peut-on faire plus simple, plus intuitif ? Avec votre expérience, comment auriez-vous fait ?

Toute la difficulté réside dans la "conversion" du char* vers le char[].
J’ai supposé que -param1 ne dépasse pas 30 caractères, par défaut.

#include <stdio.h>
#define LENGTH 30

void extract(char* p, char arr[])
{
    int i = 0;
    
    while (*p != '\0' && i < LENGTH)
    {
        arr[i] = *p;
        i++, p++;
    }
    
    arr[i] = '\0';
}

int main(int argc, char* argv[])
{
    char result[LENGTH];
    
    if (argc > 1)
    {
        extract(argv[1], result);
        printf("Le premier paramètre entré est : %s\n", result);
    }
    
    return 0;
}

Salut,

En effet, la gestion des chaines de caractère en C est affreuse, mais le standard met quand même à disposition quelques fonctions pour nous rendre la vie plus simple. Dans ton cas tu peux utiliser strcpy (voir l’exemple).

Et tu es sûr que tu as besoin d’une autre variable que argv ?

+0 -0

Pourquoi pas simplement:

int main(int argc, char * argv[]){
  if(argc > 1){
    printf("First parameter: %s\n" argv[1]);
  }
}

?

Et s’il s’agit d’obtenir un accès à la chaîne:

int main(int argc, char * argv[]){
  char const * param = NULL ;
  if(argc > 1){
    param = argv[1];
  }
}

Sinon il faudra mesurer la chaîne, allouer dynamiquement et utiliser strcpy.

Ben déjà, i++, p++, tu peux litérallement le faire à la ligne précédente. Mais ça n’a pas beaucoup d’importance.

Par-contre, ton code comporte un bug, si la paramètre fait 30 char ou plus, le \0 est placé une case après le tableau.

Ensuite, en C, on va souvent réutiliser les fonctions standard pour ce genre de choses. Donc strncpy ici. ^^

Sinon, quel est l’intéret de ton code ? La conversion entre char[] et char* ? Je comprend ce que tu veux dire mais ça n’a que très peux d’intérêt. Par exemple dans ta fonction:

void extract(char* p, char arr[])

Ici, c’est strictement équivalent à :

void extract(char* p, char* arr)
// ou encore à :
void extract(char* p, char arr[2])

Car une fonction ne prend pas en paramètre un tableau. En C, on passe les tableaux par références. Quand on écrit :

int tab[5] = {2, 3, 4, 5, 6};
printf("%p", (void*)tab);

Ici, tab qui est un tableau, est implicitement convertit en pointeur sur son premier élément. Une fonction ne peut pas recevoir de tableau en argument.

Edit: Je me suis rendu compte que j’ai pas beaucoup étofé ma première affirmation. Ce que je voulais dire c’est que :

    while (*p != '\0' && i < LENGTH)
    {
        arr[i] = *p;
        i++, p++;
    }

C’était équivalent à ça :

    while (*p != '\0' && i < LENGTH) // Ici, tu peux mettre *p plutôt que *p != '\0'
        arr[i++] = *p++;

Le C a été développé dans l’optique d’obtenir une syntaxe courte.

+0 -0

Salut,

Ensuite, en C, on va souvent réutiliser les fonctions standard pour ce genre de choses. Donc strncpy ici. ^^

ache

Juste pour dire, la fonction strncpy() n’est pas franchement super pour ce genre de chose (en fait, elle n’est pas franchement super tout court :-° ), car elle n’insère pas forcément de caractère nul final (s’il n’y a pas assez d’espace pour la copie, il n’y aura pas de caractère nul), ce qui fait qu’il faut quand même vérifier derrière si tout est bon.

+0 -0

Ensuite, en C, on va souvent réutiliser les fonctions standard pour ce genre de choses. Donc strncpy ici. ^^

ache

Juste pour dire, la fonction strncpy() n’est pas franchement super pour ce genre de chose (en fait, elle n’est pas franchement super tout court :-° ), car elle n’insère pas forcément de caractère nul final (s’il n’y a pas assez d’espace pour la copie, il n’y aura pas de caractère nul), ce qui fait qu’il faut quand même vérifier derrière si tout est bon.

Taurre

? Faut juste le prévoir nan ?

char tab[LENGTH];
tab[LENGTH-1] = 0;
strncpy(tab, src, LENGTH-1);

Edit: Rajout du n et correction de la longueur LENGTH-2 plutôt que LENGTH-1. ^^

Edit2: Je viens de vérifier et il existe strncpy_s qui est plus adapté ! Disponible depuis C11 seulement ^^

+0 -0

? Faut juste le prévoir nan ?

ache

Yep, mais il n’y a pas que ça. Dans les faits, cette fonction a été taillée pour les copies entre champs de la structure dirent sous UNIX. C’est ce que traduit son comportement lors de la copie (je ne mets plus la main sur l’article qui parlait de ça).

If the array pointed to by s2 is a string that is shorter than n characters, null characters are appended to the copy in the array pointed to by s1 , until n characters in all have been written.

ISO/IEC 9899:2017, doc. N2176, § 7.24.2.4, The strncpy function, al. 3, p. 265
#include <stddef.h>
#include <stdio.h>
#include <string.h>


int
main(void)
{
    char dst[] = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x' };
    char src[] = "strncpy";

    strncpy(dst, src, sizeof dst - 1);

    for (size_t i = 0; i < sizeof dst; ++i)
        printf("dst[%u] = %c\n", i, dst[i]);

    return 0;
}
Résultat
dst[0] = s
dst[1] = t
dst[2] = r
dst[3] = n
dst[4] = c
dst[5] = p
dst[6] = y
dst[7] = 
dst[8] = 
dst[9] = x

Bref, elle est pas tip top, cette fonction standard. ^^"

+2 -0

Merci pour vos réponses !

La solution non dynamique la plus simple et intuitive reste selon moi :

#include <stdio.h>
#include <string.h>
#define LENGTH 30

int main(int argc, char* argv[])
{
    char result[LENGTH];
    
    if (argc > 1)
    {
        strcpy(result, argv[1]);
        printf("Le premier paramètre entré est : %s\n", result);
    }
    
    return 0;
}

Par-contre, ton code comporte un bug, si la paramètre fait 30 char ou plus, le \0 est placé une case après le tableau.

Effectivement, et par ailleurs le nombre 30 étant arbitraire, je vais me pencher sur une solution dynamique ci-dessous pour être plus flexible.

Sinon, quel est l’intéret de ton code ? La conversion entre char[] et char* ? Je comprend ce que tu veux dire mais ça n’a que très peux d’intérêt.

En fait, ce code a été conçu dans le cadre d’un exercice qui demande d’accéder à un fichier dont le nom est donné comme paramètre et je cherchais un moyen de passer d’un char* vers un char[] pour me simplifier la gestion du nom de fichier. Or, je me rends compte après recherches que la fonction FILE * fopen ( const char * filename, const char * mode ); prend en paramètre un pointeur et non un tableau, donc en fait oui le code n’a que peu d’intérêt.

Concernant une solution dynamique, j’ai pensé à :

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    // On ajoute +1 pour le caractère '\0'
    unsigned const int SIZE = 1 + strlen(argv[1]);
    
    char result[SIZE];
    
    if (argc > 1)
    {
        strcpy(result, argv[1]);
        printf("Le premier paramètre entré est : %s\n", result);
    }
    
    return 0;
}

Merci pour vos réponses !

La solution non dynamique la plus simple et intuitive reste selon moi :

Toujours un problème dès que le premier paramètre est plus long que 30 caractères.D’où l’utilisation de strncpy_s plutôt que strcpy !

Effectivement, et par ailleurs le nombre 30 étant arbitraire, je vais me pencher sur une solution dynamique ci-dessous pour être plus flexible.

Dans mon domaine, 30 est la constante de Guédon. Pas besoin d’aller plus loin ^^

Sinon, quel est l’intéret de ton code ? La conversion entre char[] et char* ? Je comprend ce que tu veux dire mais ça n’a que très peux d’intérêt.

En fait, ce code a été conçu dans le cadre d’un exercice qui demande d’accéder à un fichier dont le nom est donné comme paramètre et je cherchais un moyen de passer d’un char* vers un char[] pour me simplifier la gestion du nom de fichier. Or, je me rends compte après recherches que la fonction FILE * fopen ( const char * filename, const char * mode ); prend en paramètre un pointeur et non un tableau, donc en fait oui le code n’a que peu d’intérêt.

Ok ^^
Aucune fonction ne prend de tableau en paramètre. C’est toujours des pointeurs. :)

Concernant une solution dynamique, j’ai pensé à :

Problème s’il n’y a pas d’argument (argc <= 1). Alors |argv[1] == NULL) et donc strlen(argv[1]) va désindexer le pointeur NULL. Déplace juste la déclaraton de SIZE dans le if. Et donc celle du tableau aussi.

Sinon, tu as conscience que tu utilises un VLA là ?

+0 -0

En fait, ce code a été conçu dans le cadre d’un exercice qui demande d’accéder à un fichier dont le nom est donné comme paramètre et je cherchais un moyen de passer d’un char* vers un char[] pour me simplifier la gestion du nom de fichier. Or, je me rends compte après recherches que la fonction FILE * fopen ( const char * filename, const char * mode ); prend en paramètre un pointeur et non un tableau, donc en fait oui le code n’a que peu d’intérêt.

info-matique

C’était louche, c’est rarement le cas qu’on aie besoin de copier les options.

unsigned const int SIZE = 1 + strlen(argv[1]); char result[SIZE];

info-matique

pour creer des tableaux dynamiques, regarde du côté du malloc. Et surtout lis attentivement un bon cours, car il aurait dû te l’apprendre.
Pour pouvoir déclarer un tableau statique, il faut connaitre sa taille à la compilation, or ta valeur de SIZE n’est connu qu’à l’exécution. EDIT : grillé, et du coup ce que je dis est faux, je connaissais pas le VLA, je pensais que c’était simplement interdit

+0 -0

Problème s’il n’y a pas d’argument (argc <= 1). Alors |argv[1] == NULL) et donc strlen(argv[1]) va désindexer le pointeur NULL. Déplace juste la déclaraton de SIZE dans le if. Et donc celle du tableau aussi.

Bien vu !

Sinon, tu as conscience que tu utilises un VLA là ?

Un quoi ? o_O

Salut info-matique.

La solution non dynamique la plus simple et intuitive reste selon moi :

#include <stdio.h>
#include <string.h>
#define LENGTH 30

int main(int argc, char* argv[])
{
    char result[LENGTH];
    
    if (argc > 1)
    {
        strcpy(result, argv[1]);
        printf("Le premier paramètre entré est : %s\n", result);
    }
    
    return 0;
}

info-matique

Cet article explique en quoi ton code présente un risque critique pour l’utilisateur.

La question a déjà été résolue, mais je me dis que cette ressource peut, à défaut de te permettre d’approfondir tes connaissances, te sensibiliser.

Bien à toi.

@info-matique: Il a la version auteur ^^

Voici la bonne version: https://zestedesavoir.com/articles/100/introduction-aux-buffer-overflows/

D’ailleurs en général, tous les artciles de Ge0 sont intéressants à ce sujet ! :D

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte