AlexMog Posté(e) May 22, 2014 Signaler Posté(e) May 22, 2014 Bonjour à tous, Bienvenue dans ce 4ième cours sur la programmation en C. Aujourd'hui, nous allons voir un point intéréssent de notre programme, et un point clé en C: les Arguments de lancement, et les allocations dynamiques. I- Qu'est-ce qu'un argument de lancement? 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). Ainsi, si je lance le programme avec cette ligne de commande: mon_programme mon_argument mon programme recevra comme argument 1 : "mon_argument". (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). Quelques exemples d'utilisations d'arguments: Les commandes unix utilisent très souvent les arguments: ls --help cd -- cd /home cd ~ mkdir test ... 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: II- Récupérer des arguments de lancement Nous avions vu la structure basique d'un main en C: int main(void) { } Eh bien, je ne vous ai pas dit la vérité sur cette fonction! En effet, main peut prendre un void pour argument (et donc ne prendre aucun arguments), ou bien prendre un int, et un char **. Le véritable prototypage de la fonction main est donc: int main(int argc, char **argv) { } En voyant ça, vous allez me dire "dis donc, j'ai mal à la tête, y'a un ** dans ton code!" Eh bien ne vous inquiétez pas, il s'agit d'un tableau de chaines de caractères.Prenons pour exemple ce programme lancé comme ceci: coucou arg1 arg2 arg3 je suis fou Eh bien, nous pouvons récupérer les arguments de ce programme de la façon suivante: /* ** 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); } "cocuou" "arg1" "arg2" "arg3" "je" "suis" "foud" Nous butons à présent sur un problème, ré-utilisons le programme précédent, mais supprimons tous, ou un des arguments: Segmentation Fault 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. Pour remédier à cela, les développeurs nous ont rajoutés "argc". Pour mieux vous faire comprendre, je vais vous définir exactement ce que signifie argc et argv: 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. ArgV est un couplage entre les mots: Argument et Value, il s'agit donc des valeurs de ces dits arguments. Maintenant que vous avez le nombre d'arguments, vous pouvez vérifier que le nombre d'arguments est correcte avant de l'afficher: int main(int argc, char **argv) { if (argc > 1) my_putstr(argv[1]); return (0); } 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). Passons à présent à la partie complexe de ce cours: les allocations dynamiques. III- Les allocations dynamiques L'allocation dynamique, c'est l'art de savoir utiliser sa RAM et les répercussions de l'alloc sur celle-ci. 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. Comme ceci: char mon_tableau[talle_de_mon_tableau]; Là est le problème, imaginons que nous devions modifier notre chaine, en lui imposant une taille plus grande: SegFault. 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)) 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! Prenons un exemple simple: J'ai un programme qui connaît la taille d'une chaine de caractère, et je dois la stocker dans une variable: int main(void) { int lenght = 300000; // Oui, notre chaine est très longue } 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. C'est problématique, en effet. Tentons donc d’allouer un emplacement dans la ram pour cette chaine: int main(void) { char *ptr; ptr = malloc(300000 * sizeof(char)); } Tiens, ça marche! Lisons un peu mieux le man de malloc: en cas d'erreur, malloc retourne NULL. Nous allons donc vérifier si il y a eu une erreur (pour éviter le segfault): int main(void) { char *ptr; ptr = malloc(300000 * sizeof(char)); if (ptr == NULL) { my_putstr("Erreur malloc\n"); return ; } } 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! Nous pouvons donc remplire "ptr" exactement comme un tableau (case par case, voir cours sur les chaines de caractères). ATTENTION: ptr est un POINTEUR. Si vous modifiez son adresse, vous perdez l'ancienne adresse allouée! Donc ceci: int main(void) { char *ptr; ptr = malloc(300000 * sizeof(char)); if (ptr == NULL) { my_putstr("Erreur malloc\n"); return ; } ptr = "coucou"; } est interdit et stupide (ne rigolez pas, les profs de programmation font souvent l'erreur!). 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! Pour la vider, la fonction "free" est là pour nous aider! Voilà mon programme une fois la fonction "free" utilisée: int main(void) { char *ptr; ptr = malloc(300000 * sizeof(char)); if (ptr == NULL) { my_putstr("Erreur malloc\n"); return ; } free(ptr); } Tout marche! Cool! ATTENTION: pour bien vous montrer que modifier un pointeur est une mauvaise idée, tentez de faire ceci: int main(void) { char *ptr; ptr = malloc(300000 * sizeof(char)); if (ptr == NULL) { my_putstr("Erreur malloc\n"); return ; } ptr = "coucou"; free(ptr); } 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 allouée. Pour vous montrer que mon code fonctionne, je vais afficher ma chaine de caractère, qui fera l'alphabet avec 300 000 lettres! 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); } Et voilà, vous savez à présent utiliser les allocations dynamiques! A bientôt pour le prochain cours! Cours écrit par AlexMog. Contact: alexmog [at] live [point] fr 2
Azad Posté(e) May 23, 2014 Signaler Posté(e) May 23, 2014 J'adore tes tutoriels : longs (très longs et tant mieux), complets et fournis. Tu mérites tout à fait ton grade. +1 rep.
davydavek Posté(e) May 24, 2014 Signaler Posté(e) May 24, 2014 J'ajouterais aussi que Valgrind-Memcheck est très utile pour détecter les erreurs liées a l'allocation dynamique. Pour l'utiliser : valgrind ./votreprogramme Et avec un makefile c'est encore plus simple
AlexMog Posté(e) May 25, 2014 Auteur Signaler Posté(e) May 25, 2014 J'ajouterais aussi que Valgrind-Memcheck est très utile pour détecter les erreurs liées a l'allocation dynamique. Pour l'utiliser : valgrind ./votreprogramme Et avec un makefile c'est encore plus simple L'utiliser pour les memory leaks et aussi très intéréssente . J'ai déjà cité Valgrind dans le cours #0 justement . GDB est aussi parfait pour ce genre de travail.
Recommended Posts
Veuillez vous connecter pour commenter
Vous pourrez laisser un commentaire après vous êtes connecté.
Connectez-vous maintenant