Jump to content

[Résolu] C : valeur d'un string récupérée en input


Jarodd

Recommended Posts

Bonjour,

Je suis grand débutant en C, j'essaye de faire fonctionner un programme qui récupère en entrée une valeur double, suivi d'une lettre. Mon code :

# include <stdio.h>

int main() {

    double valeur;
    char lettre[1];
    scanf("%lf", &valeur);
    scanf("%s", lettre);
        
    if (lettre == 'v') {
        printf("unité == v");
    }
    else {
    	printf("ko : %s", lettre);
	}

    return 0;
}

Le code exécutable est accessible ici : https://ideone.com/PDsZiV

En entrée j'ai 2 valeurs : "12.3 v" (peu importe les valeurs, je teste les types).

Mais je ne parviens pas à comprendre la valeur qu'a la variable lettre. J'ai tenté avec char lettre; (en enlevant [1]), je ne rentre jamais dans le if. Cela fait 2h que je m'arrache les cheveux là dessus, j'ai besoin d'un coup de main (je suppose que la solution est évidente...)

Merci pour votre aide :smack:

Link to comment
Share on other sites

Lorsque le compilateur te dit "main.c:XX:13: attention : comparaison entre un pointeur et un entier" cela sent mauvais :siffle:

Édit : J'ai un doute sur le tableau d'une case, parce que scanf ajoute un '\0' terminal (soit minimum 2 caractères)

Sinon

# include <stdio.h>

int main(int /*argc*/, char** /*argv*/) {
    double valeur;
    char space;
    char lettre;
    char str[1]; // <- c'est moche un tableau d'une case
                 //    juste pour avoir un pointeur

//  Version 1

    scanf("%lf", &valeur);
    scanf("%c", &space);
    scanf("%c", &lettre);
        
    if (lettre == 'v') {
        printf("unité == v\n\n");
    } else {
        printf("ko : %c\n\n", lettre);
    }


//  Version 2

    scanf("%lf", &valeur);
    scanf("%1s", str); // <- pour éviter les overflow
        
    if (str[0] == 'v') {
        printf("unité == v");
    } else {
        printf("ko : %s", str); // <- scanf ajoute un '\0'
    }

    return 0;
}

 

Link to comment
Share on other sites

Hello !

Les pointeurs, c'est facile :D !

int / char / double / float / long / etc... Ce sont des types.
int* / char* / double* / float* / long* / etc... Ce sont des pointeurs sur types.
int[N] / char[N] / double[N] / float[N] / long[N] / etc... Ce sont des tableau statiques de N éléments du type désiré.

Pour un type basique déclaré en variable locale ou en paramètre, la valeur de la variable est stockée sur la pile et la variable représente directement cette valeur.

Pour un pointeur déclaré en variable locale ou en paramètre, la valeur de la variable stockée sur la pile est en fait juste une adresse mémoire. Et à cette adresse (qui peut se situer n'importe où, sur la pile ou sur le tas) se trouve la valeur "pointée" par la variable. Si l'adresse est invalide, il y aura un comportement indéterminé lorsque tu essaieras de lire ou écrire à cette adresse (en général ça produit une erreur de segmentation. Mais pas forcément !).

Pour un tableau statique de taille N déclaré en variable locale ou en paramètre, un nombre N d'éléments sont alloués sur la pile et la variable désignant ce tableau représente en fait l'adresse du premier élément du tableau. Donc c'est un peu particulier dans le sens où ce n'est pas vraiment un pointeur (l'adresse n'est pas stockée sur la pile), mais ça se comporte comme un pointeur (quand on demande la valeur de la variable, on récupère une adresse mémoire). Donc c'est comme un pointeur, mais la valeur est directement déterminée à la compilation (en tant qu'offset sur le "frame pointer" si on veut rentrer dans les détails). Du coup il est impossible de changer l'adresse pointée par un array, contrairement à un pointeur normal.

Donc pour revenir plus précisément à ta question :

char c; //ceci est un caractère
char *c; //ceci est un pointeur sur un caractère
char c[1]; //ceci est un tableau statique de caractères de taille 1 (contenant donc un seul caractère)

Donc pour utiliser un pointeur sur caractère plutôt qu'un tableau statique de caractères, il suffit donc de déclarer :

char c; //ceci est le "vrai" caractère, là où sera effectivement stockée la valeur du caractère
char *pc = &c; //pc est désormais un "pointeur sur c", un vrai pointeur sur caractère qui possède une adresse valide puisqu'il pointe vers un vrai caractère existant

