Partage

Exercices pour débutants en C

Au menu : zSommeChiffres (nombres, algo)

28 février 2010 à 10:58:19

Citation : candide

#include <stdio.h>
#include <string.h>
#define SZ_MAX  1000
void additionner(char *p, char *q, char *s)
{
  size_t np = strlen(p), nq = strlen(q), i;
  int retenue = 0;

  for (i = 0; i < np || i < nq; i++)
    {
      int x =
        retenue + (i < np ? p[i] : '0') - '0' + (i < nq ? q[i] : '0') - '0';
      s[i] = "0123456789"[x - (retenue = (x > 9)) * 10];
    }
  /* Gestion de la retenue finale et fermeture de la chaine */
  s[i] = retenue ? s[i + 1] = 0, '1' : 0;
}

int main(void)
{
  char a[SZ_MAX] = "1";
  char r[SZ_MAX] = "0";
  char *p = a, *q = r;
  int i, n = 2010;

  for (i = 0; i < n; i++)
    {
      additionner(p, p, q);
      p = q;
      q = a;
    }
  n = strlen(p);
  for (i = n - 1; i >= 0; i--)
    printf("%c", p[i]);
  printf("\n");
  return 0;
}




J'aurais une petite question sur le code de cette fonction :
1) A quoi sert le '1' après l'opérateur virgule et avant l'opérateur deux points de la ternaire ?
s[i] = retenue ? s[i + 1] = 0, '1' : 0;
28 février 2010 à 11:14:58

Quand tu mets une virgule ça veut dire grossomodo que tu exécutes tes instructions les unes à la suite des autres.
Par exemple tu fais a = 0, b = 3, c = 5; ces instructions seront exécutées de gauche à droite (en suivant la virgule).

Donc finalement dans son code, dès que tu as une retenue, tu mets i+1 à '\0' (fin de chaîne) ensuite, toujours dans la même instruction de par le fait que tu as une virgule, tu mets '1' à l'indice i, sinon si ta condition est fausse tu termines la chaîne avec '\0'.
28 février 2010 à 11:22:51

Oulà, je suis vraiment stupide des fois. Je savais que l'opérateur virgule permettait d'enchainer les instructions mais là, je n'avais vraiment pas vu que le '1' se rapportait à la valeur de s[i].
1 mars 2010 à 16:05:38

Déjà la correction ! j'ai a peine fini l'addition ! :(
il m'aurait fallu au moins 15 jours de plus pour le reste et encore suis pas sur d'y être arrive jusqu'à la division! :lol:
cet exo pour moi a été mega dur !

merci pour la correction, j'ai au moins une piste pour y reflechir à comment proceder pour la multiplication et la division :-°
2 mars 2010 à 16:23:01

Titre : zLCD
Mois : Premier exercice de Mars 2010
Sujet : Simulation d'un afficheur 7 segments.
Connaissances requises : Partie 2 : [Théorie] Techniques avancées(uniquement tableaux et chaînes de caractères).

Objectif


Ecrire la fonction
void zLCD(unsigned int n);
qui affiche la valeur d'un entier positif passé en paramètre en simulant un afficheur 7 segments

2 formes possibles:
1ère forme:
Un chiffre aura une largeur de 3 colonnes, et une hauteur de 5 lignes. Les chiffres seront séparés par une colonne d'espace.

Exemple:

int main(void)
{
    zLCD(1234567890);

    return 0;
}


en sortie
*
     -   -       -   -   -   -   -   -
  |   |   | | | |   |     | | | | | | |
     -   -   -   -   -       -   -
  | |     |   |   | | |   | | |   | | |
     -   -       -   -       -   -   -

Process returned 0 (0x0)   execution time : 0.031 s
Press any key to continue.

Le caractère '*' est présent seulement pour pouvoir un affichage correct avec le zCode. :-°


2ème forme :
Un chiffre aura une largeur de 3 colonnes, et une hauteur de 3 lignes. Les chiffres seront séparés par une colonne d'espace.
L'underscore remplace le tiret.

int main(void)
{
    zLCD(1234567890);

    return 0;
}

*
     _   _       _   _   _   _   _   _
  |  _|  _| |_| |_  |_    | |_| |_| | |
  | |_   _|   |  _| |_|   | |_|  _| |_|

Process returned 0 (0x0)   execution time : 0.016 s
Press any key to continue.

Pour les plus avancés

L'article de wikipédia, montre 2 manières efficaces de représenter ses chiffres en mémoire.
En vous en inspirant, vous pouvez coder la fonction zLCD et par la même occasion vous entrainer à manipuler les opérateurs bitwise. ;)

Bon courage.

J'ai ouvert un topic en rapport avec cet exercice ici.
Zeste de Savoir, le site qui en a dans le citron !
2 mars 2010 à 16:36:42

Très intéressant :)
Dès que j'ai une demi-heure je m'y attèle =D
2 mars 2010 à 18:17:26

C'est vrai qu'il est ludique.
Tu fais un sujet pour qu'on puisse poster nos réponses où ici ça ira ?
Dans la partie "pour les plus avancés", il faut en fait se servir des opérateurs bitwise c'est ça ?
Anonyme
2 mars 2010 à 20:36:43

Celui là je vais tenter de le faire :)
2 mars 2010 à 22:02:39

Bonsoir,
Pour l'exo precedent dans la fonction additionner, quelqu'un peut m'expliquer dans
la boucle for, je ne comprend pas l'instruction:
i <= szC || cr
Merci
Anonyme
2 mars 2010 à 22:04:44

Citation : ero-sennin95

Bonsoir,
Pour l'exo precedent dans la fonction additionner, quelqu'un peut m'expliquer dans
la boucle for, je ne comprend pas l'instruction:
i <= szC || cr
Merci



Si i est inférieur ou égale à szC ou cr.
2 mars 2010 à 22:21:34

Merci pour ta réponse rapide.
Ok c'est ce que j'avais "pas" compris...
par exemple si szC=13 et cr = 1( ou 0)
cela signifie i inférieur ou égal a 13 OU 1??? quel est le but de cette boucle serait plutôt ma question? comment évaluer 13 OU 1?
c'est bon g pigé merci!
3 mars 2010 à 4:09:19

Navré, j'ai oublié d'ouvrir un sujet pour les réponses. :honte:
Je répare tout de suite.

J'édite également l'énoncé, suite à une remarque judicieuse de candide sur la mise en forme des chiffres. Pensez à regarder, à l'occasion. ;)
Zeste de Savoir, le site qui en a dans le citron !
3 mars 2010 à 15:33:20

Une petite question, l'afficheur émuler est à cathodes communes ou à anodes communes où cela n'a pas d'importance et on n'utilise celui que l'on veut. (Je pose la question car les tables de vérités changent grandement en fonction du cas)
3 mars 2010 à 16:58:31

Citation : Lithrein

Une petite question, l'afficheur émuler est à cathodes communes ou à anodes communes où cela n'a pas d'importance et on n'utilise celui que l'on veut. (Je pose la question car les tables de vérités changent grandement en fonction du cas)


o_O
Je ne m'étais pas posé la question.
C'est surtout l'affichage qui importe. Après pour les tables de vérités c'est votre cuisine personnelle.
Zeste de Savoir, le site qui en a dans le citron !
13 mars 2010 à 12:17:51

zLCD: Correction



soit une fonction qui affiche ceci
*
     _   _       _   _   _   _   _   _
  |  _|  _| |_| |_  |_    | |_| |_| | |
  | |_   _|   |  _| |_|   | |_|  _| |_|

avec la valeur 1234567890 en paramètre.

En console standard, on ne peux pas placer un caractère ou on le désire, il faut donc passer par un tableau temporaire.
C'est ce tableau qu'on remplira, et qu'on affichera ensuite dans la console.

Il y a avait plusieurs façon d'obtenir le résultat souhaité:
On pouvait:
  • Décomposer notre nombre par la droite, et remplir le tableau en commençant par la fin
  • Convertir notre nombre en chaîne de caractère, pour ensuite le parcourir facilement.


On va voir les 2 solutions.

1. En décomposant le nombre par la droite.
  1. On commence par calculer le nombre de caractères de notre nombre .
  2. On récupère le chiffre à droite de notre nombre.
  3. On divise notre nombre par 10.
  4. On rempli le tableau(par la fin) avec la représentation LCD de notre chiffre.
  5. On répète les opérations à partir de 2 tant que le nombre est supérieur ou égal à 0.puce>
  6. On affiche le tableau temporaire.


Pour le chiffre tout à droite du nombre, on utilise l'opérateur %.

Ce qui traduit en C, nous donne
#include <stdio.h>

#define N_LIN   3
/* 3 colonnes + 1 espace */
#define N_COL   4

/* Taille standard de la console */
#define BUF_W   80


/* Retourne le nombre de chiffres de n */
int nbChiffres ( int n )
{
    char tmp[32];
    /* Les fonctions de la famille printf retourne le nombre de caractère
     * imprimés */
    return sprintf (tmp, "%d", n );
}


