Initiation au langage C


précédentsommairesuivant

VI. L'allocation dynamique de mémoire

VI-A. Les fonctions malloc et free

L'intérêt d'allouer dynamiquement de la mémoire se ressent lorsqu'on veut créer un tableau dont la taille dont nous avons besoin n'est connue qu'à l'exécution par exemple. On utilise généralement les fonctions malloc et free.

 
Sélectionnez
int t[10];
...
/* FIN */

Peut être remplacé par :

 
Sélectionnez
int * p;

p = malloc(10 * sizeof(int));
...
free(p); /* libérer la mémoire lorsqu'on n'en a plus besoin */
/* FIN */

Les fonctions malloc et free sont déclarées dans le fichier stdlib.h. malloc retourne NULL en cas d'échec. Voici un exemple qui illustre une bonne manière de les utiliser :

 
Sélectionnez
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int * p;
    
    /* Creation d'un tableau assez grand pour contenir 10 entiers */
	p = malloc(10 * sizeof(int));
	
    if (p != NULL)
    {
        printf("Succes de l'operation.\n");
        p[0] = 1;
        printf("p[0] = %d\n", p[0]);
        free(p); /* Destruction du tableau. */
    }
    else
        printf("Le tableau n'a pas pu etre cree.\n");
    
    return 0;
}

VI-B. La fonction realloc

La fonction realloc :

 
Sélectionnez
void * realloc(void * memblock, size_t newsize);

permet de « redimensionner » une mémoire allouée dynamiquement (par malloc par exemple). Si memblock vaut NULL, realloc se comporte comme malloc. En cas de réussite, cette fonction retourne alors l'adresse de la nouvelle mémoire, sinon la valeur NULL est retournée et la mémoire pointée par memblock reste inchangée.

 
Sélectionnez

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

int main()
{
    int * p = malloc(10 * sizeof(int));
    
    if (p != NULL)
    {
        /* Sauver l'ancienne valeur de p au cas ou realloc echoue. */
        int * q = p;
        /* Redimensionner le tableau. */
        p = realloc(p, 20 * sizeof(int));
        
        if (p != NULL)
        {
            printf("Succes de l'operation.\n");
            p[0] = 1;
            printf("p[0] = %d\n", p[0]);
            free(p);
        }
        else
        {
            printf("Le tableau n'a pas pu etre redimensionne.\n");
            free(q);
        }
    }
    else
        printf("Le tableau n'a pas pu etre cree.\n");
    
    return 0;
}

VI-C. Exercices

VI-C-1. Calcul de la moyenne (version 2)

Reprendre l'exercice IV-D-2 en demandant cette fois-ci à l'utilisateur le nombre de notes qu'il désire entrer (limité à exactement 5 dans l'ancien sujet). Voici un exemple d'exécution :

 
Sélectionnez
Ce programme permet de calculer votre moyenne scolaire.
Entrez le nombre de notes que vous voulez entrer : 5
Note 1 (0 a 20) : 14
Coef 1 (1 a 5)  : 5
Note 2 (0 a 20) : 10
Coef 2 (1 a 5)  : 5
Note 3 (0 a 20) : 16
Coef 3 (1 a 5)  : 3
Note 4 (0 a 20) : 8
Coef 4 (1 a 5)  : 1
Note 5 (0 a 20) : 12
Coef 5 (1 a 5)  : 1
+------+------+----------+
| Note | Coef | Note Def |
+------+------+----------+
|   14 |    5 |       70 |
+------+------+----------+
|   10 |    5 |       50 |
+------+------+----------+
|   16 |    3 |       48 |
+------+------+----------+
|    8 |    1 |        8 |
+------+------+----------+
|   12 |    1 |       12 |
+------+------+----------+
| Tot. |   15 |      188 |
+------+-----------------+
| Moy. |           12.53 |
+------+-----------------+
Merci d'avoir utilise ce programme. A bientot !

VI-C-2. Recherche dans un tableau (version 2)

Reprenez l'exercice IV-D-1 en remplaçant les nombres par des chaînes de caractères. L'utilisateur entrera en premier lieu le nombre de chaînes qu'il va entrer.

Aucune chaîne ne doit excéder 20 caractères mais la mémoire que vous utiliserez pour stocker chaque chaîne entrée doit être juste assez grande pour la contenir. Autrement dit, votre objectif sera de minimiser la quantité de mémoire utilisée. Voici un exemple d'exécution :

 
Sélectionnez
Ce programme permet de trouver toutes les occurrences d'une chaine.
Combien de chaines voulez-vous entrer ? 10
Entrez 10 chaines de caracteres (20 caracteres par chaine au plus) :
t[0] : programmation
t[1] : langage
t[2] : C
t[3] : ANSI
t[4] : ISO
t[5] : IEC
t[6] : programmation
t[7] : programme
t[8] : code
t[9] : programme
Entrez la chaine a rechercher : programmation
t[0]
t[6]
2 occurrence(s) trouvee(s).
Merci d'avoir utilise ce programme. A bientot !

VI. Solutions des exercices

VI-A. La lettre X (II-E-1)

Voici la solution la plus évidente et la plus lisible :

 
Sélectionnez
#include <stdio.h>

int main()
{
    printf("*   *\n");
    printf(" * * \n");
    printf("  *  \n");
    printf(" * * \n");
    printf("*   *\n");
    return 0;
}

On peut cependant remarquer que les espaces après le dernier * de chaque ligne sont inutiles. De plus, rien ne nous oblige à faire un appel à printf pour chaque ligne. On peut faire tout le dessin avec un seul appel à cette fonction. Voici donc une autre solution, un peu moins évidente que la première :

 
Sélectionnez
#include <stdio.h>

int main()
{
    printf("*   *\n * *\n  *\n * *\n*   *\n");
    return 0;
}

VI-B. Périmètre d'un rectangle (II-E-2)

 
Sélectionnez
#include <stdio.h>

#define LONGUEUR 100
#define HAUTEUR 60

int perimetre(int L, int h);

int main()
{
    printf(
        "Le perimetre d'un rectangle de longueur %d et de hauteur %d est %d.\n",
        LONGUEUR, HAUTEUR, perimetre(LONGUEUR, HAUTEUR)
    );
    return 0;
}

int perimetre(int L, int h)
{
    return 2 * (L + h);
}

Ayez l'habitude de représenter les valeurs figées par des macros. Cela permet d'avoir un code à la fois lisible et facilement maintenable : si un jour la spécification change, il suffit de changer les définitions des macros et non plusieurs parties du programme.

VI-C. Valeur absolue (III-G-1)

Il y a plusieurs façons d'y arriver. Voici deux exemples :

 
Sélectionnez
#include <stdio.h>

int main()
{
    double x, y;

    printf("Ce programme permet de determiner la valeur absolue d'un nombre.\n");
    printf("Entrez un nombre : ");
    scanf("%lf", &x);

    if (x >= 0)
        y = x;
    else
        y = -x;

    printf("La valeur absolue de %f est : %f\n", x, y);

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}
 
Sélectionnez
#include <stdio.h>

int main()
{
    double x;

    printf("Ce programme permet de determiner la valeur absolue d'un nombre.\n");
    printf("Entrez un nombre : ");
    scanf("%lf", &x);

    printf("La valeur absolue de %f est : %f\n", x, (x >= 0 ? x : -x));

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

VI-D. Moyenne (III-G-2)

 
Sélectionnez
#include <stdio.h>

int main()
{
    int note;

    printf("Ce programme permet de determiner si vous avez eu la moyenne ou non.\n");
    printf("Entrez votre note (0 a 20) : ");
    scanf("%d", &note);

    if (0 <= note && note <= 20)
    {
        if (note >= 10)
            printf("Vous avez eu la moyenne.\n");
        else
            printf("Vous n'avez pas eu la moyenne.\n");
    }
    else
        printf("Cette note n'est pas valide.\n");

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

VI-E. L'heure dans une minute (III-G-3)

 
Sélectionnez
#include <stdio.h>

int main()
{
    int h, m;

    printf("Ce programme permet de determiner l'heure qu'il sera une minute plus tard.\n");
    printf("Entrez l'heure actuelle : ");
    scanf("%d", &h);
    printf("Entrez la minute actuelle : ");
    scanf("%d", &m);

    m++;

    if (m == 60)
    {
        m = 0;
        h++;

        if (h == 24)
            h = 0;
    }

    printf("Dans une minute il sera : %02d:%02d\n", h, m);

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

VI-F. Rectangle (III-G-4)

Le programme a est complet. Pour les programmes b et c, seule la fonction main() est donnée (les autres parties du programme restant inchangées par rapport à a).

a.

 
Sélectionnez
#include <stdio.h>

void affiche_car(char c, int n);

int main()
{
    int L;

    printf("Ce programme dessine une ligne.\n");
    printf("Entrez la longueur de la ligne (0 a 20) : ");
    scanf("%d", &L);

    if (0 <= L && L <= 20)
    {
        affiche_car('*', L);
        putchar('\n');
    }
    else
        printf("Cette valeur est invalide.\n");

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

void affiche_car(char c, int n)
{
    if (c > 0 && n > 0)
    {
        int i; /* i : nombres de caracteres deja affiches */

        for(i = 0; i < n; i++) /* on arrete des que i a atteint n */
            putchar(c);
    }
}

Vous ne vous attendiez certainement pas à ce que la valeur de c soit testée avant de le passer à putchar. La raison est tout simplement que l'argument attendu par putchar est un int (qui doit avoir une valeur positive) et non un char. Comme char peut faire référence à signed char, il vaut mieux tester la valeur du caractère reçu avant de faire quoi que ce soit avec. putchar sera étudiée dans plus de détails dans le chapitre V.

b.

 
Sélectionnez
int main()
{
    int L;

    printf("Ce programme dessine un rectangle plein.\n");
    printf("Entrez la longueur du rectangle (0 a 20) : ");
    scanf("%d", &L);

    if (0 <= L && L <= 20)
    {
        int h;

        printf("Entrez la hauteur du rectangle (0 a 20) : ");
        scanf("%d", &h);

        if (0 <= h && h <= 20)
        {
            /* On dessine h lignes (lignes 1 a h) */
        	
            int i; /* i : numero de la ligne en cours */

            for(i = 1; i <= h; i++)
            {
                affiche_car('*', L);
                putchar('\n');
            }
        }
        else
            printf("Cette valeur est invalide.\n");
    }
    else
        printf("Cette valeur est invalide.\n");

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

c.

 
Sélectionnez
int main()
{
    int L;

    printf("Ce programme dessine un rectangle creux.\n");
    printf("Entrez la longueur du rectangle (0 a 20) : ");
    scanf("%d", &L);

    if (0 <= L && L <= 20)
    {
        int h;

        printf("Entrez la hauteur du rectangle (0 a 20) : ");
        scanf("%d", &h);

        if (0 <= h && h <= 20)
        {
            int i;

            for(i = 1; i <= h; i++)
            {
            	/* Pour les lignes 1 et h, dessiner une ligne continue */
                if (i == 1 || i == h)
                    affiche_car('*', L);
                else
                {
                    /* Pour le reste, seules les extreimites sont visibles */
                    putchar('*');
                    affiche_car(' ', L - 2);
                    putchar('*');
                }

                putchar('\n');
            }
        }
        else
            printf("Cette valeur est invalide.\n");
    }
    else
        printf("Cette valeur est invalide.\n");

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

VI-G. Triangle isocèle (III-G-5)

Ici aussi, seule la fonction main() est donnée pour les mêmes raisons que dans VII-F.

a. Il faut juste remarquer qu'à la ligne i (i allant de 1 à h), il y a h - i espaces suivi de 1 + 2 * (i - 1) étoiles.

 
Sélectionnez
int main()
{
    int h;

    printf("Ce programme dessine un triangle.\n");
    printf("Entrez la hauteur du triangle (0 a 20) : ");
    scanf("%d", &h);

    if (0 <= h && h <= 20)
    {
        int i;

        for(i = 1; i <= h; i++)
        {
            affiche_car(' ', h - i);
            affiche_car('*', 2 * i - 1);
            putchar('\n');
        }
    }
    else
        printf("Cette valeur est invalide.\n");

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

b.

 
Sélectionnez
int main()
{
    int h;

    printf("Ce programme dessine un triangle.\n");
    printf("Entrez la hauteur du triangle (0 a 20) : ");
    scanf("%d", &h);

    if (0 <= h && h <= 20)
    {
        int i, n, nb_etoiles; /* nb_etoiles : nombre d'etoiles deja affichees */

        for(i = 1, nb_etoiles = 0; i <= h; i++)
        {
            n = h - i;
            affiche_car(' ', n);

            n = 2 * i - 1;
            affiche_car('*', n);

            nb_etoiles += n;

            putchar('\n');
        }

        printf("Il y a %d etoile(s) affichee(s).\n", nb_etoiles);
    }
    else
        printf("Cette valeur est invalide.\n");

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

VI-H. Somme (III-G-6)

 
Sélectionnez
#include <stdio.h>

int main()
{
    int n, saisis = 0, fini = 0, total = 0;

    printf("Ce programme permet de calculer une somme.\n");
    printf("Entrez la liste des nombres a additionner (terminez en entrant 0).\n");

    while (!fini)
    {
        scanf("%d", &n);
        saisis++;
        total += n;
        fini = (saisis == 10 || n == 0);
    }

    printf("Le total est : %d\n", total);

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

VI-I. Recherche dans un tableau (IV-D-1)

 
Sélectionnez
#include <stdio.h>

int main()
{
    int t[10], i, n, nb_occurrences = 0;

    printf("Ce programme permet de trouver toutes les occurrences d'un nombre.\n");
    printf("Entrez 10 nombres :\n");

    for(i = 0; i < 10; i++)
    {
        printf("t[%d] : ", i);
        scanf("%d", t + i);
    }

    printf("Entrez le nombre a rechercher : ");
    scanf("%d", &n);

    for(i = 0; i < 10; i++)
    {
        if (t[i] == n)
        {
            printf("t[%d]\n", i);
            nb_occurrences++;
        }
    }

    printf("%d occurrence(s) trouvee(s)\n", nb_occurrences);

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

VI-J. Calcul de la moyenne (IV-D-2)

 
Sélectionnez
#include <stdio.h>

int main()
{
    int t[5][3], i, total_coefs = 0, total_notes = 0;
    double moyenne;

    printf("Ce programme permet de calculer votre moyenne scolaire.\n");

    for(i = 0; i < 5; i++)
    {
        /* Saisie des notes */

        printf("Note %d : ", i + 1);
        scanf("%d", &(t[i][0]));
        printf("Coef %d : ", i + 1);
        scanf("%d", &(t[i][1]));
        t[i][2] = t[i][0] * t[i][1];

        /* Calcul des totaux */

        total_notes += t[i][2];
        total_coefs += t[i][1];
    }

    moyenne = ((double)total_notes) / total_coefs;

    /* Affichage du resultat */

    printf("+------+------+----------+\n");
    printf("| Note | Coef | Note Def |\n");
    printf("+------+------+----------+\n");

    for(i = 0; i < 5; i++)
    {
        printf("| %4d | %4d | %8d |\n", t[i][0], t[i][1], t[i][2]);
        printf("+------+------+----------+\n");
    }

    printf("| Tot. | %4d | %8d |\n", total_coefs, total_notes);
    printf("+------+-----------------+\n");
    printf("| Moy. | %15.2f |\n", moyenne);
    printf("+------+-----------------+\n");

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

VI-K. Manipulation de chaînes (IV-D-3)

 
Sélectionnez
char * str_cpy(char * dest, const char * source)
{
    int i;
    
    for(i = 0; source[i] != '\0'; i++)
        dest[i] = source[i];
    
    dest[i] = '\0';
    
    return dest;
}

size_t str_len(const char * t)
{
    size_t len;
    
    for(len = 0; t[len] != '\0'; len++)
        /* On ne fait rien, on laisse seulement boucler */ ;
    
    return len;
}

int str_equals(const char * s1, const char * s2)
{
    int i = 0, equals = 1;

    while (equals && s1[i] != '\0' && s2[i] != '\0')
    {
        if (s1[i] != s2[i])
            equals = 0;
        i++;
    }

    if (equals && (s1[i] != '\0' || s2[i] != '\0'))
        equals = 0;

    return equals;
}

Remarquez bien que nous avons choisi char * comme type de retour de str_cpy et non void. La valeur retournée n'est autre que la valeur du pointeur dest. Le prototype de str_cpy est strictement le même que celui de la fonction standard strcpy. Cela permet d'écrire du code compact lorsqu'on a envie, par exemple :

 
Sélectionnez
char s[50];
strcat(strcpy(s, "Bonjour"), " tout le monde !");

Le deuxième argument de la fonction (s2) est déclaré const char * et non simplement char *. En effet, les données pointées par s2 doivent être uniquement lues par la fonction, cette dernière ne doit pas avoir le droit de les modifier. L'ajout du qualificateur const ici est donc plus que recommandé. De plus, cela a un aspect documentaire : la présence ou l'abscence de const permet aux utilisateurs d'une fonction de savoir quelles données pourraient être modifiées par la fonction et quelles données seront justes lues.

VI-L. Calcul de la moyenne (version 2) (VI-C-1)

 
Sélectionnez
#include <stdio.h>
#include <stdlib.h>

typedef int NOTE[3];

int main()
{
    int n;

    printf("Ce programme permet de calculer votre moyenne scolaire.\n");
    printf("Entrez le nombre de notes que vous voulez entrer : ");
    scanf("%d", &n);

    if (n > 0)
    {
        NOTE * t = malloc(n * sizeof(NOTE));

        if (t != NULL)
        {
            int i, total_coefs = 0, total_notes = 0;
            double moyenne;

            for(i = 0; i < n; i++)
            {
                printf("Note %d : ", i + 1);
                scanf("%d", &(t[i][0]));
                printf("Coef %d : ", i + 1);
                scanf("%d", &(t[i][1]));
                t[i][2] = t[i][0] * t[i][1];
                total_notes += t[i][2];
                total_coefs += t[i][1];
            }

            moyenne = ((double)total_notes) / total_coefs;

            printf("+------+------+----------+\n");
            printf("| Note | Coef | Note Def |\n");
            printf("+------+------+----------+\n");

            for(i = 0; i < n; i++)
            {
                printf("| %4d | %4d | %8d |\n", t[i][0], t[i][1], t[i][2]);
                printf("+------+------+----------+\n");
            }

            free(t);

            printf("| Tot. | %4d | %8d |\n", total_coefs, total_notes);
            printf("+------+-----------------+\n");
            printf("| Moy. | %15.2f |\n", moyenne);
            printf("+------+-----------------+\n");
        }
        else
            printf("Le tableau n'a pas pu etre cree.\n");
    }
    else
        printf("Cette valeur est invalide.\n");

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

VI-M. Recherche dans un tableau (version 2) (VI-C-2)

 
Sélectionnez
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef char * PCHAR;

size_t get_string(char * lpBuffer, int nBufSize);

int main()
{
    int n;
    char s[21];

    printf("Ce programme permet de trouver toutes les occurrences d'une chaine.\n");
    printf("Combien de chaines voulez-vous entrer ? ");
    scanf("%d", &n);
    get_string(s, sizeof(s)); /* Pour purger stdin */

    if (n > 0)
    {
         PCHAR * t = malloc(n * sizeof(PCHAR));

         if (t != NULL)
         {
            int i, nb_occurrences = 0, succes = 1;

            printf("Entrez %d chaines de caracteres (20 caracteres par chaine au plus) :\n", n);

            for(i = 0; succes && i < n; i++)
            {
            	size_t len;
            	
                printf("t[%d] : ", i);
                len = get_string(s, sizeof(s));
                t[i] = malloc(len + 1);

                if (t[i] != NULL)
                    strcpy(t[i], s);
                else
                {
                    int j;

                    for(j = 0; j < i; j++)
                        free(t[j]);

                    succes = 0;

                    printf("Memoire insuffisante pour continuer.\n");
                }
            }

            if (succes)
            {
                printf("Entrez la chaine a rechercher : ");
                get_string(s, sizeof(s));

                for(i = 0; i < n; i++)
                {
                    if (strcmp(t[i], s) == 0)
                    {
                        printf("t[%d]\n", i);
                        nb_occurrences++;
                    }

                    free(t[i]);
                }

                printf("%d occurrence(s) trouvee(s)\n", nb_occurrences);
            }

            free(t);
         }
         else
            printf("Le tableau n'a pas pu etre cree.\n");
    }

    printf("Merci d'avoir utilise ce programme. A bientot !\n");

    return 0;
}

size_t get_string(char * lpBuffer, int nBufSize)
{
    size_t len = 0;

    if (fgets(lpBuffer, nBufSize, stdin) != NULL)
    {
    	char * p;
        len = strlen(lpBuffer);

        p = lpBuffer + len - 1;
        if (*p == '\n')
        {
            *p = '\0'; /* on ecrase le '\n' */
            len--;
        }
        else
        {
            /* On vide le tampon de lecture du flux stdin */
            int c;

            do
				c = getchar();
            while (c != EOF && c != '\n');
        }
    }
    
    return len;
}

précédentsommairesuivant

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2008 Melem. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.