[Cours #5] ArgC, ArgV et allocations Dynamiques
-
<p>Bonjour à tous,<br/>
Bienvenue dans ce 4ième cours sur la programmation en C.<br/>
Aujourd'hui, nous allons voir un point intéréssent de notre programme, et un point clé en
les<br/>
Arguments de lancement, et les allocations dynamiques.</p>
<p><strong>I- Qu'est-ce qu'un argument de lancement?</strong><br/>
Un argument de lancement est une string ajoutée au lancement du programme permettant de faire passer ladite string au programme (string = chaine de caractères).<br/>
Ainsi, si je lance le programme avec cette ligne de commande:</p>
<blockquote class="ipsQuote" data-ipsquote=""><div>mon_programme mon_argument
<p> </p>
</div></blockquote>
<p>mon programme recevra comme argument 1 : "mon_argument".<br/>
(En faite, d'une façon générale, les arguments sont gérés par les shells qui lancent le programme. C'est une convention de programmation).<br/>
Quelques exemples d'utilisations d'arguments:<br/>
Les commandes unix utilisent très souvent les arguments:</p>
<blockquote class="ipsQuote" data-ipsquote=""><div>ls --help<br/>
cd --<br/>
cd /home<br/>
cd ~<br/>
mkdir test<br/>
...
<p> </p>
</div></blockquote>
<p>et peuvent donc permettre de faire passer une valeur, ou même un chemin à son programme (bah oui, c'est des Strings (tableaux de caractères). Bon, passons à la pratique:</p>
<p><strong>II- Récupérer des arguments de lancement</strong><br/>
Nous avions vu la structure basique d'un main en C:</p>
<pre class="ipsCode prettyprint">
int main(void)
{
}</pre>
<p>
Eh bien, je ne vous ai pas dit la vérité sur cette fonction!<br/>
En effet, main peut prendre un void pour argument (et donc ne prendre aucun arguments), ou bien prendre un int, et un char **.<br/>
Le véritable prototypage de la fonction main est donc:</p>
<pre class="ipsCode prettyprint">
int main(int argc, char *argv)
{
}</pre>
<p>
En voyant ça, vous allez me dire "dis donc, j'ai mal à la tête, y'a un ** dans ton code!" Eh bien ne<br/>
vous inquiétez pas, il s'agit d'un tableau de chaines de caractères.Prenons pour exemple ce programme lancé comme ceci:</p>
<pre class="ipsCode prettyprint">
coucou arg1 arg2 arg3 je suis fou
</pre>
<p>Eh bien, nous pouvons récupérer les arguments de ce programme de la façon suivante:</p>
<pre class="ipsCode prettyprint">
/
** Je re-crée les fonctions d'affichage. Voir cours #1 et #2
*/
void my_putchar(char c)
{
write(1, &c, 1);
}void my_putstr(char *str)
{
while(str)
my_putchar((str++));
}int main(int argc, char **argv)
{
my_putstr(argv[0]); // Affichera
my_putstr(argv[1]); // Affichera
my_putstr(argv[2]); // Affichera
my_putstr(argv[3]); // Affichera
my_putstr(argv[4]); // Affichera
my_putstr(argv[5]); // Affichera
my_putstr(argv[6]); // Affichera
return (0);
}
</pre>
<blockquote class="ipsQuote" data-ipsquote=""><div>"cocuou"<br/>
"arg1"<br/>
"arg2"<br/>
"arg3"<br/>
"je"<br/>
"suis"<br/>
"foud"
<p> </p>
</div></blockquote>
<p>Nous butons à présent sur un problème, ré-utilisons le programme précédent, mais supprimons tous,<br/>
ou un des arguments:</p>
<blockquote class="ipsQuote" data-ipsquote=""><div>Segmentation Fault
<p> </p>
</div></blockquote>
<p>
Eh oui, comme vous le voyez, nous essayons d'accéder à la valeur d'un tableau qui n'existe pas, ce qui provoque une erreur d'accès en mémoire.<br/>
Pour remédier à cela, les développeurs nous ont rajoutés "argc".<br/>
Pour mieux vous faire comprendre, je vais vous définir exactement ce que signifie argc et argv:<br/>
ArgC est un couplage entre les mots: Argument et Count, il s'agit donc du nombre d'arguments qui ont été passés en paramètres.<br/>
ArgV est un couplage entre les mots: Argument et Value, il s'agit donc des valeurs de ces dits arguments.</p>
<p>
Maintenant que vous avez le nombre d'arguments, vous pouvez vérifier que le nombre d'arguments est correcte avant de l'afficher:</p>
<pre class="ipsCode prettyprint">
int main(int argc, char **argv)
{
if (argc > 1)
my_putstr(argv[1]);
return (0);
}</pre>
<p>argv[0] sera toujours présent, puisqu'il s'agit du tout premier argument, qui est vital pour lancer le programme (il s'agit plus exactement de la commande utilisée pour lancer le programme).<br/>
Passons à présent à la partie complexe de ce cours: les allocations dynamiques.</p>
<p><strong>III- Les allocations dynamiques</strong><br/>
L'allocation dynamique, c'est l'art de savoir utiliser sa RAM et les répercussions de l'alloc sur celle-ci.<br/>
Nous avions vu précédemment que pour pouvoir avoir une chaine de caractère avec une certaine taille, nous devions définir un tableau de caractères, avec une taille FIXE.<br/>
Comme ceci:</p>
<pre class="ipsCode prettyprint">
char mon_tableau[talle_de_mon_tableau];
</pre>
<p>Là est le problème, imaginons que nous devions modifier notre chaine, en lui imposant une taille plus grande: SegFault.<br/>
Eh bien, la solution, c'est d'utiliser l'allocation dynamique: Au lieu de stocker notre chaine dans la stack, stockons la dans la RAM! (ce qui nous permet d'avoir plus d'espace, d'ailleurs! (généralement, la stack est limitée à quelques Ko, alors que la RAM non))<br/>
Vous comprenez donc qu'il est dangereux d'utiliser malloc de façon idiote: dans une boucle infinie par exemple... Puisque vous allez remplir votre RAM!</p>
<p>
Prenons un exemple simple:<br/>
J'ai un programme qui connaît la taille d'une chaine de caractère, et je dois la stocker dans une variable:</p>
<pre class="ipsCode prettyprint">
int main(void)
{
int lenght = 300000; // Oui, notre chaine est très longue
}</pre>
<p>
Si je tentais de déclarer un tableau statique de char de taille 300 000, il y aura énormément de chances pour que mon programme segfault dès le lancement.<br/>
C'est problématique, en effet.<br/>
Tentons donc d’allouer un emplacement dans la ram pour cette chaine:</p>
<pre class="ipsCode prettyprint">
int main(void)
{
char *ptr;ptr = malloc(300000 * sizeof(char));
}</pre>
<p>
Tiens, ça marche!<br/>
Lisons un peu mieux le man de malloc: en cas d'erreur, malloc retourne NULL.<br/>
Nous allons donc vérifier si il y a eu une erreur (pour éviter le segfault):</p>
<pre class="ipsCode prettyprint">
int main(void)
{
char ptr;
ptr = malloc(300000 * sizeof(char));
if (ptr == NULL)
{
my_putstr("Erreur malloc\n");
return ;
}
}</pre>
<p>
Bon, nous avons donc alloué un emplacement de type char (donc tableau de caractères) dans notre ram, avec une taille de 300 000 cases! Cool!<br/>
Nous pouvons donc remplire "ptr" exactement comme un tableau (case par case, voir cours sur les chaines de caractères).<br/>
ATTENTION: ptr est un POINTEUR. Si vous modifiez son adresse, vous perdez l'ancienne adresse allouée! Donc ceci:</p>
<pre class="ipsCode prettyprint">
int main(void)
{
char *ptr;ptr = malloc(300000 * sizeof(char));
if (ptr == NULL)
{
my_putstr("Erreur malloc\n");
return ;
}
ptr = "coucou";
}
</pre>
<p>est interdit et stupide (ne rigolez pas, les profs de programmation font souvent l'erreur!).<br/>
Bon, c'est bien, nous avons alloué de la mémoire, mais bon, c 'est pas cool pour la RAM, on ne la vide jamais, il va falloir la vider!<br/>
Pour la vider, la fonction "free" est là pour nous aider!<br/>
Voilà mon programme une fois la fonction "free" utilisée:</p>
<pre class="ipsCode prettyprint">
int main(void)
{
char *ptr;ptr = malloc(300000 * sizeof(char));
if (ptr == NULL)
{
my_putstr("Erreur malloc\n");
return ;
}
free(ptr);
}
</pre>
<p>Tout marche! Cool!<br/>
ATTENTION: pour bien vous montrer que modifier un pointeur est une mauvaise idée, tentez de faire ceci:</p>
<pre class="ipsCode prettyprint">
int main(void)
{
char *ptr;ptr = malloc(300000 * sizeof(char));
if (ptr == NULL)
{
my_putstr("Erreur malloc\n");
return ;
}
ptr = "coucou";
free(ptr);
}</pre>
<p>
Hop, vous tomberez sur une erreur qui se nomme "glibC" et qui corresponds à une erreur de pointage, la fonction "free" vous hurle dessus comme quoi votre pointeur ne pointe pas sur une zone<br/>
allouée.<br/>
Pour vous montrer que mon code fonctionne, je vais afficher ma chaine de caractère, qui fera<br/>
l'alphabet avec 300 000 lettres!</p>
<pre class="ipsCode prettyprint">
int main(void)
{
char *ptr;
int i;i = 0;
j = 0;
ptr = malloc(300000 * sizeof(char));
if (ptr == NULL)
{
my_putstr("Erreur malloc\n");
return ;
}
while(i < 300000)
{
ptr[i] = j + 'a';
i = i + 1;
j = j + 1;
if (j + 'a' >= 'z')
j = 0;
}
ptr[i] = '\0'; // NE PAS OUBLIER LA FIN DE LA CHAINE (voir cours sur les chaines de caractères)
my_putstr(ptr);
free(ptr);
}</pre>
<p>
Et voilà, vous savez à présent utiliser les allocations dynamiques!<br/>
A bientôt pour le prochain cours!<br/>
Cours écrit par AlexMog. Contact: alexmog [at] live [point] fr</p> -
<p>J'ajouterais aussi que <a href="http://valgrind.org/docs/manual/mc-manual.html" rel="external nofollow">Valgrind-Memcheck</a> est très utile pour détecter les erreurs liées a l'allocation dynamique.</p>
<p> </p>
<p>Pour l'utiliser : </p>
<pre class="ipsCode prettyprint">
valgrind ./votreprogramme
</pre>
<p>Et avec un makefile c'est encore plus simple
</p> -
<blockquote class="ipsQuote" data-cite="davydavek" data-ipsquote="" data-ipsquote-contentclass="forums_Topic" data-ipsquote-contentcommentid="8006" data-ipsquote-contentid="744" data-ipsquote-contenttype="forums" data-ipsquote-timestamp="1400960511" data-ipsquote-username="davydavek"><div>
<div>
<p>J'ajouterais aussi que <a href="http://valgrind.org/docs/manual/mc-manual.html" rel="external nofollow">Valgrind-Memcheck</a> est très utile pour détecter les erreurs liées a l'allocation dynamique.</p>
<p> </p>
<p>Pour l'utiliser : </p>
<pre class="ipsCode prettyprint">
valgrind ./votreprogramme
</pre>
<p>Et avec un makefile c'est encore plus simple
</p>
</div>
</div></blockquote>
<p>L'utiliser pour les memory leaks et aussi très intéréssente
.</p>
<p>J'ai déjà cité Valgrind dans le cours #0 justement
.</p>
<p> </p>
<p>GDB est aussi parfait pour ce genre de travail.</p>
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login
</p>