/* Affiche le contenu du buffer dans la console */
void afficherBuf ( char scr [][BUF_W] )
{
    int lin;
    for ( lin = 0; lin < N_LIN; lin++ )
        puts ( scr[lin] );
}



void remplirBuf ( char buf [][BUF_W], int pos, int n)
{
    static char const *s_digits[] =
    {
        " _       _   _       _   _   _   _   _  ",
        "| |   |  _|  _| |_| |_  |_    | |_| |_| ",
        "|_|   | |_   _|   |  _| |_|   | |_|  _| "
    };

    int lin, col;
    for ( lin = 0; lin < N_LIN; lin++ )
        for ( col = 0; col < N_COL; col++ )
            buf[lin][N_COL * pos + col] = s_digits[lin][col + n * N_COL];
}


void zLCD ( int n )
{
    /* Le buffer est rempli de 0 */
    char buf[N_LIN][BUF_W] = {{0}};
    /* On récupère le nombre de chiffres de n */
    int nDigits = nbChiffres(n);
    /* Position du chiffre courant */
    int pos;
    /* On rempli le buffer en commencant par la fin */
    for(pos = nDigits - 1; pos >= 0; pos--)
    {
        /* On décompose notre nombre par la droite */
        remplirBuf ( buf, pos, n % 10);
        /* On contenu avec le résultat de la division */
        n /= 10;
    }

    afficherBuf ( buf );
}



int main ( void )
{
    zLCD(1234567890);

    return 0;
}


2. En travaillant sur une chaîne de caractères

C'est beaucoup plus simple de cette manière.
Un fois notre nombre sous forme de chaîne, il nous suffit de le parcourir caractère par caractère jusqu'au '\0' final.

Le code, change très peu.
#include <stdio.h>

#define N_LIN   3
/* 3 colonnes + 1 espace */
#define N_COL   4

/* Taille standard de la console */
#define BUF_W   80

/* Affiche le contenu du buffer dans la console */
void afficherBuf ( char scr [][BUF_W] )
{
    int lin;
    for ( lin = 0; lin < N_LIN; lin++ )
        puts ( scr[lin] );
}



void remplirBuf ( char buf [][BUF_W], int pos, int n)
{
    static char const *s_digits[] =
    {
        " _       _   _       _   _   _   _   _  ",
        "| |   |  _|  _| |_| |_  |_    | |_| |_| ",
        "|_|   | |_   _|   |  _| |_|   | |_|  _| "
    };

    int lin, col;
    for ( lin = 0; lin < N_LIN; lin++ )
        for ( col = 0; col < N_COL; col++ )
            buf[lin][N_COL * pos + col] = s_digits[lin][col + n * N_COL];
}


void zLCD ( int n )
{
    char buf[N_LIN][BUF_W] = {{0}};
    char s_n[10];
    int i;

    sprintf(s_n, "%d", n);

    for(i = 0; s_n[i] != '\0'; i++)
        remplirBuf ( buf, i, s_n[i] - '0');

    afficherBuf ( buf );
}



int main ( void )
{
    zLCD(1234567890);

    return 0;
}



Pour les plus avancés

Même chose en utilisant les opérateur bitwise(operateurs bit à bit).

On voit que pour représenter nos segments, on utilise une matrine de [N_LIN][(N_COL + 1 espace) * 10 ].
C'est plus que nécessaire.

On a unt total de 7 segments. En stockant l'état de chaque segment pour sur un bit, on tient dans un octet(8 bits).

Représentation des segments
- 0 -
|     |
1     3
|     | 
 - 2 - 
|     |
4     5
|     | 
 - 6 -

Segment décimal hexadécimal binaire
0 1 0x1 00000001
1 2 0x2 00000010
2 4 0x4 00000100
3 8 0x8 00001000
4 16 0x10 00010000
5 32 0x20 00100000
6 64 0x40 01000000


Nos chiffres sont uniquement un ensemble de segments activé ou non.
Chiffres Segments
0 S0 | S1 | S3 | S4 | S5 | S6
1 S3 | S6
2 S0 | S2 | S3 | S4 | S5
3 S0 | S2 | S3 | S5 | S6
4 S1 | S2 | S3 | S6
5 S0 | S1 | S2 | S5 | S6
6 S0 | S1 | S2 | S4 | S5 | S6
5 S0 | S1 | S3 | S6
6 S0 | S1 | S2 | S3 | S4 | S5 | S6
7 S0 | S1 | S2 | S3 | S5 | S6


Pour activer un bit dans un octet

On utilise l'opérateur | (ou), qui a pour table de vérité
a b a | b
0 0 0
0 1 1
1 0 1
1 1 1

Ainsi, le chiffre 0 à pour représentation S0 | S1 | S3 | S4 | S5 | S6, soit 1111011
bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
1 1 1 1 0 1 1


Pour tester l'état d'un bit, on utilise l'opérateur & (et) qui a pour table de vérité:
a b a & b
0 0 0
0 1 0
1 0 0
1 1 1


Comment connaitre l'état d'un segment dans un chiffre donné ?
Par exemple le chiffre 0.
Soit dgt0 la représentation sur un octet du chiffre 0
S0 | S1 | S3 | S4 | S5 | S6
On souhaite par exemple tester l'état du 6ème segment.

On fait dgt0 & S6, et on à l'état du segment pour ce chiffre.

Ensuite, pour chaque segment, on sait si on doit afficher un | ou un _, et sa position.
C'est plié. :lol:

C'est exactement le principe du code suivant
#include <stdio.h>

#define N_LIN   3
/* 3 colonnes + 1 espace */
#define N_COL   4

/* Taille standard de la console */
#define BUF_W   80

/* Masque pour chaque segment */
#define S0 0x0001
#define S1 0x0002
#define S2 0x0004
#define S3 0x0008
#define S4 0x0010
#define S5 0x0020
#define S6 0x0040

void remplirBuf(char buf [][BUF_W], int pos, int d)
{
    pos *= NB_COL;

    /* Première ligne */
    buf[0][pos + 0] = ' ';
    buf[0][pos + 1] = d & S0 ? '_' : ' ';
    buf[0][pos + 2] = ' ' ;
    buf[0][pos + 3] = ' ' ;

    /* lignes suivantes */
    buf[1][pos + 0] = d & S1 ? '|' : ' ';
    buf[1][pos + 1] = d & S2 ? '_' : ' ';
    buf[1][pos + 2] = d & S3 ? '|' : ' ';
    buf[1][pos + 3] = ' ';
    buf[2][pos + 0] = d & S4 ? '|' : ' ';
    buf[2][pos + 1] = d & S5 ? '_' : ' ';
    buf[2][pos + 2] = d & S6 ? '|' : ' ';
    buf[2][pos + 3] = ' ';

}

void afficherBuf ( char scr [][BUF_W] )
{
    int lin;
    for ( lin = 0; lin < BUF_H; lin++ )
        puts ( scr[lin] );
}

void zLCD(unsigned n)
{
    /* Le buffer est rempli de 0 */
    char buf[BUF_H][BUF_W] = {{0}};

    /* Segments allumés pour chaque chiffre */
    unsigned char dgt[10] =
    {
        S0 | S1 | S3 | S4 | S5 | S6,
        S3 | S6,
        S0 | S2 | S3 | S4 | S5,
        S0 | S2 | S3 | S5 | S6,
        S1 | S2 | S3 | S6,
        S0 | S1 | S2 | S5 | S6,
        S0 | S1 | S2 | S4 | S5 | S6,
        S0 | S1 | S3 | S6,
        S0 | S1 | S2 | S3 | S4 | S5 | S6,
        S0 | S1 | S2 | S3 | S5 | S6
    };

    char s_tmp[11];
    int i;

    sprintf(s_tmp, "%d", n);
    for (i = 0; s_tmp[i] != '\0'; i++)
    {
        unsigned char d = dgt[s_tmp[i] - '0'];
        remplirBuf(buf, i, d);
    }
    afficherBuf(buf);
}


int main ( void )
{
    zLCD(1234567890);

    return 0;
}



En prime, une petite variante pour la fonction remplirBuf, qui utilise les décalages plutôt que les masques pour tester l'état d'un bit.
void remplirBuf(char buf [][BUF_W], int pos, int d)
{
    int i;

    pos *= NB_COL;

    /* Première ligne */
    buf[0][pos + 0] = ' ';
    buf[0][pos + 1] = d & 1 ? '_' : ' ';
    buf[0][pos + 2] = ' ' ;
    buf[0][pos + 3] = ' ' ;

    /* lignes suivantes */
    for (i = 0; i < BUF_H - 1 ; i++)
    {
        buf[i + 1][pos + 0] = d >> ((3 * i) + 1) & 1 ? '|' : ' ';
        buf[i + 1][pos + 1] = d >> ((3 * i) + 2) & 1 ? '_' : ' ';
        buf[i + 1][pos + 2] = d >> ((3 * i) + 3) & 1 ? '|' : ' ';
        buf[i + 1][pos + 3] = ' ';
    }
}


Il y a eu de très bonnes solutions ici, en complément, n'hésitez pas à les regarder.

Egalement un topic traitant du sujet Affichage d'un entier au format SSD
Pour plus d'infos sur les opérateurs bits à bits, vous pouvez regarder ce tuto.

Merci à ceux qui ont posté un code pour cet exercice. :)

Le prochain, sera un jeu! :)
Zeste de Savoir, le site qui en a dans le citron !
13 mars 2010 à 17:26:36

J'aurais une petite question : si on a une idée d'exercice, on t'envoie l'idée ou bien on la propose ici ?
Au passage je te remercie pour la correction qui m'a permis de voir une méthode à laquelle je n'avais pas songer.
13 mars 2010 à 17:30:12

Tu peux me MP... Au moins pour qu'on évalue la difficulté.

Soit
  • Tu as un exercice clés en main, tu postes un énoncé et la correction, 15j après.
  • Ce n'est qu'une idée, et on peut en discuter, pour la mettre en forme.


On peut même être plusieurs à discuter. ;)
A toi de voir. :)

Toutes les idées sont bienvenues. :)
Zeste de Savoir, le site qui en a dans le citron !
13 mars 2010 à 19:19:56

zCoupure



Cet exercice est le second du mois de mars, et vous permettra de jouer au banquier.
Vous êtes un agent et aujourd'hui vous recevez votre client. Celui-ci vous demande de lui livrer une somme avec la coupure qu'il vous a indiquée. Par exemple, il vous dit qu'il souhaite récupérer 300 000 € uniquement en billets de 500€ et de 200€. Dans ce cas vous lui donnerez le plus de billets de 500€ possible puis vous continuerez avec des billets de 200€.

Niveau 1



Compétences minimales requises : Partie 1 : [Théorie] Les bases du débutants.

Pour ce niveau la coupure sera la suivante :
  • Des billets de 100€.
  • Des billets de 50€.
  • Des billets de 20€.
  • Des billets de 10€.
  • Des pièces de 2€.
  • Des pièces de 1€.


Votre client vous indique la somme qu'il souhaite et vous la lui fournissez en tenant compte des règles d'accords (un 's' au pluriel et rien au singulier) et de la coupure spécifique au niveau 1.

Quelle somme voulez-vous ? 285
2 billets de 100.
1 billet de 50.
1 billet de 20.
1 billet de 10.
2 pièces de 2.
1 pièce de 1.


Exemple de prototype pour la fonction : void zCoupure (int somme);

Niveau 2



Compétences minimales requises : Partie 2 : [Théorie] Techniques avancées.

Cette fois ci, le client peut choisir la coupure (la pièce de 1€ est par contre obligatoire car pour rendre 301€ avec uniquement des billets de 100€ c'est assez difficile) qu'il veut, après tout le client est roi et vous, à son service :-° .

Premièrement vous demanderez à votre client le nombre de billets différents qu'il souhaite. Puis vous lui demanderez de vous indiquer lesquels.
De la même manière que dans le niveau 1, vous lui donnerez la somme de son choix dans la coupure qu'il vous aura indiquée en donnant le plus de gros billets possible.

Il est garantie que les billets représentent une somme d'argent supérieure à n'importe quelle pièce.

Combien de types de billets ? 4
Combien de types de pièces ? 2
Les billets : 100 50 20 10
Les pièces : 2 1
Quelle somme voulez-vous ? 285
2 billets de 100.
1 billet de 50.
1 billet de 20.
1 billet de 10.
2 pièces de 2.
1 pièce de 1.


Le prototype de la fonction reste inchangé : void zCoupure (int somme);

Niveau 3 (avancé)



Compétences minimales requises : [Allocation dynamique] [Chaînes de caractères]

Vous vous dites que jouer au banquier est vraiment trop simple pour vous. Le programme que vous avez fait en 5 minutes, vous fait la coupure en un clin d'œil et vous, vous êtes tranquille.
Sauf que vous n'aviez pas tout prévu ...
Eh oui, vous êtes devenu célèbre, on vous appelle même "L'homme qui coupe (la monnaie) plus vite que son ombre".
C'est pourquoi aujourd'hui vous avez rendez-vous avec l'homme le plus riche de tout l'univers (environ un untrigintillion d'euros (soit 10^186€) de fortune personnelle) :
À l'heure de votre rendez-vous, il vous demande gentiment :
"J'aurais besoin que vous me fassiez la monnaie de mon compte en petite coupure de préférence"
"Pourriez vous préciser les billets que vous voulez, s'il vous plaît ?"
"Des billets de 200€ et des pièces de 1€. Merci"
Là vous vous dites : "Ça va pas être possible mon super programme ne marche qu'avec des petits entiers"
Vous lui expliquer donc : "Pourriez vous revenir demain, le temps que je révise le programme qui fait la coupure."

En clair vous devez gérer les grosses masses monétaires (un untrigillon ne doit pas vous faire peur) et permettre au client de choisir sa coupure comme dans le niveau 2.

Exemple de prototype : void zCoupure (char * somme);

Bonne chance.

Vous pouvez poster vos réponses ou demander des conseils ici.
17 mars 2010 à 0:52:38

Citation : Lithrein

zCoupure



Pas du tout convaincu par cet exo verbeux, aux contours peu clairs, aux questions redondantes et qui une fois de plus fait beaucoup plus appel à de l'algo (certes de base) que de privilégier l'apprentissage du langage.

17 mars 2010 à 16:07:35

@Candide : Est-ce que tu pourrais me préciser les passages que tu ne trouve pas clairs ou bien si ce sont les limites de l'exercice en elles-même qui sont mal définies de manière à ce que j'éclaircisse les passages obscures ou précise les limites.
Étant donné que les question vise à améliorer ce qui à était fait précédemment elle sont par conséquent redondantes (j'en convient).
L'algorithme à la base de l'exercice ne me semblait pas difficile et assez basique. Tant utilisant pas mal de notion du langage (à partir du niveau 2) : Allocation dynamique, pointeurs, fonctions, tableau à une et à deux dimensions (pointeur sur chaînes), et possibilité d'aborder le problème de manière récursive.
Pour finir, tu souligne que la petite histoire était de trop, j'en conclus que ce n'était pas une bonne idée de ce fait si il y a une prochaine fois je n'en mettrait pas.
18 mars 2010 à 0:22:22

Citation : Lithrein

@Candide : Est-ce que tu pourrais me préciser les passages que tu ne trouve pas clairs



Non, je ne peux pas te le préciser car il faudrait reprendre tout l'exercice. Pour un exercice d'apprentissage et de consolidation de connaissances du C, je trouve que l'enrobage est trop complexe, pas assez intuitif, pas assez immédiat. Moi je sais que si j'étais débutant en C et si j'avais du mal à accrocher avec le langage C, ton exo me rebuterait.


Les questions (tes "niveaux") ne sont pas suffisamment différenciées, je vois pas vraiment où tu veux en venir. L'algorithme, tu le donnes toi-même en disant :


Citation


. Dans ce cas vous lui donnerez le plus de billets de 500€ possible puis vous continuerez avec des billets de 200€.



c'est un algorithme gourmand. Donc dans ton niveau 3, je vois pas où tu as besoin d'allocation dynamique ou alors c'est que tu veux gérer des grands entiers mais alors là vu le contexte c'est totalement artificiel, il faut implémenter une division de grands entiers par de petites valeurs, ce n'est pas dur en soi mais je doute que beaucoup te suivent.



Citation : Lithrein


L'algorithme à la base de l'exercice ne me semblait pas difficile et assez basique.




Ce n'est pas le problème. Il y a plein de choses simples et même très simples qu'on fait en maths par exemple et auxquelles tes interlocuteurs n'accrocheront pas parce que le contexte n'est pas assez familier ou immédiat.




Citation : Lithrein

Allocation dynamique, pointeurs, fonctions, tableau à une et à deux dimensions (pointeur sur chaînes), et possibilité d'aborder le problème de manière récursive.




Là tu parles C mais ton exo avec son énoncé compliqué cache tout ça.



Bon après c'est aussi une question de point de vue, ton exo peut très bien plaire à d'autres et à certains profils. Perso, je pense assez bien connaître la réaction du débutant en C moyen (parce que j'enseigne du C à des débutants et je vois bien qu'il faut simplifier au maximum les énoncés pour qu'ils arrivent à avancer en C).



18 mars 2010 à 21:08:14

Citation : candide

Citation : Lithrein

zCoupure



Pas du tout convaincu par cet exo verbeux, aux contours peu clairs, aux questions redondantes et qui une fois de plus fait beaucoup plus appel à de l'algo (certes de base) que de privilégier l'apprentissage du langage.

Que proposes tu ? Au moins quelqu un essaye de faire
de son mieux!
19 mars 2010 à 8:44:49

Citation : darkipod

Citation : candide

Citation : Lithrein

zCoupure



Pas du tout convaincu par cet exo verbeux, aux contours peu clairs, aux questions redondantes et qui une fois de plus fait beaucoup plus appel à de l'algo (certes de base) que de privilégier l'apprentissage du langage.

Que proposes tu ? Au moins quelqu un essaye de faire
de son mieux!


Pas la peine de l'incendier, candide donnait des conseils on ne peut plus judicieux.
19 mars 2010 à 9:35:03

Citation : Merkil

Citation : darkipod

Citation : candide

Citation : Lithrein

zCoupure



Pas du tout convaincu par cet exo verbeux, aux contours peu clairs, aux questions redondantes et qui une fois de plus fait beaucoup plus appel à de l'algo (certes de base) que de privilégier l'apprentissage du langage.

Que proposes tu ? Au moins quelqu un essaye de faire
de son mieux!


Pas la peine de l'incendier, candide donnait des conseils on ne peut plus judicieux.


c'est pas le but de l'incendier! Mon but c'est d'encourager ceux qui propose quelque chose. Je m'interroge seulement sur certaines pratiques qui consiste à dire :
oui bon ton truc c'est pas top, et d'en faire profiter à tout le monde...pour ensuite aboutir à des joutes oratoires. Cela peut se faire par message privé.
19 mars 2010 à 13:13:46

Au contraire, de cette façon, tous ceux qui avaient dans l'idée de confectionner un nouvel exercice pourront se poser les bonnes questions et peut-être corriger certaines choses :)
19 mars 2010 à 13:42:53

Citation : Merkil

Au contraire, de cette façon, tous ceux qui avaient dans l'idée de confectionner un nouvel exercice pourront se poser les bonnes questions et peut-être corriger certaines choses :)


Tout le monde ne réagit pas de la même façon aux critiques, même si elles sont justifiées.
Comme darkipod, je pense qu'un MP aurait été plus approprié.

Dans tous les cas, ça n'empèche pas l'exercice de Lithrein d'intéresser.

Je préfère largement, voir un exercice, même critiqué, que rien du tout, donc...
Et, il reste une semaine. A (vous)nous de montrer, qu'on est intéressé. ;)
Zeste de Savoir, le site qui en a dans le citron !
19 mars 2010 à 14:00:27

Citation : Merkil

Au contraire, de cette façon, tous ceux qui avaient dans l'idée de confectionner un nouvel exercice pourront se poser les bonnes questions et peut-être corriger certaines choses :)


je pense pour clore le débat (pour moi il est clos)
- les questions lesquelles ?
- ensuite il est facile de dire "tu aurais du faire comme ci ou comme ca" ..... c'est pas bon et de mauvais gout,
pour deux raisons au moins :
. la première : c'est démotivant pour celui qui propose de se voir REPROCHER que cela ne correspond pas a leur attentes.
D'ailleurs personne a part 3 ou 4 personnes se sont exprimés la dessus.......
. la deuxième : si on pense que c'est pas bon, on envoie un message en particulier sans faire profiter toute la galerie.....
et la effectivement on appelle cela de la formation corrective et pas négative comme pourrait ressentir celui qui propose
les exos et qui en plus prend de son temps libre pour regarder nos codes :pMERCI POUR EUX
20 mars 2010 à 22:58:54

Dite j'ai vraiment un problème : les cour de progrmation en C sont donner a partir de l'IDE Code::Block, or j'ai actuellement installer windows 7 donc impossible d'utiliser l'IDE.
Je me suis donc rabatut sur visual C++ 2008 Expresse, mais la encore je tape les instruction et je me retrouve avec de nombreuses erreur déclarer dans la zone de mort alors que je suit a la lettre les instructions(les erreur s'affichent même dans des instruction simplie comme afficher "bonjour"). J'ai aussi remarquer que pour cet IDE le retour a la ligne n'est pas "/n" mais "Console::WriteLine" et que pour écrire l'instruction par défault est "Console::WriteLine".
Donc HELP car je suis arriver a la fin du premier cour et impossible de tester mes programmes.
Merci d'avance.
20 mars 2010 à 23:07:47

Ce n'est pas le bon endroit.

Tu peux(dois) créer un nouveau sujet sur le Forum.
Ici, tu n'auras pas de réponses. ;)

Euh,

"Console::WriteLine", j'ai très peur.
Zeste de Savoir, le site qui en a dans le citron !
28 mars 2010 à 17:00:20

zCoupure : Correction



Niveau 1



On se rappelle que pour ce niveau les coupures à rendre étaient fournies par l'énoncé (100€, 50€, 20€ et 10 € pour les billets et 2€ et 1€ pour les pièces).

Je propose que l'on mette ces valeurs dans un tableau et que l'on précise via deux constantes ( #define ) le nombre de pièces et de billets.

#define NB_BILLETS 4
#define NB_PIECES  2

int coupure[] = {100, 50, 20, 10, 2, 1};


Ensuite pour obtenir les valeurs successives du nombre de billets de 100€, de 50€, ... je propose que l'on utilise la division euclidienne (le quotient donne le nombre de billet tandis que le reste donne la somme restante).

Citation : Exemple


Imaginons que l'utilisateur veuille 185€.

185 / 100 = 1 (reste = 85) <math>\(\Rightarrow\)</math> 1 billet de 100€.
85 / 50 = 1 (reste = 35) <math>\(\Rightarrow\)</math> 1 billet de 50€.
35 / 20 = 1 (reste = 15) <math>\(\Rightarrow\)</math> 1 billet de 20€.
15 / 10 = 1 (reste = 5) <math>\(\Rightarrow\)</math> 1 billet de 10€.
5 / 2 = 2 (reste = 1) <math>\(\Rightarrow\)</math> 1 pièces de 2€.
1 / 1 = 1 (reste = 0) <math>\(\Rightarrow\)</math> 1 pièce de 1€.



Or il se trouve que la bibliothèque standard propose deux types et deux fonctions adaptées à la division euclidienne.

/* quot = quotient ; rem = reminder (reste) */
typedef struct div_s {int quot, rem;} div_t;
div (int dividende, int diviseur);

/* long int peut être abrégé en long */
typedef struct ldiv_s {long int quot, rem;} ldiv_t;
ldiv (long int dividende, long int diviseur);


Il ne nous reste plus qu'à appliquer l'algorithme décrit dans l'exemple ci-dessus :
  • Tant qu'il y a des billets ou des pièces
    • nb <math>\(\leftarrow\)</math> quotient de la somme courante par la coupure courante)
    • billet <math>\(\leftarrow\)</math> C'est un billet ou une pièce ( 1 = billet et 0 = pièce)
    • pluriel <math>\(\leftarrow\)</math> Doit-on mettre un 's' à la fin de pièce ou de billet (1 = oui et 0 = non)
    • Si on doit donner de la coupure courante
      • On affiche ce que l'on doit rendre
    • Fin Si
    • somme <math>\(\leftarrow\)</math> reste de la division de la somme actuelle par la coupure actuelle
  • Fin Tant que


Ce qui traduit en C donne :
#define NB_BILLETS 4
#define NB_PIECES  2

void
zCoupure (int somme) {
    int coupure[] = {100, 50, 20, 10, 2, 1};
    int i = 0;

    for (i = 0 ; i < NB_PIECES+NB_BILLETS ; ++i) {
         /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupure[i]);
         /* C'est un billet ou une piece */
        int billet = (i >= NB_BILLETS) ? 0 : 1;
         /* Doit on mettre un 's' à la fin de billet ou de piece */
        int pluriel = (nb.quot > 1) ? 1 : 0;
        
        if (nb.quot > 0) {
            printf("%d %s%s de %d.\n", nb.quot,
                  billet ? "billet" : "piece", pluriel ? "s" : "" ,
                  coupure[i]);
        }
        
        somme = nb.rem;
    }
}


Pour ceux qui ont du mal avec les opérateurs ternaires.
#define NB_BILLETS 4
#define NB_PIECES  2

void
zCoupure (int somme) {
    int coupure[] = {100, 50, 20, 10, 2, 1};
    int i = 0;

    for (i = 0 ; i < NB_PIECES+NB_BILLETS ; ++i) {
         /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupure[i]);
         /* C'est un billet ou une piece */
        int billet = (i >= NB_BILLETS) ? 0 : 1;
        if (nb.quot > 1) { /* Doit on mettre un 's' à la fin de billet ou de piece */
            pluriel = 1;
        } else {
            pluriel = 0;
        }
        
        if (nb.quot > 0) {
            if (billet) {
                if (pluriel == 0) {
                    printf("%d billet de %d.\n", nb.quot, coupure[i]);
                } else {
                    printf("%d billets de %d.\n", nb.quot, coupure[i]);
                }
            } else {
                if (pluriel == 0) {
                    printf("%d piece de %d.\n", nb.quot, coupure[i]);
                } else {
                    printf("%d pieces de %d.\n", nb.quot, coupure[i]);
                }
            }
        }
        
        somme = nb.rem;
    }
}


Il existe aussi une implémentation récursive - en utilisant un accumulateur - (Tutoriel sur la récursivité de bluestrom) de cet algorithme mais personne ne l'a proposé.

La voici (le principe général est le même) :


#define NB_BILLETS 4
#define NB_PIECES  2

void _zCoupure (int somme, const int * coupure, size_t acc) {
    if (acc < NB_BILLETS+NB_PIECES) {
         /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupure[acc]);
         /* C'est un billet ou une piece */
        int billet = (acc >= NB_BILLETS) ? 0 : 1;
         /* Doit on mettre un 's' à la fin de billet ou de piece */
        int pluriel = (nb.quot > 1) ? 1 : 0;

        if (nb.quot > 0) {
            printf("%d %s%s de %d.\n", nb.quot,
                   billet ? "billet" : "piece", pluriel ? "s" : "" ,
                   coupure[acc]);
        }

        somme = nb.rem;

        _zCoupure(somme, coupure, acc+1);
    }
}

void
zCoupure (int somme) {
    const int coupure[] = {100, 50, 20, 10, 2, 1};
    _zCoupure(somme, coupure, 0);
}


Sans opérateurs ternaires :
#define NB_BILLETS 4
#define NB_PIECES  2

void _zCoupure (int somme, const int * coupure, size_t acc) {
    if (acc < NB_BILLETS+NB_PIECES) {
         /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupure[acc]);
         /* C'est un billet ou une piece */
        int billet = (acc >= NB_BILLETS) ? 0 : 1;
        if (nb.quot > 1) { /* Doit on mettre un 's' à la fin de billet ou de piece */
            pluriel = 1;
        } else {
            pluriel = 0;
        }
        
        if (nb.quot > 0) {
            if (billet) {
                if (pluriel == 0) {
                    printf("%d billet de %d.\n", nb.quot, coupure[i]);
                } else {
                    printf("%d billets de %d.\n", nb.quot, coupure[i]);
                }
            } else {
                if (pluriel == 0) {
                    printf("%d piece de %d.\n", nb.quot, coupure[i]);
                } else {
                    printf("%d pieces de %d.\n", nb.quot, coupure[i]);
                }
            }
        }
        somme = nb.rem;

        _zCoupure(somme, coupure, acc+1);
    }
}

void
zCoupure (int somme) {
    const int coupure[] = {100, 50, 20, 10, 2, 1};
    _zCoupure(somme, coupure, 0);
}


Le code comple du niveau 1 :

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

#define NB_BILLETS 4
#define NB_PIECES  2

void
clear_stdin () {
    int c;
    while ((c= getchar()) != '\n' && c != EOF);
}

void
zCoupure (int somme) {
    int coupure[] = {100, 50, 20, 10, 2, 1};
    int i = 0;

    for (i = 0 ; i < NB_PIECES+NB_BILLETS ; ++i) {
         /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupure[i]);
         /* C'est un billet ou une piece */
        int billet = (i >= NB_BILLETS) ? 0 : 1;
         /* Doit on mettre un 's' & la fin de billet ou de piece */
        int pluriel = (nb.quot > 1) ? 1 : 0;
        
        if (nb.quot > 0) {
            printf("%d %s%s de %d.\n", nb.quot,
                  billet ? "billet" : "piece", pluriel ? "s" : "" ,
                  coupure[i]);
        }
        
        somme = nb.rem;
    }
}

int
main (void) {
    int somme = 0;
    signed char ok = 0;

    while (!ok) {
        printf("Quelle somme d'argent voulez vous couper ? ");
        ok = scanf("%d", &somme);
        clear_stdin();
    }

    zCoupure(somme);

    return EXIT_SUCCESS;
}

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

#define NB_BILLETS 4
#define NB_PIECES  2

void
clear_stdin () {
    int c;
    while ((c= getchar()) != '\n' && c != EOF);
}

void _zCoupure (int somme, const int * coupure, size_t acc) {
    if (acc < NB_BILLETS+NB_PIECES) {
         /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupure[acc]);
         /* C'est un billet ou une piece */
        int billet = (acc >= NB_BILLETS) ? 0 : 1;
         /* Doit on mettre un 's' & la fin de billet ou de piece */
        int pluriel = (nb.quot > 1) ? 1 : 0;

        if (nb.quot > 0) {
            printf("%d %s%s de %d.\n", nb.quot,
                   billet ? "billet" : "piece", pluriel ? "s" : "" ,
                   coupure[acc]);
        }

        somme = nb.rem;

        _zCoupure(somme, coupure, acc+1);
    }
}

void
zCoupure (int somme) {
    const int coupure[] = {100, 50, 20, 10, 2, 1};
    _zCoupure(somme, coupure, 0);
}

int
main (void) {
    int somme = 0;
    signed char ok = 0;

    while (!ok) {
        printf("Quelle somme d'argent voulez vous couper ? ");
        ok = scanf("%d", &somme);
        clear_stdin();
    }

    zCoupure(somme);

    return EXIT_SUCCESS;
}



Niveau 2



Le client étant roi, cette fois ci, on doit le laisser choisir sa coupure.

Étant donné que l'on risque de demander beaucoup de nombre à l'utilisateur du programme, je vous propose de faire une fonction qui récupère un entier de manière sécurisée.
int
get_i (const char * msg) {
    int val;
    
    if (msg != NULL) {
        printf("%s ", msg);
    }
    while (scanf("%d", &val) != 1) {
        puts("Veuillez entrez un nombre entier");
        clear_stdin();
    }
    return val;
}


Cette fois ci, je propose que l'on décompose que l'on décompose le traitement des billets et des pièces (ce que l'on peut se permettre puisque leurs valeurs sont inférieures à celles des billets). De ce fait je propose le prototype suivant pour récupérer les coupures :
unsigned int zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type)
Dans le prototype `somme` représente la somme actuelle, `*coupures` le tableau de coupures, `nb_coupures` le nombre de coupures du tableau coupures et `type` indique si on travaille sur des pièces ou des billets.
Étant donné que l'algorithme est gourmand - on doit donner les gros billets d'abord - il faut donc trier les valeurs ressuent dans l'ordre décroissant.

Pour s'occuper du tri, je propose d'utiliser la fonction `qsort` de la bibliothèque standard qui a pour prototype :
void qsort (void * base, size_t n, size_t size,
            int (*cmp)(const void *, const void *);

Éclaircissons ce prototype un peu particulier:
`base` est le tableau a trié.
`n` est le nombre d'élément du tableau base.
`size` est la taille d'un élément du tableau (on l'obtient généralement grâce à l'opérateur sizeof)
et le dernier paramètre est un pointeur de fonction sur la fonction de comparaison (qsort est une fonction générique). Cette fonction doit prendre en paramètre des pointeurs génériques constants.
Nous, on veut faire un tri décroissant il nous faut donc une fonction qui renvoie un nombre positif si a<b, un nombre négatif si a>b et 0 si a=b.

La fonction est donc la suivante:
int
cmp (const void * a, const void * b) {
    const int pa = * (const int *) a;
    const int pb = * (const int *) b;

    if (pa > pb)
        return -1;
    else if (pb > pa)
        return 1;
    else
        return 0;
}

Il y aurait une autre possibilité pour la fonction de comparaison.
int
cmp (const void * a, const void * b) {
    const int pa = * (const int *) a;
    const int pb = * (const int *) b;

    return pb-pa;
}

Mais elle est fortement déconseillée car elle présente des risques d'overflow (INT_MAX-INT_MIN par exemple (INT_MIN est un nombre négatif très grand))


Au final, la fonction pour récupérer les coupures ressembles à quelque chose comme ça :
#define COLS_MAX 128

size_t
get_coupure (int ** coupures, const char * type) {
    size_t nb, i;
    char msg[COLS_MAX];

    sprintf(msg, "Combien de %ss differents voulez vous ?", type);
    nb = get_i(msg);

    *coupures = malloc(nb * sizeof **coupures);
    if (*coupures == NULL) {
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
    }

    for (i = 0 ; i < nb ; ++i)
        (*coupures)[i] = get_i(NULL);

    qsort(*coupures, nb, sizeof **coupures, cmp);
    
    return nb;
}


L'algorithme de traitement est sensiblement le même. Son implémentation en C donne :
unsigned int
zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type) {
    size_t i;
    
    for (i = 0 ; i < nb_coupures ; ++i) {
         /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupures[i]);
         /* Doit-on mettre la coupure au pluriel */
        int pluriel = (nb.quot > 1) ? 1 : 0;

        if (nb.quot > 0) {
            printf("%d %s%s de %d.\n", nb.quot, type,
                   pluriel ? "s" : "" , coupures[i]);
        }

        somme = nb.rem;
    }
    
    return somme;
}


Sans opérateurs ternaires :
unsigned int
zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type) {
    size_t i;
    
    for (i = 0 ; i < nb_coupures ; ++i) {
         /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupures[i]);

        if (nb.quot > 1) { /* Doit on mettre un 's' à la fin de billet ou de piece */
            pluriel = 1;
        } else {
            pluriel = 0;
        }
        
        if (nb.quot > 0) {
            if (pluriel == 0) {
                printf("%d %s de %d.\n", nb.quot, type, coupure[i]);
            } else {
                printf("%d %ss de %d.\n", nb.quot, type, coupure[i]);
            }
        }
        somme = nb.rem;
    }
    
    return somme;
}


unsigned int
zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type) {
    unsigned int _zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type, size_t acc);
    
    return _zCoupure(somme, coupures, nb_coupures, type, 0);
}

unsigned int
_zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type, size_t acc) {
    if (acc < nb_coupures) {
        /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupures[acc]);
         /* Doit-on mettre la coupure au pluriel */
        int pluriel = (nb.quot > 1) ? 1 : 0;

        if (nb.quot > 0) {
            printf("%d %s%s de %d.\n", nb.quot, type,
                  pluriel ? "s" : "" , coupures[acc]);
        }

        somme = nb.rem;

        return _zCoupure(somme, coupures, nb_coupures, type, acc+1);
    }
    
    return somme;
}


Sans opérateurs ternaires :
unsigned int
zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type) {
    unsigned int _zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type, size_t acc);
    
    return _zCoupure(somme, coupures, nb_coupures, type, 0);
}

unsigned int
_zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type, size_t acc) {
    if (acc < nb_coupures) {
        /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupures[acc]);

        if (nb.quot > 1) { /* Doit on mettre un 's' à la fin de billet ou de piece */
            pluriel = 1;
        } else {
            pluriel = 0;
        }
        
        if (nb.quot > 0) {
            if (pluriel == 0) {
                printf("%d %s de %d.\n", nb.quot, type, coupure[i]);
            } else {
                printf("%d %ss de %d.\n", nb.quot, type, coupure[i]);
            }
        }

        somme = nb.rem;

        return _zCoupure(somme, coupures, nb_coupures, type, acc+1);
    }
    
    return somme;
}


Le prototype utilisé a été expliqué un peu au dessus.

Le code complet du niveau 2 :

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

#define COLS_MAX 128

void
clear_stdin () {
    int c;
    while ((c =getchar()) != '\n' && c != EOF);
}

int
cmp (const void * a, const void * b) {
    const int pa = * (const int *) a;
    const int pb = * (const int *) b;

    if (pa > pb)
        return -1;
    else if (pb > pa)
        return 1;
    else
        return 0;
}

int
get_i (const char * msg) {
    int val;
    
    if (msg != NULL) {
        printf("%s ", msg);
    }
    while (scanf("%d", &val) != 1) {
        puts("Veuillez entrez un nombre entier");
        clear_stdin();
    }
    return val;
}

size_t
get_coupure (int ** coupures, const char * type) {
    size_t nb, i;
    char msg[COLS_MAX];

    sprintf(msg, "Combien de %ss differents voulez vous ?", type);
    nb = get_i(msg);

    *coupures = malloc(nb * sizeof **coupures);
    if (*coupures == NULL) {
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
    }

    for (i = 0 ; i < nb ; ++i)
        (*coupures)[i] = get_i(NULL);

    qsort(*coupures, nb, sizeof **coupures, cmp);
    
    return nb;
}

unsigned int
zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type) {
    size_t i;
    
    for (i = 0 ; i < nb_coupures ; ++i) {
         /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupures[i]);
         /* Doit-on mettre la coupure au pluriel */
        int pluriel = (nb.quot > 1) ? 1 : 0;

        if (nb.quot > 0) {
            printf("%d %s%s de %d.\n", nb.quot, type,
                   pluriel ? "s" : "" , coupures[i]);
        }

        somme = nb.rem;
    }
    
    return somme;
}

int
main (void) {
    unsigned int somme;
    int *billets = NULL, *pieces = NULL;
    size_t nb_billets, nb_pieces;

    somme = get_i("Quelle somme voulez vous coupez ?");

    nb_billets = get_coupure(&billets, "billet");
    nb_pieces = get_coupure(&pieces, "piece");

    somme = zCoupure(somme, billets, nb_billets, "billet");
    somme = zCoupure(somme, pieces, nb_pieces, "piece");

    free(billets), billets = NULL;
    free(pieces), pieces = NULL;
    
    return EXIT_SUCCESS;
}

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

#define COLS_MAX 128

void
clear_stdin () {
    int c;
    while ((c =getchar()) != '\n' && c != EOF);
}

int
get_i (const char * msg) {
    int val;
    
    if (msg != NULL) {
        printf("%s ", msg);
    }
    while (scanf("%d", &val) != 1) {
        puts("Veuillez entrez un nombre entier");
        clear_stdin();
    }
    return val;
}

size_t
get_coupure (int ** coupures, const char * type) {
    size_t nb, i;
    char msg[COLS_MAX];

    int cmp (const void * a, const void * b);

    sprintf(msg, "Combien de %ss differents voulez vous ?", type);
    nb = get_i(msg);

    *coupures = malloc(nb * sizeof **coupures);
    if (*coupures == NULL) {
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
    }

    for (i = 0 ; i < nb ; ++i)
        (*coupures)[i] = get_i(NULL);

    qsort(*coupures, nb, sizeof **coupures, cmp);
    
    return nb;
}

unsigned int
zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type) {
    unsigned int _zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type, size_t acc);
    
    return _zCoupure(somme, coupures, nb_coupures, type, 0);
}

int
main (void) {
    unsigned int somme;
    int *billets = NULL, *pieces = NULL;
    size_t nb_billets, nb_pieces;

    somme = get_i("Quelle somme voulez vous coupez ?");

    nb_billets = get_coupure(&billets, "billet");
    nb_pieces = get_coupure(&pieces, "piece");

    somme = zCoupure(somme, billets, nb_billets, "billet");
    somme = zCoupure(somme, pieces, nb_pieces, "piece");

    free(billets), billets = NULL;
    free(pieces), pieces = NULL;
    
    return EXIT_SUCCESS;
}

int
cmp (const void * a, const void * b) {
    const int pa = * (const int *) a;
    const int pb = * (const int *) b;

    if (pa > pb)
        return -1;
    else if (pb > pa)
        return 1;
    else
        return 0;
}

unsigned int
_zCoupure (unsigned int somme, int * coupures, size_t nb_coupures, const char * type, size_t acc) {
    if (acc < nb_coupures) {
        /* On calcule le nombre de billets ou de pieces a donner */
        div_t nb = div(somme, coupures[acc]);
         /* Doit-on mettre la coupure au pluriel */
        int pluriel = (nb.quot > 1) ? 1 : 0;

        if (nb.quot > 0) {
            printf("%d %s%s de %d.\n", nb.quot, type,
                  pluriel ? "s" : "" , coupures[acc]);
        }

        somme = nb.rem;

        return _zCoupure(somme, coupures, nb_coupures, type, acc+1);
    }
    
    return somme;
}



Niveau 3



Cette fois-ci, on doit pouvoir travailler sur les grands entiers. Je vous propose donc d'utiliser d'utiliser les fonctions de la correction de l'exercice zBigInt (légèrement modifiées).

typedef struct sdiv_s { char *quot, *rem; } sdiv_t;

/**
 * \brief Compare deux entiers : Tri croissant
 *
 * @param a : const void *
 * @param b : const void *
 * @return -1 (a<b), 1 (b>a) autrement 0
 */
int
cmp_cre (const char * a, const char * b) {
    size_t sz1;
    size_t sz2;

    char * skip_zeros(const char *);

    sz1 = strlen(skip_zeros(a));
    sz2 = strlen(skip_zeros(b));

    if (sz1 > sz2)
        return 1;
    else if (sz1 < sz2)
        return -1;
    else
        return strcmp(skip_zeros(a), skip_zeros(b));
}

void rm_zeros_fin(char *s)
{
    size_t n = strlen(s);
    size_t i;

    /* On supprime les zéros inutiles */
    for (i = n - 1; i > 0 && s[i] == '0'; i--)
        s[i] = '\0';
}

void
soustraction(char *a, char *b, char *c)
{
    void reverse(char *);
    char * skip_zeros(const char *);
    
    if (cmp_cre(a, b) < 0)
    {
        /* |a| < |b| -> -(b - a) */
        c[0] = '-';
        soustraction(b, a, c + 1);
    }
    else
    {
        /* Nombre de chiffres de a */
        int szA = strlen (a);
        /* Nombre de chiffres de b */
        int szB = strlen (b);
        /* Retenue */
        int cr = 0;

        int i;
        for ( i = 0; i < szA; i++ )
        {
            /* On effecute la soustraction de la colonne actuelle */
            int tmp = a[szA-i-1] - '0' - cr;

            if ( i < szB )
                tmp -= b[szB-i-1] - '0';

            /* si tmp est négatif on pose la retenue */
            cr = tmp < 0;
            if (cr)
            {
                /* Il y a une retenue on rajoute base à la valeur de tmp*/
                tmp += 10;
            }
            /* On récupère le caractère correspondant à la valeur de tmp */
            c[i] = tmp + '0';
        }

        /* On place le caractère de fin de chaîne */
        c[i] = '\0';

        /* On a rempli c par la gauche, on doit la retourner,et supprimer les
         * zéros inutiles.
         */

        rm_zeros_fin(c);
        reverse(c);
    }
}

sdiv_t
sdiv(char * dividend, char * divisor) {
    /* Reste précédent */
    char tmp[COLS_MAX];
    /* Resultat (quot & rem) */
    sdiv_t res = {NULL, NULL};

    int szA = strlen (dividend);
    int i, j;

    void reverse(char *);

    res.quot = calloc(szA+1, sizeof *res.quot);
    if (res.quot == NULL) {
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
    }
    res.rem = calloc(szA+1, sizeof *res.rem);
    if (res.rem == NULL) {
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
    }

    for (i = 0; i < szA; i++)
    {
        for ( j = 0; res.rem[j] != '\0'; j++ )
            ;
        /* On abaisse le chiffre courant */
        res.rem[j] =  dividend[i];

        /* On place le caractère '\0' pour former une chaîne valide */
        res.rem[j+1] = '\0';

        /* On initialise le chiffre courant du quotient à 0 */
        res.quot[szA-i-1] = 0;

        /* Dans reste contient de fois divisor ?
         * Le résultat va dans le chiffre courant du quotient
         */
        while (cmp_cre(res.rem, divisor) >= 0)
        {
            res.quot[szA-i-1]++;
            soustraction ( res.rem, divisor, tmp);
            strcpy ( res.rem, tmp );
        }
        /* Si le reste est nul */
        if ( res.rem[0] == '0' )
        {
            /* On se replace au début de la chaîne */
            res.rem[0] = '\0';
        }

        /* On récupère le caractère correspondant à la valeur */
        res.quot[szA-i-1] +=  '0';
    }

    /* On place le caractère de fin de chaîne */
    res.quot[i] = '\0';

    /* On a rempli c par la gauche, on doit la retourner,et supprimer les
     * zéros inutiles.
     */
    reverse(res.quot);
    
    if (strcmp(res.rem, "") == 0)
        strcpy(res.rem, "0");

    return res;
}

/**
 * \brief Saute les zeros en debut de chaine
 *
 * @param str : const char *
 * @return char *
 */
char *
skip_zeros(const char * str) {
    char * s = (char *)str;
    size_t len = strlen(str);
    size_t i;

    for (i = 0 ; i < len-1 && s[i] == '0' ; ++i)
        ;
    return s+i;
}

/**
 * \brief `Renverse` la chaine s : abcd -> dcba
 *
 * @param str : char *
 */ 
void reverse(char *str)
{
    size_t len = strlen(str);
    size_t i;

    for (i = 0; i < len / 2; ++i)
    {
        int tmp = str[i];
        str[i] = str[len-i-1];
        str[len-i-1] = tmp;
    }
}


Il nous faut aussi une fonction pour demander à l'utilisateur un chiffres sous forme de lettres. Je propose pour cela que nous utilisions la fonction suivante :
char *
get_s (const char * msg) {
    char * val = NULL;
    size_t len = 0;
    
    if (msg != NULL)
        printf("%s\n", msg);

    len = get_i("Combien de chiffres a votre nombre ?");

    val = calloc(len+10, sizeof *val);
    if (val == NULL) {
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
        return NULL;
    }
    
    while (scanf("%[0-9]", val) != 1) {
        puts("Veuillez entrez un nombre entier");
        clear_stdin();
    }

    return val;
}


La fonction pour récupérer les coupures ne change quasiment pas, si ce n'est que l'on travaille maintenant avec des char * .
size_t
get_coupure (char *** coupures, const char * type) {
    size_t nb, i;
    char msg[COLS_MAX];

    nb = i = 0;

    sprintf(msg, "Combien de %ss voulez-vous ?", type);
    nb = get_i(msg);

    *coupures = calloc(nb, sizeof **coupures);
    if (coupures == NULL) { /* Echec de l'allocation memoire */
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
	return 0;
    }

    for (i = 0 ; i < nb ; ++i) {
        (*coupures)[i] = get_s("Indiquez sa valeur ...");;
    }

    qsort(*coupures, nb, sizeof **coupures, cmp_dec);

    return nb;
}


De même, l'algorithme de traitement n'a pas changer, il a juste besoin d'une fonction de comparaison sur les grands entiers en plus de la dernière fois.

/**
 * \brief Compare deux entiers
 *
 * @param a : const void *
 * @param b : const void *
 * @return -1 (a<b), 1 (b>a) autrement 0
 */
int
cmp_cre (const char * a, const char * b) {
    size_t sz1;
    size_t sz2;

    char * skip_zeros(const char *);

    sz1 = strlen(skip_zeros(a));
    sz2 = strlen(skip_zeros(b));

    if (sz1 > sz2)
        return 1;
    else if (sz1 < sz2)
        return -1;
    else
        return strcmp(skip_zeros(a), skip_zeros(b));
}

char *
zCoupure (char * somme, char ** coupures, size_t nb_coupures, const char * type) {
    size_t i;
    sdiv_t nb = {NULL, NULL};
    int pluriel;

    char * skip_zeros(const char *);
    sdiv_t sdiv(char *, char *);
    
    for (i = 0 ; i < nb_coupures ; ++i) {
        memset(&nb, 0, sizeof nb);
         /* On calcule le nombre de billets ou de pieces a donner */
        nb = sdiv(somme, coupures[i]);
         /* Doit-on mettre la coupure au pluriel */
        pluriel = cmp_cre(skip_zeros(nb.quot), "1") == 1 ? 1 : 0;

        if (cmp_cre(nb.quot, "0") == 1) { /* Y a-t-il plus de 0 billet/piece a rendre */
            printf("%s %s%s de %s.\n", skip_zeros(nb.quot), type,
                   pluriel ? "s" : "" , coupures[i]);
        }

        strcpy(somme, nb.rem);
    }
    free(nb.quot), nb.quot = NULL;
    free(nb.rem), nb.rem = NULL;

    return somme;
}


Sans les opérateurs ternaires :
char *
zCoupure (char * somme, char ** coupures, size_t nb_coupures, const char * type) {
    size_t i;
    sdiv_t nb = {NULL, NULL};
    int pluriel;

    char * skip_zeros(const char *);
    sdiv_t sdiv(char *, char *);
    
    for (i = 0 ; i < nb_coupures ; ++i) {
        memset(&nb, 0, sizeof nb);
         /* On calcule le nombre de billets ou de pieces a donner */
        nb = sdiv(somme, coupures[i]);
         /* Doit-on mettre la coupure au pluriel */
        pluriel = cmp_cre(skip_zeros(nb.quot), "1") == 1 ? 1 : 0;

        if (cmp_cre(nb.quot, "0") == 1) { /* Y a-t-il plus de 0 billet/piece a rendre */
            if (pluriel == 0) {
                printf("%s %s de %s.\n", skip_zeros(nb.quot), type, coupures[i]);
            } else {
                printf("%s %ss de %s.\n", skip_zeros(nb.quot), type, coupures[i]);
            }
        }

        strcpy(somme, nb.rem);
    }
    free(nb.quot), nb.quot = NULL;
    free(nb.rem), nb.rem = NULL;

    return somme;
}

</code>


La version récursive est sensiblement la même que pour celle du niveau 2, je ne la propose donc pas.

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

#define COLS_MAX 128

typedef struct sdiv_s { char *quot, *rem; } sdiv_t;

/**
 * \brief Vide le buffer clavier
 */
void
clear_stdin () {
    int c;
    while ((c =getchar()) != '\n' && c != EOF);
}

/**
 * \brief Recupere un entier de maniere securisee
 *
 * @param msg : const char *
 * @return int
 */
int
get_i (const char * msg) {
    int val;
    
    if (msg != NULL) {
        printf("%s ", msg);
    }
    while (scanf("%d", &val) != 1) {
        puts("Veuillez entrez un nombre entier");
        clear_stdin();
    }
    return val;
}


/**
 * \brief Recupere un entier sous forme de chaine de maniere securisee
 *
 * @param msg : const char *
 * @return char *
 */
char *
get_s (const char * msg) {
    char * val = NULL;
    size_t len = 0;
    
    if (msg != NULL)
        printf("%s\n", msg);

    len = get_i("Combien de chiffres a votre nombre ?");

    val = calloc(len+10, sizeof *val);
    if (val == NULL) {
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
        return NULL;
    }
    
    while (scanf("%[0-9]s", val) != 1) {
        puts("Veuillez entrez un nombre entier");
        clear_stdin();
    }

    return val;
}

