Seuls les membres ayant 30 points peuvent parler sur le chat.

Forum Casio - Vos tutoriels et astuces


Index du Forum » Vos tutoriels et astuces » [Tutoriel] Utiliser la bibliothèque standard du C
LephenixnoirHors ligneAdministrateurPoints: 15740 Défis: 136 Message

[Tutoriel] Utiliser la bibliothèque standard du C

Posté le 27/04/2014 19:16

La bibliothèque standard du C est une mine d'or pour les développeurs quels qu'ils soient, et je trouve bête de la voir si rarement utilisée.
Voilà une présentation de ce qu'elle vous permet de faire.
Avant toute utilisation de ces fonctions, n'oubliez pas d'ajouter
#include <stdio.h>
#include <stdlib.h>
au début de votre programme.

Sommaire

-> Les fonctions de formatage
-> Les fonctions de conversion
-> Les fonctions de l'aléatoire
-> Les valeurs absolues
-> Les divisions euclidiennes
-> L'allocation dynamique
-> Fonctionnalités avancées




Les fonctions de formatage

Ces fonctions, vous les connaissez sûrement, si vous avez fait du C sur ordinateur. Les plus simples sont les fonctions printf() et scanf().
Si vous ne les connaissez pas, je vous conseille d'aller jeter un coup d'oeil ici et , car elles sont essentielles pour la suite.
Le problème de ces deux fonctions, c'est qu'elles utilisent les flux d'entrée/sortie standard du C, qui n'existent pas sur la calculatrice. Du coup ces fonctions ont été retirées, et le SDK vous insulte si vous tentez de les utiliser.

Mais qu'importe ! Car la bibliothèque standard dispose d'autres fonctions de lecture/écriture formatée. Les plus utiles d'entre elles vous seront probablement:
sprintf(char *str, const char *format, ...);
sscanf (char *str, const char *format, ...);

Ces deux fonctions ont exactement le même rôle que printf() et scanf(), mais ne vont pas lire/écrire via les flux d'entrée/sortie standard, mais dans des chaînes de caractères.
Il n'y a donc aucune sortie à l'écran ni aucune demande de saisie, tout se passe dans des variables.

sprintf()
Prenons un exemple assez simple. Je veux écrire dans une chaîne de caractères l'adresse d'un fichier, dont l'utilisateur a entré le nom. Pour obtenir l'adresse complète, il suffit de mettre
char ch[20], name[20];
sprintf(ch,"\\\\fls0\\%s",name);

name contient le nom du fichier.
La chaîne ch contient maintenant l'adresse complète du fichier. Il ne reste plus qu'à l'ouvrir !
sprintf() permet non seulement le debuggage de valeurs, mais aussi la fusion des chaînes de caractères, si vous mettez plusieurs "%s" dans votre format.

sscanf()
Prenons un autre exemple. Je voudrais que l'utilisateur entre un nombre via une interface simple basée sur GetKey(). Ce nombre se trouve à la fin de la saisie dans une chaîne de caractères, comme dans l'exemple suivant.
char nombre[8] = "23745";
int x;

Ensuite, il faut que x prenne la valeur entière de 23745. C'est là que sscanf() intervient:
sscanf(nombre,"%d",&x);

Et c'est tout ! x prend la valeur que la fonction a lue dans la chaîne de caractères nombre, et contient maintenant la valeur souhaitée.

vsprintf()
Cette fonction a le même rôle que sprintf, mais prend en troisième argument une liste d'arguments (va_list).




Les fonctions de conversion[/big]

Ce sont des fonctions qui se comportent à peu près comme sscanf(), sauf qu'elles ne vont lire qu'une valeur. Avec sscanf(), il suffit de mettre plusieurs expression du type "%d" pour lire plusieurs valeurs dans la chaîne.
Reprenons l'exemple précédent. L'intruction sscanf() peut être remplacée par quelque chose d'encore plus simple.
x = atoi(nombre);

