Partage
  • Partager sur Facebook
  • Partager sur Twitter

Exercices pour débutants en C

Au menu : zSommeChiffres (nombres, algo)

13 novembre 2008 à 23:35:32

bonjour yoch

Citation : yoch

for (ptr1=debut, ptr2=debut+size; ptr2<=fin; ptr1+=size, ptr2+=size)
{
}

j'ai du mal a dechiffrer ton code , je suis pas habitué ! Et j'aimerais comprendre
pour moi une boucle pour s'ecrit :

pour ptr1 variant de début jusquà fin avec un pas de +1
faire
{
}

je le code en C
for (ptr1=debut;ptr1<=fin;ptr1 ++)
{
}


et la ta boucle déclarée comme si dessus, peux tu me la retranscrire d'une façon algorithmique, parce que je suis perdu ! :-°
merci
@+

  • Partager sur Facebook
  • Partager sur Twitter
14 novembre 2008 à 0:27:59

Salut,

Sa boucle possède plusieurs arguments juxtaposés pour chacune des trois indications à donner à for.
Cette boucle peut donc êter analysée ainsi :
  • Étape initiale : On met debut dans ptr1 et debut+size dans ptr2
  • La condition d'arrêt est si ptr2>fin est vrai
  • À chaque tour de boucle : On incrémente ptr1 et ptr2 de size

;)
  • Partager sur Facebook
  • Partager sur Twitter
14 novembre 2008 à 7:06:54

Merci crys' ! :)

Citation : crys'

Cette boucle peut donc êter analysée ainsi :

  • Étape initiale : On met debut dans ptr1 et debut+size dans ptr2
  • La condition d'arrêt est si ptr2<=fin est vrai
  • À chaque tour de boucle : On incrémente ptr1 et ptr2 de size


Je dirais plutôt 'tant que ptr2<=fin est vrai'.
  • Partager sur Facebook
  • Partager sur Twitter
14 novembre 2008 à 12:45:06

Ah oui, j'ai oublié d'inverser la comparaison. La condition d'arrêt est bien si ptr2>fin. ;)
  • Partager sur Facebook
  • Partager sur Twitter
14 novembre 2008 à 18:21:10

bonjour :
for (ptr1=debut, ptr2=debut+size; ptr2<=fin; ptr1+=size, ptr2+=size)
{
}



Citation : Yoch

Merci crys' !

Citation : crys'
Cette boucle peut donc êter analysée ainsi :

Étape initiale : On met debut dans ptr1 et debut+size dans ptr2
La condition d'arrêt est si ptr2<=fin est vrai
À chaque tour de boucle : On incrémente ptr1 et ptr2 de size


Je dirais plutôt 'tant que ptr2<=fin est vrai'.





Merci pour avoir eclairer ma lanterne :D
pour reformuler le code ci dessus cela veut dire :

ptr1 <-- debut
pour ptr2 variant de [ debut + size , fin ] par pas de size faire
    {
      ....................
       Corps de la boucle
      ....................
      ptr1 < --- ptr1 + size
    }


merci @+
  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 10:11:46

bonjour,
Dans le nouveau excercice, a t on le droit d'utiliser la
bibliotheque standart ctype.h ?
merci
@+
  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 14:01:28

Citation : zx-spectrum

pour reformuler le code ci dessus cela veut dire :

ptr1 <-- debut
pour ptr2 variant de [ debut + size , fin ] par pas de size faire
    {
      ....................
       Corps de la boucle
      ....................
      ptr1 < --- ptr1 + size
    }

Exactement. :)

Citation : zx-spectrum

Dans le nouveau excercice, a t on le droit d'utiliser la
bibliotheque standart ctype.h ?


Non justement, l'exercice est de faire sans. Mais il n'y aucune difficulté : pour tester si un caractère est majuscule ou minuscule, teste sa valeur ASCII. Pour transformer un caractère majuscule en minuscule ou l'inverse, incrémente ou décrémente le char.

Ceci peut t'aider à comprendre :
char c = 'A';
if(c >= 65 && c <= 90)
    printf("%c est majuscule \n",c);
else if(c >= 97 && c <= 122)
    printf("%c est minuscule \n",c);
  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 14:15:23

ok
merci
@+ :)


ps: j'aurais plutot fait comme cela :
char c = 'A';
if(c >= 'A' && c <= 'Z')
    printf("%c est majuscule \n",c);
else if(c >= 'a' && c <= 'z')
    printf("%c est minuscule \n",c);



j'ai essaye ton code car j'ai ete surpris que l'on puisse comparer :
un type char avec un nombre entier ?
pour ma part A € [A,Z] et a € [a,z] cela me sempble plus logique que :
A € [65,90] et a €[97,122] faisant appel a la correspondance de la table ASCII.
je pensais qu'il fallait composer une fonction de convertion char ---> n°de position dans la table ASCII!
  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 14:36:26

Le mieux c'est de pas utiliser de nombres du tout :
char c = 'X';

printf("%c est ", c);

if (c >= 'A' && c <= 'Z')
    printf("majuscule\n");

else if (c >= 'a' && c <= 'z')
    printf("miniscule\n");

else if (c >= '0' && c <= '9')
    printf("un nombre\n");

else
    printf("un symbole\n");
  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 18:45:05

Comme déjà dit, je suis entièrement d'accord avec zx-spectrum et python-guy (plus simple et plus portable).

Mais il n'est visiblement pas inutile pour un débutant de savoir comment ca marche :

#include <stdio.h>

int main(void)
{
    printf ("'%c' vaut %d", 'A', 'A');
    return 0;
}

SORTIE (chez moi) :
'A' vaut 65

  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 19:13:04

C'est vrai, la solution la plus portable consiste à ne pas spécifier de nombres mais c'était pour vous montrer le lien char <> ASCII mais aussi pour vous montrer comment tester la valeur numérique d'un char (dans mon exemple : sur le charset ASCII), c'est toujours bon à savoir.
  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 19:44:28

C'est juste !

Rappel des faits :
Un char est un type tenant généralement sur 8 bits (un octet), donc pouvant contenir de -127 a +127 (soit de 0 a 255).

Ce type est surtout utilisé pour stocker des caractères. La correspondance nombre/lettre suit un charset donné (généralement ASCII), ce qui explique que l'on puisse comparer des lettres avec des nombres. C'est aussi cela qui fait que 'b' est supérieur a 'a', etc. (utile pour strcmp(), par exemple).

Comme signalé, il est déconseillé de se fier a un charset quelconque dans son code, cela complexifie inutilement le code, et le rend moins portable (= peut ne pas fonctionner sur une architecture particulière).
  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 21:47:50

Citation : crys'


void tri_insertion(int* tab, size_t taille)
{
    unsigned i, j;
    int element_a_inserer, temp;
    for(i = 1; i < taille; i++)
    {
        element_a_inserer = tab[i];
        for(j = 0; j < i; j++)
        {
            if (element_a_inserer < tab[j])
            {
                temp = element_a_inserer;
                element_a_inserer = tab[j];
                tab[j] = temp;
            }
        }
        tab[i] = element_a_inserer;
    }
}




Cette méthode est outrageusement couteuse en copies d'éléments. J'ai fait un essai : pour trier le tableau à 4 éléments 4,2,6,3, le tri fait 12 copies alors qu'en théorie au pire on devrait en faire 7 et qu'un implémentation différente en nécessiterait 8. L'erreur vient du fait qu'il faut commencer le décalage par la droite et donc qu'on ne fait qu'UNE SEULE copie dans temp alors que la boucle ci-dessus fait un copie dans temp par élément décalé.

Citation : yoch

void my_triinsertion (void *base, size_t nmemb, size_t size, int (*compar)(const void*, const void *))
{
    char *debut = base;
    char* suiv;
    char *ptr;
    char *fin = (char*)base+(size*(nmemb-1));
    char tmp[100];
    int inserer;
    for (suiv = base+size; suiv <= fin; suiv += size)
    {
        inserer = 0;
        for (ptr = debut; ptr < suiv && !(inserer = compar(ptr, suiv) > 0); ptr += size);
        if (inserer)
        {
            memcpy(tmp, suiv, size);
            memmove(ptr+size, ptr, suiv-ptr);
            memcpy(ptr, tmp, size);
            NB_PERMUTATIONS += (suiv-ptr)/size;
        }
    }
}




L'affectation suiv = base+size est incorrecte car base est de type void *.

L'usage de memmove est discutable : d'un côté, on copie tout un bloc d'un seul coup (ce qui est plus rapide de la faire morceau par morceau) mais de l'autre memmove effectue (en principe) 2 fois plus que de copies que nécessaire. (*)

Par ailleurs, en vue de l'insertion, il me semble plus efficace d'effectuer le parcours des éléments triés en commençant par le plus grand (comme quand on fait le tri par insertion avec des cartes et non le plus petit comme toi et crys le faites. Dans ton cas c'est davantage justifié par le fait que tu utilises memmove). D'ailleurs, dans la littérature de référence c'est comme ça qu'on fait (Sedgewick par exemple).

Citation : yoch



- Je crois qu'il est normal qu'un tri générique cède en performances a un tri plus ciblé. C'est surtout du aux appels a la fonction compar.



Faudrait faire des mesures. Mais à mon avis ce qui peut couter cher ce sont plus les copies que les appels.


Citation : yoch



- Le tri insertion est limité par la taille de son buffer, on peut facilement corriger cela avec malloc...



En fait, à mon avis un bon appel de tri fait qu'on n'a jamais besoin d'un grand buffer. Si on a un tableau d'éléments qui sont de grandes tailles et que l'on doit trier, il est très maladroit de trier directement les élémeénts, il vaut mieux déplacer les adresses. Donc, on utilisera le même genre de fonction de comparaison que l'on utilise quand on veut trier des chaines de caractères.


(*)
EDIT. Contrairement à ce que j'ai dit, il n'y a pas de raison que memmove copie deux fois les données, il suffit juste de copier les données dans le bon ordre pour éviter l'écrasement pendant la copie. Je me suis laissé influencé par la réputation de memmove d'être lente par rapport à memcpy. Finalement, il ne reste plus grand chose à reprocher au code de yoch et on peut meme le féliciter de l'avoir produit. Si j'ai le temps, je donnerai ma propre version d'un isort().


  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 22:40:36

Citation : candide

L'affectation suiv = base+size est incorrecte car base est de type void *.


Juste ! Mea culpa...

Ce doit être :
for (suiv = debut+size; suiv <= fin; suiv += size)


Citation : candide

L'usage de memmove est discutable : d'un côté, on copie tout un bloc d'un seul coup (ce qui est plus rapide de la faire morceau par morceau) mais de l'autre memmove effectue (en principe) 2 fois plus que de copies que nécessaire.


Ah bon ? Mais pourquoi cela ?

implémentation possible de memmove :
void *my_memmove (void *dest, const void *src, size_t n)
{
    char* a = dest;
    const char* b = src;
    if (a < b)
    {
        while (n--)
            *a++ = *b++;
    }
    else if (a > b)
    {
        a += n, b += n;
        while (n--)
            *--a = *--b;
    }
    return dest;
}
  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 23:26:21

Citation : candide

En fait, à mon avis un bon appel de tri fait qu'on n'a jamais besoin d'un grand buffer. Si on a un tableau d'éléments qui sont de grandes tailles et que l'on doit trier, il est très maladroit de trier directement les éléménts, il vaut mieux déplacer les adresses. Donc, on utilisera le même genre de fonction de comparaison que l'on utilise quand on veut trier des chaines de caractères.



voyons :
deplacer des elements (E1 et E2 par ex) je comprends que cela doit être cela
Temp <-- E1
E1 <-- E2
E2 <-- Temp

déplacer les adresses ? j'ai du mal a saisir ?
si tu peux me doner un exemple simple
merci
@+

  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 23:42:51

Tu as fondamentalement deux façons de considérer des tableaux, ou autres structures de données.

Un exemple par code :
#include <stdio.h>

int main(void)
{
    char  tab1[][7] = { "blabla", "bliblo" };
    char* tab2[]    = { "blabla", "bliblo" };
    printf("sizeof tab1 : %u, sizeof *tab1 : %u\n", sizeof tab1, sizeof *tab1);
    printf("sizeof tab2 : %u, sizeof *tab2 : %u\n", sizeof tab2, sizeof *tab2);
    return 0;
}

sizeof tab1 : 14, sizeof *tab1 : 7
sizeof tab2 : 8, sizeof *tab2 : 4

Les deux tableaux peuvent être vus comme un tableau a deux dimensions, sauf que :
  • Le premier est un tableau au sens propre du terme. La taille du tableau dépend de la taille des éléments (nb_elems * size_elems );
  • Le second est un tableau d'adresses. Il contient simplement les adresses des éléments du tableau. La taille d'un tel tableau sera toujours de nb_elems * size_pointeur .


  • Partager sur Facebook
  • Partager sur Twitter
15 novembre 2008 à 23:59:26

Citation : zx-spectrum

voyons :
deplacer des elements (E1 et E2 par ex) je comprends que cela doit être cela
Temp <-- E1
E1 <-- E2
E2 <-- Temp

déplacer les adresses ?



les adresses sont des contenus de variables comme les autres, tu peux déplacer (pas forcément échanger comme tu le suggères ci-dessus) comme tu pourrais le faire avec des entiers. Les algos de tris par comparaison nécessitent lorsqu'ils s'exécutent de déplacer (et effectivement d'échanger) des valeurs en mémoire. Or un échange, c'est trois copies et une copie coute cher en temps d'exécution et on cherche en général à en réduire le nombre et la fréquence (vaut mieux en général copier en une seule fois que de fractionner les copies). Maintenant regarde comment on appelle la fonction standard qsort pour trier une liste d'entiers, j'ai repris l'exemple figurant sur la page de -ed- :


/* http://mapage.noos.fr/emdel/qsort.htm */
#include <stdio.h>
#include <stdlib.h>

/* affichage du tableau */
static void aff (int *a, size_t n)
{
   size_t i;
   for (i = 0; i < n; i++)
   {
      printf ("%3d", a[i]);
   }
   printf ("\n");
}

/* fonction utilisateur de comparaison fournie a qsort() */
static int compare (void const *a, void const *b)
{
   /* definir des pointeurs type's et initialise's
      avec les parametres */
   int const *pa = a;
   int const *pb = b;

   /* evaluer et retourner l'etat de l'evaluation (tri croissant) */
   return *pa - *pb;
}

int main (void)
{
/* tableau a trier */
   int tab[] = { 4, 6, -3, 4, 7 };

/* affichage du tableau dans l'etat */
   aff (tab, sizeof tab / sizeof *tab);

   qsort (tab, sizeof tab / sizeof *tab, sizeof *tab, compare);

/* affichage du tableau apres le tri */
   aff (tab, sizeof tab / sizeof *tab);

   return 0;
}


EDIT Désolé, j'ai cliqué sur "Envoyer" au lieu d'"aperçu". Je rééditerai pour terminer mon message.

La suite en question :


L'important est à la ligne 36 :

qsort (tab, sizeof tab / sizeof *tab, sizeof *tab, compare);


le troisième argument est la taille en octets de l'élément type qu'on trie, ici sizeof *tab vaut sizeof(int), en général 4 octets. En clair, ça veut dire que l'implémentation de qsort va déplacer des entiers.

Maintenant, imagine qu'au lieu de trier des entiers, tu tries des très grosses structures, un truc du genre :

struct toto {int x; int tab[2000];};



Il serait très maladroit de faire avec un tableau de telles structures comme pour avec un tableau d'entiers car l'implémentation de qsort va devoir déplacer de telles structures et qu'elles sont grosses, ça va être très très couteux. Donc, en fait, on passe en premier argument à qsort un tableau contenant non pas les structures mais les adresses des structures et on écrit la fonction de comparaison (dernier paramètre de la fonction qsort) avec un niveau d'indirection supplémentaire en sorte que l'implémentation de qsort n'ait qu'à déplacer des adresses de structures au lieu des structures elles mêmes.

Dans la pratique avoir le tableau des adresses correspondant au tri peut rendre les mêmes services que d'avoir le tableau des structures triées. Je en sais pas si je me suis bien fait comprendre.


  • Partager sur Facebook
  • Partager sur Twitter
16 novembre 2008 à 9:03:16

Merci Yoch et candide pour vos réponses.

j'ai compris la théorie du cours de mateo sur les pointeurs. Vous m'avez donné un exemple d'utilisation. Je vais faire mes propres "tests" pour bien comprendre, et surtout pratiquer cette partie du cours :-°:-° (il va me faloir un peu de temps :p )

J'ai bien noté que l'echange de valeur de deux variables coute plus de temps que le fait d'echanger leur adresses !


Citation : moi meme :

voyons :
deplacer des elements (E1 et E2 par ex) je comprends que cela doit être cela
Temp <-- E1
E1 <-- E2
E2 <-- Temp

déplacer les adresses ?


si j'ai bien compris pour les adresses :

long *temp;
long E1,E2;

temp <-- &E1
&E1  <-- &E2
&E2  <-- temp




sauf que l'on peut pas changer l'adresse d'une variable, je viens d'essayer.
ce qui veut dire que : &E1 <-- &E2 est incorrect.

Alors la seule solution que j'ai trouvé c'est de creer un pointeur qui pointe sur chaque variable, et de permuter les valeurs des pointeurs (qui contiennent les adresses des variables respectives)! :-° comme suit :

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




int main(void)
{

    int x,y;
    int *p1=&x;
    int *p2=&y;
    int *p3;


    x=1;
    y=2;
    printf("\n\n x=%d        y=%d ",*p1,*p2);

    // echange par pointeur

    p3 = p1;
    p1 = p2;
    p2 = p3;

    printf("\n\n x=%d        y=%d ",*p1,*p2);

    return 0;
}


d'ou ma réflexion suivante :
on est oblige de créer un pointeur pour chaque variable, ce qui me parait pas économique niveau gestion de la mémoire ?

@+
  • Partager sur Facebook
  • Partager sur Twitter
16 novembre 2008 à 19:31:30

Pas grave !

Supposons que tu ais une liste de 10000 éléments de 1 Ko chacun a trier.

Il est beaucoup plus efficace de créer un index d'adresses et de le trier, ce qui représente un volume de permutations de 10000 * 4 octets (un pointeur fait généralement 4 octets), que de trier les données directement, ce qui ferait un volume de permutations de 10000 * 1000 octets...
  • Partager sur Facebook
  • Partager sur Twitter
16 novembre 2008 à 20:16:50

Bonjour à tous.

Je reviens un peu au début, j'ai juste une petite question concernant une fonction de la bibliothèque strig.h !!!
Que fais la fonction strcle ?
  • Partager sur Facebook
  • Partager sur Twitter
16 novembre 2008 à 20:38:47

Citation : yoch

Pas grave !

Supposons que tu ais une liste de 10000 éléments de 1 Ko chacun a trier.

Il est beaucoup plus efficace de créer un index d'adresses et de le trier, ce qui représente un volume de permutations de 10000 * 4 octets (un pointeur fait généralement 4 octets), que de trier les données directement, ce qui ferait un volume de permutations de 10000 * 1000 octets...


ok, je comprends mieux le pourquoi. je vais m'employer maintenant au comment !
merci pour tes reponses
@+
  • Partager sur Facebook
  • Partager sur Twitter
17 novembre 2008 à 21:57:03

Bonjour,

Je débute encore en C, et j'avais une petite question:

Dans la fonction de Candide pour l'exercice ZBinary, il crée une fonction affichage_binaire:
void affichage_binaire(const unsigned int n)
{
    if (n > 1)
        affichage_binaire(n/2);
    putchar('0'+n%2);
    
}


Voilà, jusque là tout je bien je comprends tout. Mais je voudrais essayer de récupérer la valeur binaire du nombre pour la mettre dans une variable. Comment faire s'il vous plaît ?

Avec mon code, j'obtiens 10100 pour le décimal 5 :/ .
#include <stdio.h>
#include <stdlib.h>

int affichage_binaire(const unsigned int n)
{
    if (n > 1)
        affichage_binaire(n/2);
    putchar('0'+n%2);
    return (n%2);
}


int main(int argc, char *argv[])
{   
    int lol = 0;

    affichage_binaire(5);
    printf("\n\n");
    lol = affichage_binaire(20); 
    printf("\n\n");
    system("Pause");
    return 0;
}


Merci d'avance.

  • Partager sur Facebook
  • Partager sur Twitter
17 novembre 2008 à 22:06:51

Attention, la fonction de candide se contente d'afficher le résultat a l'aide de la fonction putchar().

Pour retourner quelque chose, ce sera plus compliqué. Tout d'abord, un int n'est certainement pas adapté, mieux vaut retourner une chaine. Je ne sais pas si cela va être possible avec la méthode que tu essaye d'utiliser (pas simple du moins).

EDIT: bon, je l'ai fait, c'est pas hyper difficile, mais un peu tiré par les cheveux :
#include <stdio.h>
#include <string.h>

char* en_binaire(const unsigned int n)
{
    static char result [32] = { 0 };
    char buf[2] = { 0 };
    /* On réinitialise result a chaque nouvel appel */
    *result = '\0';
    if (n > 1)
    {
        en_binaire(n/2);
    }
    *buf = '0'+n%2;
    strcat(result, buf);
    return result;
}

int main(void)
{
    puts(en_binaire(5));
    puts(en_binaire(20));
    puts(en_binaire(255));
    return 0;
}
  • Partager sur Facebook
  • Partager sur Twitter
18 novembre 2008 à 18:19:46

Le langage C est un langage :
- Simple
- Fort
- Trés fort
  • Partager sur Facebook
  • Partager sur Twitter
18 novembre 2008 à 21:35:28

Citation : khaledbenamor

Le langage C est un langage :
- Simple
- Fort
- Trés fort



Merci pour ces précisions :/ .

Sinon, merci beaucoup Yoch, ça m'a bien aidé.

A+ et merci à vous.
  • Partager sur Facebook
  • Partager sur Twitter
18 novembre 2008 à 21:36:57

Bientôt : la correction de zStrCapitalize. :)
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
19 novembre 2008 à 18:12:43

Comme prochain exercice je propose un exercice d'algorithmique : calculer le PGCD deux de entiers naturels aléatoires avec l'algorithme d'Euclide (niveau en maths de 3e).
  • Partager sur Facebook
  • Partager sur Twitter
19 novembre 2008 à 21:04:48

Citation : ttthebest

Comme prochain exercice je propose un exercice d'algorithmique : calculer le PGCD deux de entiers naturels aléatoires avec l'algorithme d'Euclide (niveau en maths de 3e).


Trop simple je trouve. :)
Par contre, Invading est injoignable, donc pour l'instant, pas de correction de zStrCapitalize. Je posterais le prochain exercice demain sans doute. ;)
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
20 novembre 2008 à 14:36:07

trop simple... il faut aussi des exos pour les doubles zéros ! (comme moi)
  • Partager sur Facebook
  • Partager sur Twitter