Convertir une chaîne de caractères en trois nombres entiers

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

Bonjour,

Comment feriez-vous pour écrire une fonction en C qui étant donnée une chaîne de caractère de la forme XXX YYY ZZZ (NNN un nombre à 3 chiffres), remplit un tableau d’entiers avec les 3 nombres entiers positifs correspondants ?

Par exemple, si j’ai la chaîne "231 208 006", la fonction remplit un tableau d’entiers pour avoir comme résultat [231, 208, 6].

J’imagine que ma solution n’est pas optimale, comment auriez-vous procédé ?
Est-ce important de mettre unsigned quand c’est possible ?

#include <stdio.h>
#include <stdlib.h>

void str2int(char* s, int* int_array)
{
    for (unsigned int i = 0; i < 2; i++)
    {
        int_array[i] = (int) strtol(s + 4*i, NULL, 10);
    }
}

int main()
{
    int result[3];
    
    str2int("231 208 006", result);
    
    printf("Résultat : [%d, %d, %d]", result[0], result[1], result[2]);
    return 0;
}
+0 -0

Ta fonction renvoie un tableau ?!

Un tableau dynamique donc ?

Sinon, si tu sais déjà que tu va retourner des nombres, tu peux utiliser des pointeurs sur unsigned.

+0 -0

Ta fonction renvoie un tableau ?!

J’ai corrigé le code, en fait il n’y a pas besoin de renvoyer de tableau, elle remplit celui passé en paramètre.

Un tableau dynamique donc ?

On peut admettre qu’il n’y a que 3 entiers à gérer, un tableau statique suffit.

Sinon, si tu sais déjà que tu va retourner des nombres, tu peux utiliser des pointeurs sur unsigned.

ache

En fait, ma question était plus générale : faut-il toujours, lorsque possible, utiliser unsigned ou cela n’est-il pas nécessaire ?

Ta fonction renvoie un tableau ?!

J’ai corrigé le code, en fait il n’y a pas besoin de renvoyer de tableau, elle remplit celui passé en paramètre.

Ok. Donc tu renvoyais un pointeur sur le premier élément du tableau. Renvoyer un tableau n’est pas possible. Généralement renvoyer un pointeur sur le premier élément du tableau est une erreur car la durée de vie du tableau est limité à la fonction.

Sinon, si tu sais déjà que tu va retourner des nombres, tu peux utiliser des pointeurs sur unsigned.

ache

En fait, ma question était plus générale : faut-il toujours, lorsque possible, utiliser unsigned ou cela n’est-il pas nécessaire ?

info-matique

Peu importe, reste juste juste logique. Si ta fonction compte un nombre d’élément alors tu peux utiliser usize, unsigned. Si ta fonction renvoie une valeur dont les valeurs positives ont un sens alors il peut être judicieux d’utiliser des int

+0 -0

Peu importe, reste juste juste logique. Si ta fonction compte un nombre d’élément alors tu peux utiliser usize, unsigned. Si ta fonction renvoie une valeur dont les valeurs positives ont un sens alors il peut être judicieux d’utiliser des int

ache

Okey, merci. :)
Pour être sûr d’avoir bien compris, tu voulais plutôt dire :

Si ta fonction renvoie une valeur dont les valeurs négatives ont un sens alors il peut être judicieux d’utiliser des int

A mon avis, ça passe avec sscanf:

sscanf(chaine, "%d %d %d", &tab[0], &tab[1], &tab[2]);

Sinon, ta fonction est assez restrictive, elle suppose toujours que les nombres ont chacun trois chiffres. Pour faire mieux, tu pourrais utiliser le deuxième paramètre de strtol.

En fait, ma question était plus générale : faut-il toujours, lorsque possible, utiliser unsigned ou cela n’est-il pas nécessaire ?

La question de base est de savoir quelles valeurs peuvent prendre tes nombres. Par exemple pour short c’est ±32767, et unsigned short 0–65535. Si tu es certain que tes nombres ne seront jamais négatifs, ou que tu as besoin de la plage 32768–65535, alors le bon sens te conseille de choisir un type non signé.