La fonction atoi() (pour ASCII To Integer) va lire la chaîne de caractères comme un nombre entier, générer ce nombre et le renvoyer.

Il y a dans la bibliothèque standard cinq fonctions qui ont ce rôle, décliné à chaque fois.
double atof   (const char *nptr);
int    atoi   (const char *nptr);
long   atol   (const char *nptr);
double strtod (const char *nptr, char **endptr);
long   strtol (const char *nptr, char **endptr, int base);

Les trois premières sont les plus utilisées. Elle permettent de lire une chaîne de caractères comme un nombre, respectivement un float, un int et un long.




Les fonctions de l'aléatoire[/big]

Il est inutile d'en parler ici, puisqu'il existe déjà un excellent tutoriel de Dark Storm pour cela.




Les valeurs absolues[/big]

Là sont deux fonctions pratiques de la bibliothèque standard.
int  abs  (int i);
long labs (long l);

Les deux renvoient la valeur absolue du paramètre. abs pour les int, labs pour les long.




Les divisions euclidiennes[/big]

La bibliothèque standard permet même de faire des divisions euclidiennes !
Les structures div_t et ldiv_t permettent de gérer ces nombres, la première en utilisant des int, la deuxième avec des long.
Les deux fonctions pour diviser sont très simples.
div_t  div  (int number, int denom);
ldiv_t ldiv (long number, long denom);

Enfin, on peut accéder aux membres de ces structures comme suit.
div_t x;
x = div(34,6);
x.quot; // = 5
x.rem;  // = 4





L'allocation dynamique[/big]

Le gros morceau de la bibliothèque standard. Cette notion s'appuie plus que largement sur les pointeurs, que je vous conseille de très bien connaître pour comprendre cette partie.
Lorsqu'au début d'un bloc vous déclarez une variable, la calculatrice réserve la place qu'il lui faut dans la mémoire. Lorsque le bloc se termine, la mémoire est libérée pour un autre usage. L'allocation dynamique permet de gérer ces manipulations de mémoire.

Un peu de vocabulaire
-> Allocation: C'est lorsque la mémoire est réservée
-> Libération: C'est lorsque la mémoire est désallouée, l'espace mémoire est de nouveau libre pour une autre utilisation.

Comment ça fonctionne ?
L'allocation dite dynamique, que vous gérez vous-même, utilise des fonctions de la bilbiothèque standard et des pointeurs. Uniquement des pointeurs, jamais d'adresses, puisque l'adresse d'une variable pointe sur une variable que vous avez déjà déclarée, donc pour laquelle la mémoire a déjà été allouée.

Allouer de la mémoire à un pointeur: malloc()
Pour allouer de la mémoire à un pointeur, on utilise la fonction malloc(). Elle ne prend qu'un paramètre, la taille de la zone mémoire à allouer, en octets.
unsigned char *p;
p = malloc(20);

Le pointeur p pointe maintenant vers une zone mémoire qui lui a été allouée de 20 octets, si l'opération a réussi. Il faut toujours vérifier que l'allocation a fonctionné.
if(p==NULL)
{
  // l'allocation a echoue
}

Le pointeur NULL est un pointeur standard qui pointe dans le vide. Il ne faut surtout pas essayer d'écrire à l'adresse NULL, le système vous renverrait sûrement une System Error.

La fonction sizeof()
Mais il reste un problème. Imaginons que je veux allouer de l'espace pour 10 int. Sur certains systèmes un int fait 2, 4 voire même 8 octets. Impossible donc de savoir quelle taille de mémoire je dois allouer.
Heureusement, il y a sizeof() ! Cette fonction prend en argument un type de donnée et renvoie, en int, la taille en octets d'une variable d'un tel type.
Le code pour allouer dynamiquement la mémoire que je voulais est donc:
ptr = malloc(10*sizeof(int));