Voilà, c'est tout simple :D ! Et pour les chaines de caractères, comme le disait foetus, ne pas oublier qu'en C (et C++) elles sont terminées par un '\0'. Donc pour stocker une chaine de N caractères, il faut disposer d'un espace de N+1 caractères. Et cet espace s'obtient soit par la déclaration d'un tableau statique (char string[N+1];), soit par la déclaration d'un tableau dynamique ( char* string = (char*)( malloc( (N+1) * sizeof(char) ) ); ). Dans le cas d'un tableau dynamique, ne pas oublier de le désallouer quand on n'en a plus besoin (free(string);).

Vive le C (et encore plus le C++ :yes: )
 

Link to comment
Share on other sites

Il y a 2 heures, Jarodd a dit :

Pour comprendre, %1s ça correspond à une string d'1 caractère ?

Regarde la documentation de printf ou scanf pour comprendre le format :D

 

Il y a 2 heures, Jarodd a dit :

Edit : et si le tableau d'une case est moche, comment déclarer un char pour 1 caractère ?

C'est la version 1 :D avec un seul caractère et l'utilisation des %c

Par contre, j'ai fait des tests, et scanf semble passer tous les espaces (trim) avant de prendre la chaîne de caractères.

Donc avec un seul caractère, il faut le gérer à la main (c'est pour cela que j'ai rajouté la variable space pour passer l'espace entre le flottant 12.3 et l'unité v)

Link to comment
Share on other sites

Le truc c'est que le cours n'a pas (encore) abordé les pointeurs. Donc même si je pige (enfin, je crois) la notion, je ne suis pas censé l'utiliser pour mon exercice. J dois juste saisir les 2 données en entrée (un double et une lettre) pour en faire des trucs après, mais là je sèche pour la récup de la lettre. Et je voudrais le faire sans pointeur, j'espère que c'est possible !

Sinon, si je vous comprends bien, pour saisir la lettre d'un caractère, je dois faire char lettre[2] ? Et ensuite faire if(lettre[1]) pour comparer ma lettre (sans le \0) ? Pour gérer l'espace, je pensais le faire en le mettant dans les deux valeurs, par exemple scanf("%d %d") soit un entier un espace un entier. C'est pas la bonne méthode ? Le cours ne parle pas de les mettre dans une variable pour les traiter :zarb:

Edit : il y a un truc qui m'échappe : après le scanf(), je fais un printf() de ma lettre, j'ai "v" qui s'affiche.
Mais ensuite je ne passe pas dans le if(lettre == 'v')... Serait-ce à cause du \0 ?
Voir exemple en ligne : https://ideone.com/tJnE9m

Link to comment
Share on other sites

Hello !

Tu as bien compris, étant donné que tu lis une chaine de caractère (%s) et non pas un simple caractère (%c), un '\0' est automatiquement ajouté à la fin. Du coup tu as bien besoin de déclarer un "char string[2]" (tu peux éviter ça en faisant un %c au lieu de %s ou %1s).

Pour ce qui est du "if(lettre=='v')" qui ne marche pas, tu as en effet trouvé la bonne raison. "lettre" est pointeur, donc en faisant "lettre == 'v'" tu compares une adresse mémoire et un caractère. Pas étonnant que ça ne fonctionne pas ! Tu dois donc faire "if(lettre[0] == 'v')", pour prendre le premier caractère de la chaine pointée par "lettre". Attention, n'oublies pas qu'en C le premier indice des tableaux est 0, pas 1 !

Pour finir, tu dis que tu n'es pas censé utiliser de pointeurs pour ton exercice. Mais en fait tu n'a pas le choix, scanf prend des pointeurs en arguments :transpi:. Dans ton code, "valeur" est un double, "&valeur" est un pointeur sur double. Donc tu utilises des pointeurs, même si tu ne créé pas de variable de type "pointeur" :D.

Je ne te donne pas de "code solution" mais juste des explications, car à mon avis c'est en forgeant qu'on devient forgeron. Et c'est donc en essayant toi même décrire 15x le code en essayant de comprendre pourquoi ça ne marche pas, jusqu'à ce que finalement ça marche, que tu comprendras vraiment la logique derrière le code. Et une fois que tu auras vraiment bien compris comment est gérée la mémoire (différence pile / tas / static memory, différence entre un pointeur et un static array, organisation des variables et des paramètres sur la pile, comment marchent le stack pointer et le frame pointer, que représente l'adresse de retour d'une méthode, etc...), alors tu pourras écrire un code à la fois robuste et performant. Et non pas, comme bien souvent, un code qui marche pour les quelques exemples du TP, mais qui crashe dès qu'on sort un peu des valeurs ou de la volumétrie prévue :transpi:.

 

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...