Ca c’est la théorie. En pratique personnellement j’essaie d’éviter les types non signés, sauf si j’y suis obligé ou si c’est vraiment important et utile. Le fait est qu’il faut être particulièrement précautionneux quand on manipule des types non signés, plus qu’avec des types signés.

Exemple avec un piège à con: quel est le résultat de ce programme ?

unsigned long a=10, b=15, c=8;
printf("%d", a -b +c);

Vous avez dit 3 ? Ben non, perdu ! Du moins c’est pas garanti que ça vous donne 3 chez vous.

Je me suis fait avoir des dizaines de fois avec cette petite blague… alors tant qu’on peut l’éviter, on essaie de le faire. ET là c’est encore facile, mais quand ça arrive avec des pointeurs… ça fait des bonnes parties de debug en perspective.

+1 -0

A mon avis, ça passe avec sscanf:

En fait, j’ai besoin d’additionner les nombres une fois la conversion terminée. L’affichage se fait à la fin.

Sinon, ta fonction est assez restrictive, elle suppose toujours que les nombres ont chacun trois chiffres. Pour faire mieux, tu pourrais utiliser le deuxième paramètre de strtol.

C’est vrai, mais le cahier des charges du projet nous permet d’admettre qu’ici on travaille avec 3 paramètres (sûr et certain).

Exemple avec un piège à con: quel est le résultat de ce programme ?

Bizarre… je ne comprends pas où est le piège ? Chez moi, avec ou sans la conversion explicite vers (int), le programme affiche 3. Est-ce que l’opérateur - a un comportement non standard avec des types non signés ?

Merci pour cette remarque, ça me permettra de faire attention à la manipulation des types non-signés.

Salut,

Exemple avec un piège à con: quel est le résultat de ce programme ?

unsigned long a=10, b=15, c=8;
printf("%d", a -b +c);

Vous avez dit 3 ? Ben non, perdu ! Du moins c’est pas garanti que ça vous donne 3 chez vous.

QuentinC

Mmm… Pourtant, le comportement est décrit par la norme, non ?

A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

ISO/IEC 9899:2017, doc. N2176, § 6.2.5, al. 9, p. 31.

Du coup, normalement, 10 - 15 => 10 + (-15), => 10 + (ULONG_MAX - 14) => (ULONG_MAX - 4). Puis, (ULONG_MAX - 4) + 8 => (ULONG_MAX + 4) % (ULONG_MAX + 1) => 3. Par contre, c’est le format %ld (ou %lu) qui doit être utilisé.

Édit : correction dans les calculs.

+0 -0

Okey, merci. :)
Pour être sûr d’avoir bien compris, tu voulais plutôt dire :

Si ta fonction renvoie une valeur dont les valeurs négatives ont un sens alors il peut être judicieux d’utiliser des int

info-matique

._.
Oui tout à fait ! Je ne dors pas assez …

+0 -0

En fait, j’ai besoin d’additionner les nombres une fois la conversion terminée. L’affichage se fait à la fin.

L’un n’empêche pas l’autre. La fonction sscanf fonctionne comme scanf mais lit à partir d’une chaîne au lieu de l’entrée standard. Si tu as toujours trois chiffres tu peux utiliser le format %03d.

Mmm… Pourtant, le comportement est décrit par la norme, non ?

Je te jure que cette bizarrerie m’est pourtant arrivée…

Sauf que dans mon cas c’était en C++ et sur des pointeurs, ça change quelque chose ?

C’était une instruction du genre p1 = (p1-p2)+p3 qui, écrite sous la forme p1 = p1+p3-p2, plantait si p2>p3. Pourtant c’est censé être mathématiquement identique. Dans mon programme je suis certain que p1>=p2 donc avec la première forme je n’ai plus de problèmes.

Du coup depuis je me méfie, peut-être un peu trop…

Entre ça et les casts implicites pas toujours très clairs sauf à connaître la norme par coeur, je préfère vraiment éviter. J’ai aussi eu des trucs du genre 245 + 20 = -118 pourtant stocké dans un int parce que le cast implicite unsigned char -> int n’était pas fait au bon moment, alors qu’en signed char ça marchait.

+0 -0