La fonction sizeof permet également de connaître la taille d'une structure ou d'une union par exemple, mais en aucun cas la longueur d'une chaîne de caractères.

Libérer la mémoire avec free()
La mémoire allouée à un pointeur n'est pas libérée pas le système à la fin du bloc, mais la variable pointeur, elle, l'est. On a donc un gros problème: à la sortie du bloc, la mémoire est toujours allouée, et on n'y a plus accès car on ne connaît pas l'adresse de la zone mémoire, le pointeur ayant été détruit.
C'est ce que l'on appelle une fuite de mémoire.
Il faut donc toujours libérer la mémoire allouée avant de perdre le pointeur.
int ptr;
ptr = malloc(10*sizeof(int));
// ...
free(ptr);

La fonction qui alloue le pointeur peut également le renvoyer (donc sans le libérer), ce qui est très utile lorsque l'on veut qu'une fonction crée et initialise une structure par exemple.

Les fonctions calloc() et realloc()
En bonus, deux autres fonctions d'allocation.

calloc() fonctionne de la même manière que malloc(). Mais en plus d'allouer la mémoire, elle initialise tous les éléments alloués à 0. Elle prend deux paramètres, le nombre d'éléments à allouer et la taille d'un élément.
int *ptr;
ptr = calloc(10,sizeof(int));

Vous diposez alors d'un tableau dont tous les éléments ont la valeur 0.

realloc(), comme son nom l'indique, permet de réallouer une zone de mémoire à un pointeur.
Elle prend deux paramètres: le pointeur déjà alloué et la nouvelle taille. Dans le cas où la réallocation succède, la fonction est susceptible d'allouer un bloc à un autre endroit de la mémoire, et retourne alors le nouveau pointeur.
En revanche, si la réallocation échoue, l'ancienne zone de mémoire n'est pas libérée, donc attention aux fuites de mémoire !




Fonctionnalités avancées[/big]

La bibliothèque standard contient également des fonctions très avancées et optimisées. Pour comprendre cette section, je vous conseille de bien connaîtres les pointeurs, notamment sur les fonctions.

La fonction qsort()
Cette superbe fonction trie un tableau à l'aide d'un algorithme de tri rapide. Seulement elle possède une spécificité digne d'intérêt. Puisqu'elle doit pouvoir trier autre chose que des entiers, c'est vous qui définissez si un élément est inférieur, égal ou supérieur à un autre. Il faut donc que vous définissiez votre propre fonction de comparaison. Allez, on va prendre un exemple, parce que c'est un peu compliqué.
Je veux trier un tableau d'entiers, de sorte que les nombres dont le chiffre des unités est le plus faible soient au début.
La fonction de tri est donc la suivante.
int compare(const void *v1, const void *v2)
{
        int i1 = (*(int *)v1) % 10;
        int i2 = (*(int *)v2) % 10;

        if(i1 < i2) return -1;
        if(i1 > i2) return 1;
        return 0;
}

Notez que la fonction doit prendre en paramètres deux pointeurs de type const void *. C'est donc à vous de veiller à les caster dans le type que vous manipulez. Cette opération ne constitue pas une violation de la règle d'aliasing stricte puisque le pointeur original est de type void.
Maintenant que nous avons notre fonction de comparaison, nous pouvons trier notre tableau !
void qsort (void* base, size_t num, size_t size,
            int (*compar)(const void*,const void*));

Le prototype peut sembler surprenant.
base est le pointeur sur votre tableau. num est le nombre d'éléments dans le tableau, et size est la taille d'un élément, que vous pouvez obtenir avec sizeof().
Enfin, vient le pointeur sur votre fonction de comparaison.
int entiers[10] = { 21, 87, 83742, 874, 732, 923, 93, 97, 2, 743 };
qsort((void *)entiers,10,sizeof(int),compare);

Et le résultat est le tableau suivant.
21, 83742, 732, 2, 923, 93, 743, 874, 87, 97


