Aller au menu - Aller au contenu

Allocation dynamique en C - complément

Pour accéder à cette section
Connectez-vous !
connexion_rpx
Page 1 
Pseudo Commentaire
Page 1 
Hors ligne drakes00 # Posté le 29/05/2010 à 10:24:19
printf("Hello World!!");
Avatar

Ville : Grenoble
Pays : France métropolitaine
Études : UJF Grenoble

Pas mal, je n'ai pas encore tout lu mais c'est bien intéressant, en particulier calloc() qui peut s'avérer assez pratique.
Par contre, petite question: en terme d'optimisation, mieux vaut utiliser calloc() ou bien un malloc() et un for pour l'initialiser ?

Merci.

Image utilisateur
Image utilisateur

Vieillir est encore le seul moyen qu'on ait trouver de vivre longtemps.
The only thing I know is how to learn.
 
Hors ligne greg1107 # Posté le 29/05/2010 à 10:25:48

Super bien fait ce tuto !
Grand merci pour cette belle réalisation ;)
je me vais me sentir plus à l'aise pour bosser mon cours de C pour terminer mon année scolaire.
Bonne continuation

greg1107
Hors ligne Pouet_forever # Posté le 29/05/2010 à 10:38:48
Trance forever :)
Avatar

Avis : Décevant

Je croyais que les tricks & tips étaient interdis...
De plus, il y a pas mal de choses fausses dans ton tuto, notamment : "realloc() s'utilise après qu'on ait utilisé la fonction malloc() ou calloc()."

Image utilisateur
Image utilisateur
La musique du moment : Fictivision Vs. C-Quence - Symbols


[Le préprocesseur C]
Fan officiel de Tiësto !
 
Hors ligne GurneyH # Posté le 29/05/2010 à 11:09:04
Avatar

Autre chose fausse :
Utilser calloc pour initialiser un tableau de float... Voir la faq clc
bof, bof...
 
Hors ligne Grinwik # Posté le 29/05/2010 à 12:23:42
"Ca passait, c'était beau !"
Avatar

Ville : Talence
Pays : France métropolitaine

Continuons =P

Citation :
[...] la taille de ces éléments que l'on obtient avec sizeof().

Tu mets des parenthèses après sizeof comme après malloc, sauf que malloc est une fonction et sizeof un mot clé. Ca risque entretenir une confusion déjà très courante.

Citation :
Par conséquent, si on veut faire la même opération avec seulement les instructions malloc() et free(), c'est plus compliqué et cela donne :
Secret (cliquez pour afficher)
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
char *maChaine = malloc(15 * sizeof(char)), *copieDeMaChaine = NULL;
//15 ou plus probablement la valeur d'une variable.
int i;

if (maChaine == NULL)
    pb_memoire();
for (i=0; i<15; ++i)
    maChaine[i] = 0; //on met tout à zéro, ce que fait calloc()
// quelques instructions

copieDeMaChaine = malloc(15 * sizeof(char));
if (copieDeMaChaine == NULL)
    pb_memoire();

for (i = 0; i < 15; i++)
    copieDeMaChaine[i] = maChaine[i]; //On enregistre les données pour ne pas les perdre.
 
free(maChaine);//On libére la zone mémoire avant d'en acquérir une nouvelle.
maChaine = malloc(20 * sizeof(char)); //On demande plus de place en mémoire.

if (maChaine == NULL)
    pb_memoire();

for (i = 0; i < 15; i++)
    maChaine[i] = copieDeMaChaine[i]; //On récupère les valeurs qu'on a sauvegardées.
free(copieDeMaChaine);//On peut maintenant supprimer les données sauvegardées.
 
//quelques instructions
free(maChaine); //On n'oublie pas de libérer la mémoire à la fin du programme ou de la fonction.

Résumé : pour étendre une zone de 15 à 20, tu :
-> créés une autre de zone de 15.
-> Si cette zone est obtenue, tu y copies les données.
-> crées une zone de 20.
-> Si cette zone est obtenue, tu y copies les données depuis la zone "de sauvegarde".
Question : à quoi la zone de sauvegarde sert-elle ?

Secret (cliquez pour afficher)
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
char *maChaine = malloc(15 * sizeof(char)), *copieDeMaChaine = NULL;
int i;

if (maChaine == NULL)
  pb_memoire();
for (i=0; i<15; ++i)
  maChaine[i] = 0;

/* allocation d'une zone plus grande */
copieDeMaChaine = malloc(20 * sizeof(char));
if (copieDeMaChaine == NULL)
  pb_memoire();

/* si on a reussi a avoir une zone plus grande, on peut y copier les donnees */
for (i = 0; i < 15; i++)
  copieDeMaChaine[i] = maChaine[i]; //On enregistre les données pour ne pas les perdre.

/* on libere l'ancien espace et on fait pointer notre pointeur vers la nvlle zone */
free(maChaine);
maChaine = copieDeMaChaine;


/* the end */
free(maChaine);


Citation :
Dans ces exemples, on pourrait remplacer la boucle for() par la fonction strcpy(). Cela est vrai parce qu'on utilise des char mais on ne peut plus utiliser cette fonction dès lors qu'on manipule autre chose que des char, comme des int, des double etc. Donc l'utilisation de la boucle for() est une technique qui marchera dans tous les cas de figure.

Il y a également des fonctions génériques pour ça (bien que déclarées dans string.h) : memcpy(), memset() (et d'autres). Use with caution et pas connues des gens qui sortent du cours de M@theo21 c'est vrai =)

Citation :
Dans l'exemple suivant, j'ai créé un programme qui demande à l'utilisateur une lettre de début (carDebut) et une lettre de fin (carFin). Le programme va stocker en mémoire tous les caractères compris entre carDebut et carFin et on va les afficher un à un à l'aide d'un entier qui vaudra le nombre de caractères stockés : [...]

Citation :
Code : C
1
unsigned short int compteur, fin;

Pourquoi un short ?

Dans la fonction alphabet, la fonction peut être légèrement remanier pour ne plus avoir besoin du premier malloc().
Il y a plusieurs passages dans ce tuto qui sont révélateurs de ta connaissance trop partielle de réalloc() : quand le premier paramètre de ralloc() est NULL, elle se comporte exactement comme malloc().

Une question que je me poserais en tant que débutant :
Secret (cliquez pour afficher)
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
char *maChaine = calloc(15, sizeof(char));
if (maChaine == NULL)
    pb_memoire(); //Cette fonction affiche un message d'erreur et termine le programme.
//quelques instructions

maChaine = realloc(maChaine, 20 * sizeof(char))
if (maChaine == NULL)
    pb_memoire();
//quelques instructions

free(maChaine);

Si realloc() échoue, l'espace alloué ligne 1 est-il libéré ? Si non, pourquoi ne prends-tu pas le soin de le faire (alors que tu le fais partout ailleurs) ?


Un tuto à retravailler un minium, bon courage.
Hors ligne Arthurus # Posté le 29/05/2010 à 13:51:17
Everyday I'm shuffling
Avatar

Avis : Décevant Validateurs

Études : Ensimag

Pas fan du tout !!
Un tuto qui explique comment fonctionne une fonction n'est pas un tuto, il y a le man pour ça !
Bref, tuto totalement inutile à mon avis.
 
Hors ligne programLyrique # Posté le 29/05/2010 à 17:13:24
"Veniam, videbo, vincam !"
Avatar

Tu devrais aussi parler des problèmes de performance : faire une réallocation comme ça pour un élément de plus me paraît bien peu indiqué. Dans ce genre de situation où on doit souvent redimensionner, on utilise des listes chainées. A mon avis, une réallocation ne devrait être faite qu'occasionnellement, et pour un ajout de plusieurs éléments.
Tu aurais par ailleurs pu indiqué l'intérêt de ces fonctions de façon plus précise, en particulier pour calloc .
Je ne saisis pas vraiment son intérêt crucial en lisant le tuto : remplir de zéro est au plus anecdotique.
Mais l'idée est bonne ; il reste à étoffer et à compléter.

la conscience n'est pas ce qu'elle est ; elle est ce qu'elle n'est pas ; elle veut être tout ce qu'elle n'est pas mais elle ne veut pas être ce qu'elle est.







Li(\frac{1}{2}) = 2 \times (\frac{\pi^2}{6} - (\ln 2)^2)
 
Hors ligne sercus i # Posté le 29/05/2010 à 17:27:37
Forgotten.
Avatar

Avis : Décevant

Code : C
1
2
3
4
5
6
void pb_memoire()
{
 printf("ERREUR : probleme d'allocation de la memoire.\n");
 system("PAUSE");
 exit(0);
}

Un assert (); ou un perror (); seraient très bien à la place, et on oubli le system ("pause");

Sins can may be forgiven but conscience is a killer.
 
Hors ligne Quentin Guibert # Posté le 29/05/2010 à 23:09:58
Avatar

Ville : Saint martin de valgalgues
Pays : France métropolitaine

Bonjour bonjour,

Citation : GurneyH
Autre chose fausse :
Utilser calloc pour initialiser un tableau de float... Voir la faq clc
bof, bof...


Oui complètement ! Disons que ce n'est pas très portable...

Pour initialiser un tableau de données de type 'float', il faut initialiser les données manuellement et comme la fonction 'malloc' est plus rapide d'exécution que la fonction 'calloc', on préférera utiliser la fonction 'malloc' pour allouer dynamiquement une zone mémoire et affecter manuellement les valeurs (le contraire ce que fait l'auteur quoi).

Citation : Adrien37
realloc() s'utilise après qu'on ait utilisé la fonction malloc() ou calloc().

En restant dans ta logique, la fonction 'realloc' peut s'utiliser aussi pour réduire ou augmenter la taille d'une zone mémoire allouée par la fonction 'realloc'.

Sinon beaucoup de n'importe quoi dans le tutoriel... L'utilisation de la fonction 'system', tu écris les messages d'erreur sur la sortie standard alors que l'erreur standard doit être préféré pour les diagnostics et les messages d'erreur, tu utilises toujours la fonction 'printf' quand il n'y a aucun spécification de conversion alors que la fonction 'puts' est plus rapide d'exécution (bon, je sais que certains compilateurs change implicitement l'appel de la fonction 'printf' en l'appel de la fonction 'puts', 'printk' ou autre mais c'est mieux de le faire explicitement). Les commentaires style C99 ça passe encore mais bon...

Tu oublies de dire que l'appel à la fonction 'realloc' comme ceci :

Code : C
1
(void) realloc (p, 0) ;


Est équivalent à l'appel de la fonction 'free' comme ceci :

Code : C
1
free (p) ;


Bon, je sais que d'utiliser la fonction 'realloc' comme ça est une mauvaise utilisation, mais il vaut mieux le savoir...

N'oublions pas aussi que :

Code : C
1
p = realloc (NULL, sizeof (type) * x) ;


Est équivalent à :

Code : C
1
p = malloc (sizeof (type) * x) ;


Bon, il faut bien évidemment préférer l'utilisation de la fonction 'malloc' qui est plus rapide d'exécution...

La plus grosse erreur du tutoriel est surement d'oublier d'utiliser un pointeur différent de celui utilisé pour contenir la valeur de retour de la fonction 'malloc' qui contiendra l'adresse mémoire retournée par la fonction 'realloc' quand tu utilises précédemment la fonction 'malloc' (s'il y a une erreur au niveau de la fonction 'realloc', il y aura ce qu'on appelle une fuite mémoire, tu ne pourras pas libérer la zone mémoire référencé par l'adresse mémoire retournée par la fonction 'malloc').

Par exemple quand tu écris ça :

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
char *maChaine = calloc(15, sizeof(char));
if (maChaine == NULL)
    pb_memoire(); //Cette fonction affiche un message d'erreur et termine le programme.
//quelques instructions

maChaine = realloc(maChaine, 20 * sizeof(char))
if (maChaine == NULL)
    pb_memoire();
//quelques instructions

free(maChaine);


Il faut éviter de programmer comme ça, il faut utiliser un pointeur différent qui contiendra l'adresse mémoire retournée par la fonction 'realloc'.

Je m'explique ! :D
Imaginons ce code source (il contient une erreur volontaire) :

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int *p = NULL ;

if ((p = malloc (sizeof (int) * 10)) != NULL)
{
   /* Blablabla ... */

   if ((p = realloc (p, sizeof (int) * 20)) != NULL)
   {
      /* OK ! Mais on est pas à l'abri d'une fuite mémoire ! */

      /* Blablabla... */
   }

   else
   {
      /* On a perdu l'adresse mémoire retournée par la fonction 'malloc' ! On ne peut pas libérer la zone mémoire allouée à l'appel de la fonction 'malloc' ! Il y a ce qu'on appelle une fuite mémoire. */
   }
}

else
{
   /* Gestion des erreurs... */
}


Pour éviter cette fuite mémoire au cas où la fonction 'realloc' renverrait la valeur 'NULL', il faut utiliser un pointeur différent qui contiendra l'adresse mémoire retournée par la fonction 'realloc'.

Voici le code source sans erreur !

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int *p  = NULL ;
int *p2 = NULL ;

if ((p = malloc (sizeof (int) * 10)) != NULL)
{
   /* Blablabla... */

   if ((p2 = realloc (p, sizeof (int) * 20)) != NULL)
   {
      p = p2 ;

      p2 = NULL ;

      /* Blablabla... */
   }

   else
   {
      free (p) ;

      /* Gestion des erreurs... */
   }

   free (p) ;

   p = NULL ;
}

else
{
   /* Gestion des erreurs... */
}


Parler vite fait du qualificateur 'restrict' (implémentation de la norme C99) qui permet de certifier au compilateur que le pointeur déclaré avec ce qualificateur sera le seul à pointer sur une zone mémoire (de cette manière le compilateur peut effectuer des optimisations) aurait été bien. Pourquoi ne pas aussi parler des conversions explicites en langage C++ qui sont obligatoires (je sais qu'on est dans la rubrique 'langage C' mais c'est bien de faire quelques liens) ?

Aussi, il faut toujours affecter la valeur 'NULL' au pointeur passé à la fonction 'free' après l'avoir appelé. Un pointeur ne peut être que dans deux états. Un état où le pointeur ne pointe sur aucune adresse mémoire et un autre état où le pointeur pointe sur une adresse mémoire valide. Donc, comme après un appel à la fonction 'free' la zone mémoire allouée n'est plus valide, il faut affecter la valeur 'NULL' au pointeur puisqu'il ne pointe plus sur une zone mémoire valide. On peut définir une macro pour faire simple.

Citation : Adrien37
Qu'une initialisation soit requise ou non, je vous conseille d'utiliser la fonction calloc()

Bah, ça dépend des cas, la fonction 'malloc' est bien plus rapide que la fonction 'calloc', tu oublies de le dire.

Pour finir, j'ai bien rigolé à "Elle sert à réattribuer de la mémoire à un pointeur" au début de la partie 'La fonction realloc()' ! :-°
Hors ligne Adroneus # Posté le 12/06/2010 à 13:21:42
Avatar

Études : Epitech Toulouse

Je suis d'accord avec ce qui a ete dit plus haut, et apres, il y a encore cette histoire de norme... c'est chagrinant de voir des gens publier des tutoriels avec des codes bourres de lacunes...
Hors ligne HighTam # Posté le 13/07/2010 à 19:51:16
Sleep(FOR_EVER);
Avatar

Avis : Décevant

Ville : Sfax
Pays : Tunisie

La plus grosse erreur du tutoriel, c'est la fuite de mémoire éventuelle provoquée par realloc() . Je suis désolé mais le tutoriel n'est pas à la hauteur !

Image utilisateur

Image utilisateur
« L'enfer c'est les autres » Jean-Paul Sartre
Une mère a 21 ans de plus que son fils. Dans 6 ans, elle aura 5 fois l'âge de son fils. Où est le père ?
 
Hors ligne uknow # Posté le 25/08/2010 à 14:33:56
chercher est un art
Avatar

Ville : Paris
Pays : France métropolitaine
Études : CNAM

Citation : maxtoto

Tu oublies de dire que l'appel à la fonction 'realloc' comme ceci :

Code : C
1
(void) realloc (p, 0) ;


Est équivalent à l'appel de la fonction 'free' comme ceci :

Code : C
1
free (p) ;


Bon, je sais que d'utiliser la fonction 'realloc' comme ça est une mauvaise utilisation, mais il vaut mieux le savoir...


Faux ;) , ce comportement est indéfini :

Citation : ISO/IEC9899 : 7.20.3 Memory management functions

If the size of the space requested is zero, the behavior is implementationdefined:
either a null pointer is returned, or the behavior is as if the size were some
nonzero value, except that the returned pointer shall not be used to access an object.


Donc je pense que la réponse est claire (taille 0 = comportement indéfini).
 
Hors ligne Quentin Guibert # Posté le 04/09/2010 à 17:23:17
Avatar

Ville : Saint martin de valgalgues
Pays : France métropolitaine

Citation : uknow

Donc je pense que la réponse est claire (taille 0 = comportement indéfini).


Citation : ANSI X3.159-1989 : 4.10.3 Memory management functions

If the size of the space requested is zero, the behavior is implementation-defined


Ce comportement est défini par l'implémentation, dans notre cas, la fonction 'realloc'.

Citation : ANSI X3.159-1989 : 4.10.3.4 The realloc function

If size is zero and ptr is not a null pointer, the object it points to is freed.


;)
Hors ligne uknow # Posté le 05/09/2010 à 10:51:28
chercher est un art
Avatar

Ville : Paris
Pays : France métropolitaine
Études : CNAM

Dans la C99, les comportements des trois fonctions malloc, calloc et realloc, et regroupé en une seule description. J'en présume qu'il s'agit du même comportement ;) (sauf si le même sujet sujet est évoqué ailleurs chose qui m'étonnerait).


Citation : 7.20.3 Memory management functions

7.20.3 Memory management functions

1 The order and contiguity of storage allocated by successive calls to the calloc, malloc, and realloc functions is unspecified. The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated). The lifetime of an allocated object extends from the allocation until the deallocation. Each such allocation shall yield a pointer to an object disjoint from any other object. The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer is returned. If the size of the space requested is zero, the behavior is implementationdefined: either a null pointer is returned, or the behavior is as if the size were some
nonzero value, except that the returned pointer shall not be used to access an object.


Comportement indéfini ou défini par l'implémentation, ça revient au même, on sort du standard :) .

Citation : 7.20.3.4 The realloc function
Synopsis
1
Code : C
1
2
#include <stdlib.h>
void *realloc(void *ptr, size_t size);

Description
2 The realloc function deallocates the old object pointed to by ptr and returns a pointer to a new object that has the size specified by size. The contents of the new object shall be the same as that of the old object prior to deallocation, up to the lesser of the new and old sizes. Any bytes in the new object beyond the size of the old object have indeterminate values.
3 If ptr is a null pointer, the realloc function behaves like the malloc function for the specified size. Otherwise, if ptr does not match a pointer earlier returned by the calloc, malloc, or realloc function, or if the space has been deallocated by a call to the free or realloc function, the behavior is undefined. If memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.
Returns.
4 The realloc function returns a pointer to the new object (which may have the same value as a pointer to the old object), or a null pointer if the new object could not be allocated.
 
Hors ligne alatox # Posté le 06/09/2010 à 00:18:26

Tutoriel intéressant qui devrait selon moi après avoir été peaufiné ajouté au tuto de m@theo
Hors ligne Diti # Posté le 08/09/2010 à 18:42:48
Manchot empereur toon
Avatar
Validateurs

Ville : Sucy-en-brie
Pays : France métropolitaine
Études : EFREI

Le tutoriel ne devrait pas avoir une difficulté 1/3, déjà que le tuto officiel a 2/3 et que je comprends pas grand chose aux allocations dynamiques (pour l’instant)… là c’est encore pire.
 
Hors ligne Adroneus # Posté le 08/09/2010 à 21:42:58
Avatar

Études : Epitech Toulouse

En meme temps, ca sert a rien de lire un complement quand tu comprends pas les bases..
Hors ligne i00k # Posté le 29/09/2010 à 22:33:31
Waboooooow
Avatar

Ville : Sèvres
Pays : France métropolitaine

Faute dans la première phrase : bienvenuE dans...
 
Hors ligne Quentin Guibert # Posté le 24/10/2010 à 18:53:01
Avatar

Ville : Saint martin de valgalgues
Pays : France métropolitaine

C'est plutôt bizarre uknow, la partie 7.20.3.4 The realloc function ne précise pas ce comportement, comme tu dis... Mais à la partie 7.20.3.2 The free function je vois écris ceci :

Citation : 7.20.3.2 The free function

Synopsis
1
Code : C
1
2
#include <stdlib.h>
void free(void *ptr);

Description
2 The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by the calloc, malloc, or realloc function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined.
Returns
3 The free function returns no value.


Ça voudrait dire que la fonction 'realloc(3)' peut bien libérer une zone mémoire comme la fonction 'free(3)'... Je vois écrit ça un peu partout dans les fonctions de gestion de mémoire...

Qu'en penses-tu ? :]
Hors ligne uknow # Posté le 06/11/2010 à 13:36:57
chercher est un art
Avatar

Ville : Paris
Pays : France métropolitaine
Études : CNAM

Pour moi cette phrase veut dire simplement que si l'espace mémoire à été délocalisé par un appel de free (auquel cas il n'est plus significatif) ou par realloc (qui peut retourner l'adresse d'un espace non significatif si la taille n'a pas pu etre allouée par exemple), on serait entrain de libérer un espace non significatif, donc le comportement est indéfini.

Quelles autres phrases as-tu trouvée ?
(Dans la page man il est dit que realloc d'une taille nulle est comme un free chose qui me surprend car ce n'est pas dit explicitement dans la description de la fonction realloc d'après le draft de la norme).
 
Hors ligne lapipoune # Posté le 23/12/2010 à 22:44:01

Bonjour,

Serait-il possible de retirer les appels à system() de tous les exemples proposés ?
system est une fonction standard, mais conduit presque toujours à du code non-portable.
dans :
Citation : auteur du tuto

system("PAUSE") ;


PAUSE est une commande DOS.
Ce code ne marchera donc que sous Windows.
Mon idée est qu'un tuto ne devrait pas faire la promotion de code non portable.

Faire plutot quelque chose comme

Citation : moi

printf("tapez RETURN") ; scanf("%*c") ;


Merci
Hors ligne achrafaz # Posté le 06/02/2011 à 15:09:11
Avatar

Avis : Décevant

pourquoi fait-on l'allocation programmé?,des exemples?!!
Hors ligne Adrien31100 # Posté le 06/02/2011 à 17:27:54
A l'impossible nul n'est tenu.
Avatar

Ville : Tours
Pays : France métropolitaine
Études : Polytech'Tours

achrafaz tu peux être plus précis ?

Adrien31100
programmeur en C et C++
Image utilisateurImage utilisateur
 
Hors ligne Lucas-84 # Posté le 25/09/2011 à 19:04:42
[Tomber en marche]
Avatar
Validateurs
Flux RSS

Ville : Uchaux
Pays : France métropolitaine

Citation
calloc = Clear (memory) ALLOCation


Tu pourrais préciser ce que signifie cette note, je n'ai pas compris du premier coup...

Dans tes codes, tu pourrais éviter de dépasser les 80 colonnes ? C'est très désagréable de scroller en largeur sur le sdz...

fprintf (ou fputs) est à préférer à puts pour les erreurs sur stderr.

Plutôt que d'utiliser plusieurs printf à la suite, pourquoi ne pas en utiliser qu'un ?


A part ça, je trouve que le tutoriel est utile dans le sens où je vois calloc et realloc très peu utilisés chez les débutants, mais inutile dans le sens où un man fait très bien le boulot, il n'y a pas vraiment de "plus" (mis à part quelques exemples)...
 
Pour accéder à cette section
Connectez-vous !
connexion_rpx