/**
 * \brief Compare deux entiers sous forme de chaines de caracteres : Tri decroissant
 *
 * @param a : const void *
 * @param b : const void *
 * @return -1 (a>b), 1 (b>a) autrement 0
 */
int
cmp_dec (const void * a, const void * b) {
    const char * pa = *(const char **)a;
    const char * pb = *(const char **)b;

    char * skip_zeros(const char *);

    pa = skip_zeros(pa);
    pb = skip_zeros(pb);

    if (strlen (pa) > strlen(pb))
        return -1;
    else if (strlen (pa) < strlen(pb))
        return 1;
    else
        return strcmp(pb, pa);
}

/**
 * \brief Compare deux entiers
 *
 * @param a : const void *
 * @param b : const void *
 * @return -1 (a<b), 1 (b>a) autrement 0
 */
int
cmp_cre (const char * a, const char * b) {
    size_t sz1;
    size_t sz2;

    char * skip_zeros(const char *);

    sz1 = strlen(skip_zeros(a));
    sz2 = strlen(skip_zeros(b));

    if (sz1 > sz2)
        return 1;
    else if (sz1 < sz2)
        return -1;
    else
        return strcmp(skip_zeros(a), skip_zeros(b));
}

size_t
get_coupure (char *** coupures, const char * type) {
    size_t nb, i;
    char msg[COLS_MAX];

    nb = i = 0;

    sprintf(msg, "Combien de %ss voulez-vous ?", type);
    nb = get_i(msg);

    *coupures = calloc(nb, sizeof **coupures);
    if (coupures == NULL) { /* Echec de l'allocation memoire */
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
	return 0;
    }

    for (i = 0 ; i < nb ; ++i) {
        (*coupures)[i] = get_s("Indiquez sa valeur ...");;
    }

    qsort(*coupures, nb, sizeof **coupures, cmp_dec);

    return nb;
}

char *
zCoupure (char * somme, char ** coupures, size_t nb_coupures, const char * type) {
    size_t i;
    sdiv_t nb = {NULL, NULL};
    int pluriel;

    char * skip_zeros(const char *);
    sdiv_t sdiv(char *, char *);
    
    for (i = 0 ; i < nb_coupures ; ++i) {
        memset(&nb, 0, sizeof nb);
         /* On calcule le nombre de billets ou de pieces a donner */
        nb = sdiv(somme, coupures[i]);
         /* Doit-on mettre la coupure au pluriel */
        pluriel = cmp_cre(skip_zeros(nb.quot), "1") == 1 ? 1 : 0;

        if (cmp_cre(nb.quot, "0") == 1) { /* Y a-t-il plus de 0 billet/piece a rendre */
            printf("%s %s%s de %s.\n", skip_zeros(nb.quot), type,
                   pluriel ? "s" : "" , coupures[i]);
        }

        strcpy(somme, nb.rem);
    }
    free(nb.quot), nb.quot = NULL;
    free(nb.rem), nb.rem = NULL;

    return somme;
}

int
main (void) {
    char * somme;
    char **billets = NULL, **pieces = NULL;
    size_t nb_billets, nb_pieces, i;

    somme = get_s("Quelle somme voulez vous coupez ?");

    nb_billets = get_coupure(&billets, "billet");
    nb_pieces = get_coupure(&pieces, "piece");

    somme = zCoupure(somme, billets, nb_billets, "billet");
    somme = zCoupure(somme, pieces, nb_pieces, "piece");

    for (i = 0 ; i < nb_billets ; ++i)
        free(billets[i]), billets[i] = NULL;
    for (i = 0 ; i < nb_pieces ; ++i)
        free(pieces[i]), pieces[i] = NULL;
    
    free(billets), billets = NULL;
    free(pieces), pieces = NULL;
    free(somme), somme = NULL;
    
    return EXIT_SUCCESS;
}

void rm_zeros_fin(char *s)
{
    size_t n = strlen(s);
    size_t i;

    /* On supprime les zéros inutiles */
    for (i = n - 1; i > 0 && s[i] == '0'; i--)
        s[i] = '\0';
}

void
soustraction(char *a, char *b, char *c)
{
    void reverse(char *);
    
    if (cmp_cre(a, b) < 0)
    {
        /* |a| < |b| -> -(b - a) */
        c[0] = '-';
        soustraction(b, a, c + 1);
    }
    else
    {
        /* Nombre de chiffres de a */
        int szA = strlen (a);
        /* Nombre de chiffres de b */
        int szB = strlen (b);
        /* Retenue */
        int cr = 0;

        int i;
        for ( i = 0; i < szA; i++ )
        {
            /* On effecute la soustraction de la colonne actuelle */
            int tmp = a[szA-i-1] - '0' - cr;

            if ( i < szB )
                tmp -= b[szB-i-1] - '0';

            /* si tmp est négatif on pose la retenue */
            cr = tmp < 0;
            if (cr)
            {
                /* Il y a une retenue on rajoute base à la valeur de tmp*/
                tmp += 10;
            }
            /* On récupère le caractère correspondant à la valeur de tmp */
            c[i] = tmp + '0';
        }

        /* On place le caractère de fin de chaîne */
        c[i] = '\0';

        /* On a rempli c par la gauche, on doit la retourner,et supprimer les
         * zéros inutiles.
         */

        rm_zeros_fin(c);
        reverse(c);
    }
}

sdiv_t
sdiv(char * dividend, char * divisor) {
    /* Reste précédent */
    char tmp[COLS_MAX];
    /* Resultat (quot & rem) */
    sdiv_t res = {NULL, NULL};

    int szA = strlen (dividend);
    int i, j;

    void reverse(char *);

    res.quot = calloc(szA+1, sizeof *res.quot);
    if (res.quot == NULL) {
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
    }
    res.rem = calloc(szA+1, sizeof *res.rem);
    if (res.rem == NULL) {
        char error_msg[COLS_MAX];
        sprintf(error_msg, "malloc:%d : Memory allocation error.", __LINE__);
        perror(error_msg);
    }

    for (i = 0; i < szA; i++)
    {
        for ( j = 0; res.rem[j] != '\0'; j++ )
            ;
        /* On abaisse le chiffre courant */
        res.rem[j] =  dividend[i];

        /* On place le caractère '\0' pour former une chaîne valide */
        res.rem[j+1] = '\0';

        /* On initialise le chiffre courant du quotient à 0 */
        res.quot[szA-i-1] = 0;

        /* Dans reste contient de fois divisor ?
         * Le résultat va dans le chiffre courant du quotient
         */
        while (cmp_cre(res.rem, divisor) >= 0)
        {
            res.quot[szA-i-1]++;
            soustraction ( res.rem, divisor, tmp);
            strcpy ( res.rem, tmp );
        }
        /* Si le reste est nul */
        if ( res.rem[0] == '0' )
        {
            /* On se replace au début de la chaîne */
            res.rem[0] = '\0';
        }

        /* On récupère le caractère correspondant à la valeur */
        res.quot[szA-i-1] +=  '0';
    }

    /* On place le caractère de fin de chaîne */
    res.quot[i] = '\0';

    /* On a rempli c par la gauche, on doit la retourner,et supprimer les
     * zéros inutiles.
     */
    reverse(res.quot);
    
    if (strcmp(res.rem, "") == 0)
        strcpy(res.rem, "0");

    return res;
}

/**
 * \brief Supprime les zeros en trop en debut de chaines
 *
 * @param str : const char *
 * @return char *
 */
char *
skip_zeros(const char * str) {
    char * s = (char *)str;
    size_t len = strlen(str);
    size_t i;

    for (i = 0 ; i < len-1 && s[i] == '0' ; ++i)
        ;
    return s+i;
}

/**
 * \brief `Renverse` la chaine s : abcd -> dcba
 *
 * @param str : char *
 */ 
void reverse(char *str)
{
    size_t len = strlen(str);
    size_t i;

    for (i = 0; i < len / 2; ++i)
    {
        int tmp = str[i];
        str[i] = str[len-i-1];
        str[len-i-1] = tmp;
    }
}


Comme l'a signalé GurneyH, dans la correction du niveau 3, j'utilise des commentaires un peu particuliers:
/**
 * \brief Supprime les zeros en trop en debut de chaines
 *
 * @param str : const char *
 * @return char *
 */

Ces commentaires sont de type Javadoc et permettent de "documenter" les fonctions grâce à des tags (\brief, @param, ...) pour préciser ce que fait telle ou telle fonction.
Les commentaires Javadoc sur la Wikipédia.
Ou sur le site de Sun.

Voilà pour la correction de l'exercice numéro 2 du mois de Mars.
Enjoy.

Ps: Si vous avez des questions n'hésitez pas !
Et si vous voyez des fautes d'orthographes, je vous prie de me les signaler, je sais qu'il en reste malgré ma relecture.
Lithrein.


Edit :
Ajout des versions sans les opérateurs ternaires.
Ajout de l'explication sur les commentaires de type Javadoc.