La fonction bsearch()
La fonction bsearch() permet de faire une recherche dans un tableau de manière très rapide.
Avant tout, il faut que votre tableau soit trié, de manière croissante pour que la recherche fonctionne. On va donc pouvoir se servir de qsort().
void* bsearch (const void* key, const void* base,
               size_t num, size_t size,
               int (*compar)(const void*,const void*));

Aucune surprise du point de vue du prototype. La clé recherchée est castée en const void *, donc n'oubliez pas de déclarer une variable.
base est toujours l'adresse du tableau, et num et size désignent toujours le nombre d'élément et leur taille. Enfin, toujours cette fonction de comparaison.
Voyons comment utiliser cette fonction.
int entiers[10] = { 21, 87, 83742, 874, 732, 923, 93, 97, 2, 743 };
int key = 874;
void *ptr;

qsort((void *)entiers, 10, sizeof(int), compare);
ptr = bsearch((const void *)&key, (const void *)entiers, 10, sizeof(int), compare);

On trie donc d'abord notre tableau avec qsort(), puis on en extrait l'adresse du premier élément 874 trouvé. L'adresse est assignée au pointeur ptr.
Donc, si on affiche *(int *)ptr (ce qui ne crée pas d'alias), on aura bien sûr 874.
Enfin, dernière astuce.
index = (int *)ptr-entiers;

Et vous voilà avec l'index de l'objet recherché dans votre tableau.


Si vous avez la moindre question, remarque, suggestion à faire, n'hésitez pas à laisser un commentaire !


Drac0300Hors ligneMembrePoints: 839 Défis: 39 Message

Citer : Posté le 27/04/2014 20:03 | #


Tu ne parles pas de la fonction realloc(). Est-ce qu'elle fonctionne sous casio ?

Ajouté le 27/04/2014 à 20:04 :
Sinon, super tuto qui illustre parfaitement les possibilités de la bibliothèque Standard !
Dans Z/1Z, 42==666
Coïncidence ? Je ne pense pas.
LephenixnoirHors ligneAdministrateurPoints: 15740 Défis: 136 Message

Citer : Posté le 27/04/2014 20:41 | #


La fonction realloc() existe bien sous CASIO ; j'y ajouterai un chapitre dès que j'en aurai l'occasion.
On verra aussi s'il y a d'autres choses intéressantes dans cette bibliothèque.
AlphacreatorHors ligneMembrePoints: 1464 Défis: 43 Message

Citer : Posté le 27/04/2014 20:48 | #


Je trouve qu'on ne voit pas tellement le fait que les fonctions sprintf et sscanf marchent avec des chaînes et pas des flux, un novice pourrais se tromper et vouloir utiliser sprintf pour afficher du texte et sscanf pour faire une saisie.
Après c'est mon point de vue mais je trouve qu'on ne voit pas assez cette différence.
Drac0300Hors ligneMembrePoints: 839 Défis: 39 Message

Citer : Posté le 27/04/2014 20:54 | #


D'ailleurs, ci je me souviens bien, il y a une fonction fprintf() qui gère des flux. Serait-il possible de l'utiliser pour l'écran de la casio ?
Dans Z/1Z, 42==666
Coïncidence ? Je ne pense pas.
AlphacreatorHors ligneMembrePoints: 1464 Défis: 43 Message

Citer : Posté le 27/04/2014 20:56 | #


C'est pour des fichiers donc je ne pense pas.
LephenixnoirHors ligneAdministrateurPoints: 15740 Défis: 136 Message

Citer : Posté le 27/04/2014 20:58 | #


Non, pas de fprintf ni de fscanf.
@Alphacreator: c'est noté, j'insisterai sur les chaînes de caractères.

Ajouté le 27/04/2014 à 21:21 :
Done.
J'ai insisté sur les chaînes de caractères avec sprintf() et sscanf(), et ajouté une section sur realloc() avec celle sur calloc().

Ajouté le 27/04/2014 à 22:22 :
Mise à jour.
J'ai ajouté une section sur les fonctionnalités avancées proposées par qsort() et bsearch().

Ajouté le 27/04/2014 à 22:37 :
Encore autre chose.
Les valeurs absolues et les divisions euclidiennes.
TenmatxHors ligneMembrePoints: 994 Défis: 2 Message

Citer : Posté le 28/04/2014 00:57 | #


Lephenixnoir tu n'as vraiment que ça à faire ?
Mes programmes de maths
Mes programmes de maths

Toutes les formules de Première S.
Toutes les formules de Terminale S.
Un programme de calculs.
Mes meilleurs jeux
Mes meilleurs jeux

Jeu gagnant des 48h CPC n°12

Mon site de discussion pour ados : http://entre-ados.net/
Mon éditeur de cours en ligne et plateforme de partage : http://wordline.xyz
LephenixnoirHors ligneAdministrateurPoints: 15740 Défis: 136 Message

Citer : Posté le 28/04/2014 08:05 | #


Tenmatx a écrit :
Lephenixnoir tu n'as vraiment que ça à faire ?

Jamais.

Mais c'est bien de changer un peu de temps en temps, alors autant en profiter pour rédiger quelque chose d'utile (donc pas sur le wiki manifestement ).
Et moi qui me désole de voir des manipulations de chaînes de caractères avec des boucles de copie caractère par caractère (au lieu de sprintf()), ou des lectures d'entiers dans des chaînes avec des algorithmes compliqués, là ou atoi() suffit, j'ai pensé que faire profiter tout le monde des merveilles de la bibliothèque standard était une idée digne d'intérêt.
Et puis les fonctions comme qsort() et bsearch() sont incroyablement puissantes, alors pourquoi ne jamais s'en servir ?
IntelligideHors ligneMembre de CreativeCalcPoints: 46 Défis: 5 Message

Citer : Posté le 28/04/2014 14:21 | #


int *ptr;
ptr = calloc(10*sizeof(int),sizeof(int));


tu n'as pas alloué 160 octets (donc 40 int)

si tu veux allouer 10 int(40 octets), je pense qu'il fait faire:
int *ptr;
ptr = calloc(10sizeof(int));


après , je peux me tromper
LephenixnoirHors ligneAdministrateurPoints: 15740 Défis: 136 Message

Citer : Posté le 28/04/2014 14:24 | #


Non, regarde bien il y a deux arguments puisque c'est calloc().
Le premier c'est la taille à allouer, 10*sizeof(int), et le deuxième c'est la taille d'un élément.
C'est pour que la fonction puisse les initialiser à 0 qu'elle a besoin de ce second paramètre.
IntelligideHors ligneMembre de CreativeCalcPoints: 46 Défis: 5 Message

Citer : Posté le 28/04/2014 14:32 | #


mince je me suis trompé, je voulais plutot
int *ptr;
ptr = calloc(10,sizeof(int));


d'après le sdz,
SDZ a écrit :
Voici son prototype :
void* calloc(size_t num_elements, size_t size);

Le premier argument est le nombre d'éléments qu'on souhaite pouvoir stocker en mémoire et le deuxième est la taille de ces éléments que l'on obtient avec l'opérateur sizeof().


donc pour 10 elements de type int(un tableau de 10 int),
il faut calloc(10,sizeof(int));
LephenixnoirHors ligneAdministrateurPoints: 15740 Défis: 136 Message

Citer : Posté le 28/04/2014 14:44 | #


Oups ^^'
Tiens, oui.
Je corrige ça dans l'instant.

Merci. C'est vrai que ça faisait des allocations énormes.
Dark stormEn ligneMembre d'honneurPoints: 10825 Défis: 176 Message

Citer : Posté le 28/04/2014 20:39 | #


Ajouté aux tutoriels de qualité
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
LephenixnoirHors ligneAdministrateurPoints: 15740 Défis: 136 Message

Citer : Posté le 28/04/2014 20:52 | #


Merci
Par contre ce serait bien si tu pouvais mettre un 'd' à "standard". ^^'
Dark stormEn ligneMembre d'honneurPoints: 10825 Défis: 176 Message

Citer : Posté le 28/04/2014 20:53 | #


Exact
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
GollumEn ligneMembrePoints: 1261 Défis: 2 Message

Citer : Posté le 28/04/2014 22:19 | #


et donc on peux utiliser sprintf comme les guillemets en basic(retour a la ligne ?)
https://telegram.me/BrokenClock
Je suis de l'autre coté de la manche maintenant. Yay.
Dark stormEn ligneMembre d'honneurPoints: 10825 Défis: 176 Message

Citer : Posté le 28/04/2014 22:33 | #


Comment ça ?

Ajouté le 28/04/2014 à 22:34 :
D'ailleur, lephenix, tu peux mettre des balises [ label=mon_label ] pour mettre un lien interne, et [ target=mon_label ] pour y renvoyer (dans le menu )
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Páranÿe quetë Quendya
LimachiHors ligneYoutuberPoints: 2798 Défis: 67 Message

Citer : Posté le 29/04/2014 01:47 | #


Gollum a écrit :
et donc on peux utiliser sprintf comme les guillemets en basic(retour a la ligne ?)

Lephenixnoir a écrit :
Ces deux fonctions ont exactement le même rôle que printf() et scanf(), mais ne vont pas lire/écrire via les flux d'entrée/sortie standard, mais dans des chaînes de caractères.
Il n'y a donc aucune sortie à l'écran ni aucune demande de saisie, tout se passe dans des variables.

je pense que Lephenixnoir a été clair, mais au cas où. Non, les guillemets en basic écrivent dans un flux virtuel qui n'est pas disponible en C, les flux sont un peux comme des sortes de listes sans réelles limites qui sont normalisée et partagées par plusieurs fonctions, par exemple un flux de caractères est commun a la console et les fonctions print en C, mais on doit passer par d'autres méthodes en C sur casio.
Mes Programmes
Cliquer pour enrouler
-en basic: un programme nommé PICFMLIM convertissant une picture en code basic.
-en C:
-Un pong.
-Un projet en pause. Je compte le reprendre de temps en temps: Summer Cursed


-mon tuto sur les Str


Mes calto: G25+, G75
Mon minecraft en dévelopement


Projets et Programmes que je soutiens (sur une idée de Marmotti)
Cliquer pour enrouler
-Pokemon Jade de Dodormeur
-Portal2D de JavierXD
-CalCraft de Wime
-GeekBros du groupe GeekBrothers (Eiyeron,Siapran,KevKevVTT,Adbook,LIMachi)
LephenixnoirHors ligneAdministrateurPoints: 15740 Défis: 136 Message

Citer : Posté le 29/04/2014 10:53 | #


Merci Dark Storm, c'est ajouté.

@Gollum, si tu fais un sprintf() vide, rien ne se passera, puisque la fonction n'écrira rien dans ta variable.

Ajouté le 29/04/2014 à 13:10 :
En fait, les flux d'entrée/sortie standard existent avec le C Casio: stdin, stdout et stderr.
Seulement on les manipule habituellement avec les fonctions fprintf()/fscanf(), fgetc()/fgets(), fputc()/fputs() etc... qui ne sont pas supportées.
En revanche, le type FILE lui, l'est.

Peut-être que la documentation donne des indications sur l'utilisation de ces flux ?

Ajouté le 29/04/2014 à 13:15 :
Rien dans la doc de CASIO.
Le dernier fichier est le mode d'emploi de Renesas.
900 pages... je lirai tout ça un jour, on pourrait en apprendre pas mal sur le fonctionnement de nos chères calculatrices.

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2019 | Il y a 151 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd