Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.
Tous | Tutoriels du Mercredi | Basic Casio | C/C++/ASM | LuaFX | Graphisme | Transferts | Logiciels | Diverses astuces

Calculatrice
Toutes
Graph 35 à 100
Graph 25+Pro/25+E/25+E II
Graph 35+USB/75(+E)/85/95 SD
Graph 100(+)
Classpad 300/330(+)
fx-CG 10/20 (Prizm)
Classpad 400(+E)
Graph 90+E
fx-92+ SC

Retour à la liste des tutoriels
Tutoriel Casio : La librairie standard
Tutoriel rédigé le : 2014-04-28 20:40  par Lephenixnoir  Catégorie : C/C++/ASM  Calculatrice : Graph 35+USB/75(+E)/85/95 SD

Discutez de ce tutoriel sur le forum >> Voir le sujet dédié (19 commentaires)

La librairie standard
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 !


Discutez de ce tutoriel sur le forum >> Voir le sujet dédié (19 commentaires)

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 74 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