Je te jure que cette bizarrerie m’est pourtant arrivée…

QuentinC

Ah ! Mais, je te crois. C’est juste que le comportement me semble bien établi par la norme (mais je peux mal interpréter le passage).

Sauf que dans mon cas c’était en C++ et sur des pointeurs, ça change quelque chose ?

QuentinC

Pour le C++, je ne sais pas, pour les pointeurs, oui, clairement, l’arithmétique des pointeurs est nettement moins bien définie que l’arithmétique non signée.

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

ISO/IEC 9899:201x, doc. N1570, § 6.5.6, al. 8, p. 93

Sinon, c’est toujours mieux de se méfier un peu en C, les règles implicites sont effectivement parfois vivieuses. :p

+0 -0

Sauf que dans mon cas c’était en C++ et sur des pointeurs, ça change quelque chose ?

C’était une instruction du genre p1 = (p1-p2)+p3 qui, écrite sous la forme p1 = p1+p3-p2, plantait si p2>p3. Pourtant c’est censé être mathématiquement identique. Dans mon programme je suis certain que p1>=p2 donc avec la première forme je n’ai plus de problèmes.

QuentinC

C’est peut-être là d’où viens le problème. L’expression p1 = p1+p3-p2 est évalué en p1 = (p1+p3)-p2, ce qui veux dire que tu additionnes deux pointeurs et j’ai des doutes que ce soit bien défini dans la norme. En revanche p1 = p1+(p3-p2) ne devrait poser aucun problème vu que p3-p2 est bien défini et retourne un std::ptrdiff_t, qui s’additionne sans problème avec un pointeur (à condition que le résultat pointe dans le même array que p1).

En revanche p1 = p1+(p3-p2) ne devrait poser aucun problème vu que p3-p2 est bien défini et retourne un std::ptrdiff_t, qui s’additionne sans problème avec un pointeur à condition que le résultat pointe dans le même array que p1.

Ce qui était justement faux dans mon cas. Ca aurait été plus clair avec des vrais noms de variable:
offset = (offset - oldBase) + newBase
D’où j’ai encore omis de mentionner ici les cast en uintptr_t et retour pour que ça marche.

Le but était donc de déplacer un pointeur décalé suite à un agrandissement de capacité. Avec des index c’était 3 fois plus lent… en zone critique, gestion de la pile d’exécution dans mon mini-langage.

Enfin bref, désolé pour ce petit HS et pour avoir activé le mode parano

+0 -0

Ben déjà, un pointeur négatif, je sais pas où il pointe, mais certainemnt pas dans la bonne direction.

Dis toi que les opérations entre les pointeurs (+ et -), ça se fait seulement entre les mêmes zones mémoires. Sinon, c’est pas standard.

+0 -0

Mais si j’ai plus ou moins suivi la discussion, les problèmes d’opérations avec les types non-signés ne concernent que les pointeurs ?

info-matique

Ce qu’il faut retenir, c’est que l’arithmétique non signée est la mieux définie par le standard. Quoi que tu fasses avec, il n’y aura jamais de dépassements ou de comportements indéfinis. Dans le cas où une valeur est trop grande ou trop petite pour être représentée par un type non signé, le maximum du type augmenté de un est soit soustrait soit ajouté successivement jusqu’à ce que la valeur soit représentable (ce n’est pas ce qu’il se passe au niveau du processeur, mais le résultat obtenu est le même).

unsigned char n = -15; /* -15 + 256 == 241 */
unsigned char m = 1023; /* 1023 - 256 - 256 -256 == 255 */

Toutefois, il faut bien faire attention aux conversions implicites en C qui peuvent modifier le type des opérandes ou du résultat de sorte que le calcul ne s’opère plus suivant l’arithmétique non signée. Typiquement, faire la somme entre un unsigned char et un int provoquera la conversion du type unsigned char vers le type int et le résultat sera un int.

unsigned char n = 1;
int m = 255;

printf("%d\n", n + m); /* Peut-être lu comme (int)n + m == 256 */

Pour ce qui est des pointeurs, comme te l’as précisé @ache, c’est une arithmétique à part. ;)

+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