Aller au contenu

Leaderboard

Popular Content

Showing content with the highest reputation on 05/06/2014 in all areas

  1. Bonjour à tous, Dans ce cours, nous allons voir ce qu'est une liste chainées, et comment elle fonctionne. I- Liste chainée? WTF? D'après Wikipedia: "Une liste chaînée désigne en informatique une structure de données représentant une collection ordonnée et de taille arbitraire d'éléments de même type". Pour résumer, il s'agit d'une collection de données (comme un tableau, sisi), qui a une taille variable, donc on a pas besoin de connaitre la taille de la liste à l'avance (un peu comme dans le cours sur les allocations dynamiques ). Les éléments sont enregistrés les uns après les autres. C'est donc une grosse liste de données. Le premier élément pointe vers le second, le second vers le troisième, le troisième vers le quatrième etc... Comme le montre le schéma ci-dessous: (Source: Openclassroom) C'est une liste chainée simple, et nous ne verrons que celle-ci. Pour la liste chainée circulaire, ou encore double, je vous laisse chercher par vous mêmes II- Créons notre structure de données! Une liste chainées à pour but de contenir des données (c'est logique...), il faut donc créer une structure pour stocker lesdites données. Pour ma part de vais créer une liste pour stocker l'age et la taille d'un certain nombre d'utilisateurs. typedef struct s_list { int taille; int age; struct s_list *next; }t_list; Oh! c'est étrange, qu'est-ce que la variable "next" vient faire ici? Comme je l'ai dit plus haut, il s'agit du pointeur sur le prochain élément de notre liste chainée. Il va falloir penser à le mettre à NULL pour connaitre la fin de notre liste chainée! En effet, lorsqu'on sera au dernier élément, on le sera grâce au NULL. Dans ce cours, je vais uniquement vous apprendre à ajouter des éléments à votre liste, à parcourir votre liste, et enfin à supprimer votre liste. C'est à vous d'utiliser votre tête pour la suite! III- Ajouter un élément à la liste. Maintenant que notre structure est crée, nous allons créer plusieurs fonctions, pour gérer notre liste chainée. Créons tout d'abord une fonction pour ajouter un élément à la liste: int add_to_list(t_list **list, t_list *datas) { t_list *next; next = NULL; if (*list != NULL) next = *list; if ((*list = malloc(sizeof(t_list))) == NULL) return (1); (*list)->taille = datas->taille; (*list)->age = datas->age; (*list)->next =next; return (0); } Voici un exemple d'utilisation de la fonciton: int main(void) { t_list *list; t_list datas; *list = NULL; datas.age = 10; datas.taille = 180; // On ajoute à notre liste if (add_to_list(&list, &datas)) return (1); // On récupère le premier noeud pour voir ce qu'il contient printf("Age: %d, Taille: %d\n", list->age, list->taille); return (0); } III- Parcourons notre liste! Nous allons à présent parcourir notre liste. int main(void) { t_list *list; t_list datas; int i; i = -1; // On remplis notre liste avec 10 éléments while (++i < 10) { datas.age = i; datas.taille = 180 + i; if (add_to_list(&list, &datas)) return (1); } // On parcours et on affiche notre liste! while (list != NULL) { printf("Age: %d, Taille: %d\n", list->age, list->taille); list = list->next; } return (0); } IV- Vidons notre liste! (C'est important de vider la mémoire!) Pour cela, créons une fonction de vidage. void clear_list(t_list **list) { t_list *elem; t_list *next; elem = *list; while (elem) { next = elem->next; free(elem); elem = next; } *list = NULL; } Exemple d'utilisation: int main(void) { t_list *list; t_list datas; int i; i = -1; // On remplis notre liste avec 10 éléments while (++i < 10) { datas.age = i; datas.taille = 180 + i; if (add_to_list(&list, &datas)) return (1); } // On parcours et on affiche notre liste! (on utilise un pointeur annexe pour ne pas perdre le noeud mère) list *elem = list; while (elem != NULL) { printf("Age: %d, Taille: %d\n", elem->age, elem->taille); elem = elem->next; } clear_list(&list); return (0); } Je vous laisse la joie d'analyser le code, à ce stade des cours (et si vous avez bien tout suivi) vous savez analyser le code, je vous laisse donc le comprendre par vous mêmes! Je reste disponible pour répondre à vos questions! A bientôt pour un prochain cours!
    1 point
  2. Salut à tous, Je vais vous présenter ma petite galerie de créations Gource. Avant tout, qu'est-ce que Gource? Gource permet de créer une manière artistique de voir les modifications apportées sur un projet via des gestionnaires de versionning (git, svn, etc...), on voir donc, au fil du temps les fichiers créés, et les améliorations apportées à un projet. Je vais donc faire une petite galerie Gource de mes projets, si vous souhaitez voir un peu comment se passe la création d'un programme/jeu . Melichat de la version 001 à la version 092: KillDaMinions - Développement complet: Je vous en posterais d'autres dès que possible Enjoy! Et si d'autres dév s'y mettent, je serais ravis de voir ça Cordialement, AlexMog.
    1 point
  3. Salut à vous tous ! Aujourd'hui je vous présente un app design que j'ai créé pour Melinyel. Je ne l'ai pas encore fini mais voici des aperçus : Accueil : Inscription : Connexion : Voilà
    1 point
  4. C'est un peu tard : "Cependant, l'offre est limitée dans le temps, il faudra avoir ajouté le titre à votre ludothèque avant le 5 juin à 18 heures.".
    1 point
  5. WTF ? https://www.youtube.com/watch?v=NwQF_x0HrFA
    1 point
  6. Bonjour a tous ! Tous d'abord qu'est ce que le GOAP ? C'est l'abréviation de Goal Oriented Action Planning, ce qui pour les non anglophone, veut dire la planification des actions en fonction de tes buts, ou de tes souhaits. C'est un type d'IA qui a pour but de remplacer les FSMs (Finite State Machine), les automates a états finis en gros. (Le mot anglais est plus classe et plus court a écrire donc c'est celui là que je vais utiliser maintenant). En effet, les FSMs sont bêtes et méchantes, bien que facile a réaliser. Mais dès qu'il s'agit d'un comportement où il faut que l'IA ai l'air de réfléchir, les FSMs sont inadaptées. Ainsi les GOAPs sont utilisés principalement dans les FPS, et aussi dans le mondialement connu Starcraft. Cette IA a pour but d'offrir plus de challenge au joueur. Mais rien ne vous empêche de faire une IA bien plus intelligente dans les autres types de jeu comme les RPG, ou les rogues like. Le principe des GOAP maintenant : Un pnj a différents but, comme manger, dormir, travailler etc... chaque but a un niveau d'importance plus ou moins grand. schéma : but : importance dormir : 4 manger : 2 travailler : 1 Ici par exemple le pnj a très envie de dormir. Les niveaux d'importance sont mis a jour au fil du temps et les actions les plus importantes sont exécutées en premier. En code : typedef struct t_state { unsigned char id; unsigned char importance; }t_state; t_state but[nombre_de_but_different]; /* on initialise le tout int i; for( i = 0 ; i < nombre_de_but_different ; i++ ) { but[i].id = i; but[i].importance = 0; } L'id sert a savoir le but ( par exemple dormir c'est 0, manger c'est 1 et travailler c'est 3) parce qu’à chaque fois que l'importance d'un but change, il faut retrier le tableau. Regardez du coté des algorithme de tri, et n'oubliez pas que le tableau est presque trier, seul un élément est a sa mauvaise place. Une fonction de tri en exemple (oui c'est utile de faire cela) void tri(t_state but[], int nb_but) { /* Il s'agit ici d'un tri à bulle qui marche, mais qui est le plus lent possible c'est pour vous forcer à regarder du côté des algo de tri*/ int changement = 1; int i; t_state transition; while(changement) { changement = 0; for(i = 0 ; i < nb_but - 1 ; i++) { if(but[i].importance < but[i+1].importance) { transition.importance = but[i].importance; transition.id = but[i].id; but[i].importance = but[i+1].importance; but[i].id = but[i+1].id; but[i+1].importance = transition.importance; but[i+1].id = transition.id; changement = 1; } } } } On regarde donc le premier élément du tableau, qui est le plus important, et on exécute l'action associé. Et la vous allez me dire : "mais alors c'est la même chose que les FSMs non ?" Et bah non : les état ne sont plus reliés entre eux, donc l’insertion d'un nouveau but ne nécessite pas de tout revoir, mais en contre partie, cela nécessite plus de temps de processeur pour faire cela. Et aussi, une grande nouveauté : pourquoi ce contenter d'UNE action par but ? Si vous avez faim, vous pouvez vous faire vous même à manger, aller chez quelqu'un, ou encore aller dans un fast-food. il y a donc plusieurs solutions possibles. Pour chaque action, il suffit de déterminer son poids : Celui-ci peut varier en fonction de plusieurs facteurs, comme le temps mis à faire l'action, le prix que cela va nous coûter etc... Et il faut aussi déterminer l'importance que vont perdre les but. Par exemple : fastfood : manger - 2 maison : manger - 4 On peut aussi choisir une des deux actions en fonction du poids en utilisant le principe des algorithmes de Dijkstra (parcours de graphe), qui est le même principe que le pathfinding. L'avantage, c'est de pouvoir avoir une plus grande flexibilité. Il faut alors voir les actions comme un enchaînement d'actions, et plusieurs actions peuvent menées a la même suivante. Le poid peut être en fonction de plusieurs choses, comme le temps, et l'importance que cela va perdre. Par exemple, si l'on a très peu de temps et on est juste à coté du fast-food, alors on va y allez, et si on a tous notre temps, et juste à coté de chez soi, autant allez à sa maison. Le poid peut aussi être influencer par l'argent dépensé etc... exemple : /*on calcule le poid de chaque action*/ poid[nb_action_differentes]; int i; for ( i = 0 ; i < nb_action_differente ; i++) { poid[i] = (temps * 0,5) /* plus le temps est court, mieux c'est. 50% d'importance pour le temps*/ + (diminution_de_l_importance * 0,5); } je suis parfaitement conscient que le temps a un poid totalement aléatoire en fonction de l'unité utilisé. C'est à vous de calibrer correctement le calcul du poid. il ne s'agit ici que du principe général. Dans ce cas, plus le poid est grand, plus le temps pris est long. Attention, la variable diminution_de_l'importance est négative (-2, -4 etc...) ! int meilleur_poid; int meilleur_poid = poid[0];/*C'est très important d'initialiser !*/ int meilleure_action; for ( i = 0 ; i < nb_action_differente ; i++) { if(poid[i] < meilleur_poid) { meilleur_poid = poid[i]; meilleure_action = i; } } et on fait l'action : action[meilleure_action](); Je considère ici, que l'on envoie rien a la fonction, mais vous pouvez facilement le faire. Le tableau est un tableau de pointeur de fonctions (voir le tutoriel d'AlexMog à ce sujet). Pensez à updater ce tableau régulièrement tout les X secondes ou millisecondes, car une action peut à un moment être plus utile que celle que vous voulez faire, alors qu'avant, elle ne servait à rien. Vous avez donc maintenant réussi a faire un pnj qui pourra avoir des buts et faire des actions en conséquence. N'oubliez pas pas que plus vous augmenter le nombres de buts et d'actions, plus le pnj sera réaliste, et plus cela prendra de temps pour "réfléchir"(pas beaucoup non plus mais sur des milliers de pnjs, cela se ressent). Par exemple, le FPS F.E.A.R (j’insiste sur le mot FPS là !) possède 67 buts différents (pas un seul où il le but est de tuer l’ennemi ) et 197 actions différentes.
    1 point
  7. boila disque dur reçu aussi Aliexpress m'a fait faire des économies
    1 point
  8. Le PHP est un langage de programmation web permettant d'insérer du contenu dynamique dans vos pages. Ce langage offre de nombreuses possibilités telles que la gestion de news ou encore l'envoie d'email et combiné à une base de données, ses performances sont considérables. PRÉSENTATION DES BASES DE DONNÉES : Les bases de données vont vous permettre de stocker de nombreuses données et d'interagir avec simplement mais pour cela, il vous faut un minimum de connaissances que nous traiterons dans ce guide. Interactions entre clients, serveur et base de données. Les bases de données se divisent en tableaux (aussi appelés table) : INTERACTIONS ENTRE PHP ET LES BASES DE DONNÉES : Je vous conseille d'avoir un minimum de connaissances sur le langage SQL pour maîtriser les interactions mais les bases seront rappelées. Dans ce cours, nous utiliserons l’interface PDO pour accéder aux bases de données. Tout d’abord, il nous faut établir une connexion avec le serveur MySQL (dans ce cas mais nous pouvons évidemment changer de SGDB) : try { $bdd = new PDO('mysql:host=localhost;dbname=test', 'root', ''); // On ouvre une instance avec "PDO" /* ** "host" : nom de domaine du serveur SQL, ** "dbname" : nom de la base de données, ** Dans les deux derniers arguments, vous devez entrer dans cette ordre votre nom d'utilisateur et votre mot de passe de votre BDD */ } catch(Exception $e) { die('Erreur : '.$e->getMessage()); } Note : Ici j’ai utilisé une exception qui nous permettra de récupérer les erreurs et de les afficher clairement à l’écran. Maintenant que notre connexion est établie, nous pouvons exécuter des requêtes sur notre base de données. Pour cela, vous avez le choix entre deux fonctions : PDO::query : Exécute une requête SQL, retourne un jeu de résultats en tant qu'objet PDOStatement. PDOStatement::execute : Exécute une requête précédemment préparée avec PDO::prepare (Prépare une requête à l'exécution et retourne un objet). Ces fonctions permettent de récupérer des données maintenant, il nous faut quelques fonctions permettant d’agir sur ces données et aussi de les sécuriser pour éviter une injection SQL : PDOStatement::bindValue : Associe une valeur à un paramètre. PDOStatement::bindParam : Lie un paramètre à un nom de variable spécifique. PDOStatement::bindColumn : Lie une colonne à une variable PHP. PDOStatement::fetch : Récupère la ligne suivante d'un jeu de résultats PDO. PDOStatement::fetchAll : Retourne un tableau contenant toutes les lignes du jeu d'enregistrements. PDOStatement::fetchColumn : Retourne une colonne depuis la ligne suivante d'un jeu de résultats. PDOStatement::setFetchMode : Définit le mode de récupération par défaut pour cette requête. PDOStatement::rowCount : Retourne le nombre de lignes affectées par le dernier appel à la fonction PDOStatement::execute. Maintenant, on va écrire une requête SQL mais avant quelques rappels : Sélectionner des données : « SELECT attribut_1, attribut_2 FROM nom_table » (Pour sélectionner le tableau entier, remplacez les attributs par *). Créer une nouvelle ligne : « INSERT INTO table_name(attribut_1, attribut_2) values(valeur_1, valeur_2) ». Mise à jour d’une ligne : « UPDATE nom_table SET attribut_1 = valeur_1, attribut_2 = valeur_2 ». Suppression d’une ligne : « DELETE FROM nom_table WHERE attribut = valeur » (à noter que WHERE peut être utilisé avec n’importe quelle autre fonction vu précédemment). /* CONNEXION AU SERVEUR SQL ICI */ $query = $bdd->prepare('SELECT attributs FROM my_db WHERE attribut = :value'); // Préparation d'un requête de sélection (ici :value est un paramètre de la requête SQL qu’on va associer à une valeur ensuite) $query->bindValue(':value', $value, \PDO::PARAM_STR); // On associe la valeur au paramètre (ce qui évite les injections SQL) $query->execute(); // On exécute la requête $data = $query->fetchAll(); // On associe à un tableau les résultats obtenus avec la requête $query->closeCursor(); // On ferme notre requête
    1 point
  9. "tu prends Adobe After Effect (légalement biensur), tu cliques partout, c'est le meilleur moyen d'apprendre " Ou sinon tu fais comme moi : tu prends Blender et Gimp tous les deux gratuits, tu va regarder du design et motion design sur internet et tu essaye de faire la même chose avec les moyens du bord. C'est comme ça que j'ai fait et cela m'a permis d'arriver à faire quelque chose de façon totalement gratuite (bon... ok j'utilise Adobe Première Element pour le montage... Mais le plus important n'est-il pas l'animation et le design ?)
    1 point
  10. 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 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: 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); } Nous butons à présent sur un problème, ré-utilisons le programme précédent, mais supprimons tous, ou un des arguments: 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
    1 point
  11. Bonjour à tous, Ce cours va être très court (oh ce jeu de mot!). En effet, je vais juste vous montrer comment faire un pointeur sur fonction. I- Pointeur sur fonction? Kesako? Un pointeur sur fonction, c'est simplement une manière d'appeler une fonction, en passant par un pointeur. Par exemple: void (*function_pointer)(char); function_pointer = &my_putchar; // Vous pouvez retrouver la fonction my_putchar sur le cours #1. function_pointer('A'); Le programme ci-dessus crée le pointeur sur fonction function_pointer, et lui définit l'adresse de my_putchar (qui permet d'afficher un caractère). Il affiche 'A'. II- Utilité? Un pointeur sur fonction permet d'appeler une fonction via un pointeur. Il est très souvent utilisé pour les tableaux de pointeurs sur fonction. Ce qui permet, par exemple, d'appeler une fonction en fonction d'une condition, sans avoir 40 000 if/else. Exemple de tableau de pointeurs sur fonction: #include <unistd.h> void my_putchar(char c) { write(1, &c, 1); } void my_putstr(char *str) { while(*str) my_putchar(*(str++)); } void aff_help() { my_putstr("Commandes:\ncoucou: affiche salut\ndada: affiche dadi\npopo affiche caca"); } void aff_coucou() { my_putstr("salut\n"); } void aff_dada() { my_putstr("dadi\n"); } void aff_popo() { my_putstr("caca\n"); } void do_cmd(char *cmd) { void (*fptr[])() = {&aff_popo, &aff_dada, &aff_coucou}; char cmds[] = {"popo", "dada", "coucou"}; int i = -1; while (++i < 3) { if (!strcmp(cmds[i], cmd)) { fptr(i)(); return ; } } my_putstr("Commande introuvable."); } int main(int ac, char **av) { int readed; char buffer[4096]; my_putstr("Entrez votre commande. Utilisez CTRL+D pour quitter\n"); while ((readed = read(0, buffer, 4095)) != -1) { buffer[readed] = '\0'; do_cmd(buffer); } return (0); } III- Déclaration et appel. Enfin, voyons comment déclarer un pointeur sur fonction, puis comment lui définir une fonction. Déclarons notre pointeur. Forme de déclaration: typage_renvoyé_par_la_fonction (*nom_pointeur)(arguments_pris_par_la_fonction); Par exemple, pour printf: int (*myptr)(char *, ...); // oui, les ... sont un type. Il existe une fonction qui permet de récupérer une infinité d'args dans une fonction, mais je vousl’expliquerais dans un prochain cours! // Assignation myptr = &printf; // Utilisation myptr("Salut tout le %s", "monde!\n"); Voilà. Vous l'aurez compris, les pointeurs sur fonctions sont très, TRES utilisés dans le monde du développement. Il est donc important de savoir les utiliser, pour apprendre à gagner des lignes. Je vous dis a bientôt pour un prochain cours! Cours écrit par AlexMog. Contact: alexmog [at] live [point] fr
    1 point
  12. Bonjour à tous, et bienvenue dans ce premier cours dédié au C. Dans ce cours, nous allons voir pourquoi il est important de comprendre et de programmer en C, puis nous passeront à une partie plus pratique en regardant les outils qui nous sont proposés pour cela. I- Pourquoi apprendre le C? Le C est un langage le plus bas niveau qui existe (après l'ASM bien entendu!), il permet donc de mieux comprendre le fonctionnement de sa machine. Bas niveau? Cela signifie qu'il est simple? Bas niveau ne corresponds pas au niveau de simplicité du C. Il s'agit en faite d'un niveau par rapport à la machine. Plus un langage est bas niveau, plus il sera proche du fonctionnement de la machine, donc difficile. Plus il sera bas niveau, plus il nous permettera de comprendre le fonctionnement de la machine. Nous allons donc avoir plusieurs cours, en ayant pour but de comprendre comment fonctionne notre machine. Pour cela, nous allons nous orienter vers une programmation impérative que nous permet le C, en nous forçant à n'utiliser QUE des appels system (Donc pas de libc! (à titre informatif: c'est la méthode Epitech)). Nous seront donc obligés de re-créer nos propres outils de travail! Vous l'aurez compris: les prochains cours ont pour but de vous apprendre à utiliser de façon extrême le C. Sans utiliser les outils déjà mis à disposition (libs, etc...), pour vous apprendre à comprendre comment ceux-ci fonctionnent! II- Les outils pour programmer en C Le tout premier outils que je vous conseille, c'est d'utiliser LINUX. Pourquoi Linux? Eh bien c'est très simple: Linux propose énormément d'outils de débugage, et beaucoup d'outils de programmation (Make, Valgrind, GDB, Emacs etc...). Ainsi qu'une documentation détaillée des appels system et de la libc (grâce notamment à la commande "man"). Enfin, une protection plus rapprochée du système qui vous évitera de mauvaises suprises lorsque vous jouerez avec la mémoire (je conseille fortement d'utiliser les limitations de ram par users pour éviter les forkbombs et autres malloc infinis). Je vous conseille donc fortement d'utiliser Linux. De mon côté, j'ai opté pour un Debian sur lequel j'ai installé une interface graphique et tout les outils nécéssaires! Je conseille fortement Mint qui est basé sur Debian. Vous pouvez utiliser une machine virtuelle pour faire tourner votre Linux si vous ne souhaitez pas le rajouter au Dump! Ensuite, les outils de base: Nous allons programmer en utilisant 5 outils très importants: Emacs: C'est l'éditeur qui va nous permettre d'éditer nos codes en C. Il est très pratique, mais il faut connaitre les syntaxes et les racourçis qu'il propose! GDB: C'est un excellent débugueur qui permet de fixer des points d'arrêt sur son programme, pour réussir à trouver les problèmes de notre programme! Valgrind: C'est un de mes outils préférés: Il permet de savoir si vous avez bien free tout vos malloc (et donc, bien vidé toute la mémoire) et de repérer les segfaults (à notter: il faut rajouter -g3 aux flags de compilation pour que celui-ci marche pleinement, n'oubliez surtout pas de supprimer -g3 de vos makefiles à la version finale. -g3 rends l'exécutable très lourd). Make: L'outil Make est l'un des plus importants: il nous permettra de créer un "Makefile" qui va nous permettre, grâce à la commande Make, de compiler, et nétoyer notre programme facilement. htop ou top: C'est un invité de commandes pour unix, qui va nous permettre de voir l'avancement et l'utilisation de notre RAM/CPU. Voilà, je vous es donc fait une courte introduction sur tout ce qui es des outils et de la programmation en C! Rendez-vous au prochain cours! Cours écrit par AlexMog. Contact: alexmog [at] live [point] fr
    1 point
  13. Bonjour à vous, Dans ce cours, nous allons voir à quoi corresponds un fichier header, ainsi que comment créer une librairie, et enfin, les structures! I- Qu'est-ce qu'une lib? Une lib, aussi nommée librairie (dynamique ou statique) est un fichier binaire contenant une liste de fonctions prédéfinies. Je vais vous donner un exemple de son utilisation, et pourquoi il est bon de créer une lib! Imaginons que vous avez créé plusieurs fonctions, il vous serait pratique de les ré-utiliser dans un autre programme! Malheureusement, la compilation est très longue si il y a beaucoup de fonctions. La lib est donc là pour deux choses: partager des fonctions sur diverses programmes, et éviter une compilation inutile sur des fonctions déjà près compilées. Pour mieux vous faire comprendre tout cela, créons trois fichiers distincts: main.c: int main(void) { my_putstr("coucou\n"); return (0); } my_putchar.c: void my_putchar(char c) { write(1, &c, 1); } my_putstr.c: void my_putstr(char *str) { while(*str) my_putchar(*(str++)); } Tentons à présent de compiler main.c: Hum.. notre linker nous gueule dessus en nous disant qu'il est impossible de trouver la fonction "my_putstr". C'est normal! my_putstr n'a pas été compilée avec notre main. Elle ne fais donc pas partie du programme. Maintenant, tentons de compiler tous nos fichiers en même temps: Ah! Là ça marche! On a notre a.out qui est bien créé, et qui affiche bien "coucou".C'est un bon point, mais nous ne sommes pas avancés, j'ai envie de réutiliser my_putchar et my_putstr dans mes autres programmes, je vais tenter des les compiler pour voir si je peux les ré-utiliser sans avoir à les recompiler! Hum... Mon linker me dit cette fois-ci qu'aucun main n'a été trouvé... Je ne peux donc pas compiler mes fonctions séparément? Eh bien si! Il a une solution, les transformer en fichiers binaires non exécutables (aussi appelées lib), leur extentions est généralement .a pour un rassemblement de mini-libs, et .o pour les fichiers de fonctions de cette lib (pour résumer, les .o sont les briques, et .a est une partie du mur ! On utilise les briques pour construire cette partie). Créons donc nos .o! (man cc pour plus d'informations) Voila! Je découvre my_putchar.o et my_putstr.o, tentez la commande "file my_putchar.o" pour voir qu'il s'agit bien d'un fichier binaire non exécutable. C'est cool, mais pour l'instant mes fichiers sont séparés, j'aimerais bien tous les avoir dans un même "packetage" pour éviter de me trimballer 40 fichiers par programmes. Eh bien, nous allons utiliser notre linker (et non plus notre compilateur) pour tous les liés dans un même package, un véritable fichier lib: un fichier .a. Nous l'appellerons libmy.a: Pour créer notre lib, voici la commande linker (je vous laisse le soin de lire le man de celle-ci): On peux en faite considérer les .o comme des briques, et les .a comme le ciment qui va maintenir ces briques sur le programme. Nous avons enfin notre lib! Tentons à présent de compiler main.c avec notre lib (je vous laisse, encore une fois, le soin de lire le man de gcc): Hop! Nous avons notre a.out fonctionnel! Vous savez à présent créer vos propres librairies! II- Les fichiers Header Nous avons vu précédemment comment créer notre propre librairie, mais néanmoins, nous avons, de temps à autres, des warnings qui apparaissent lors de notre compilation, voir même des erreurs car notre compilateur ne connaît pas les fonctions qu'il utilise.Les fichiers Headers ont plusieurs particularités. Il s'agit avant tout de fichiers d'"entête" chargés de fournir des informations au compilateur pour qu'il sache où chercher les fonctions, et leur utilité dans le programme. Il permet aussi de rendre le code plus propre: nous pourrions faire nos headers directement dans notre .c, mais c'est plus moche, et la compilation d'un .h diffère de celle d'un .c (tentez cc *.h vous verrez bien). Un header, c'est donc un fichier de préparation à la compilation. Il contiendra ainsi les prototypes des fonctions utilisées dans notre programme. Vous pouvez trouver des exemples de headers dans les librairies que nous avions déjà utilisé! Souvenez-vous: Nous avions utilisé: #include <unistd.h> qui corresponds à inclure le fichier .h de la libraire unistd. les <> signifient que le header se trouve dans le dossier include de notre compilateur. Pour inclure un fichier local, il suffit de faire tout simplement! Donc revenons sur notre histoire de warnings, nous allons créer un .h pour la lib précédente que nous avons créé: #ifndef _MY_H_ #define _MY_H_ void my_putchar(char); void my_putstr(char *); #endif /* _MY_H_ */ Je vais vous expliquer le fichier lignes par lignes. Mais avant cela, je vais vous expliquer ce qu'est un define. Un define est une sorte de variable statique et constante. Elle permet de remplacer la valeur du define par la valeur associée à celui-ci. Voici un exemple simple: #define VERSION "1.0.0" J'ai définit VERSION comme ayant comme value 1.0.0 Je peux le ré-utiliser dans mon code, si j'inclus le fichier .h qui contient ce define, comme ceci: int main(void) { my_putstr(VERSION); return (0); } ATTENTION: il ne s'agit pas d'une variable. Un define est remplacé par le compilateur par la valeurqui lui est destinée! J'espère que vous m'aurez compris... Passons donc à la définition de notre fichier .h: #ifndef _MY_H_ Notre première ligne de code permet d'éviter ce qu'on appelle une "double inclusion", en effet, si on inclue notre fichier .h dans plusieurs autres fichiers du même programme, nous pouvons créer de multiples inclusions, ce qui ne sert à rien, et ralentit la compilation. #define _MY_H_ Si notre fichier n'a jamais été inclus, nous définissons qu'il l'a à présent été void my_putchar(char); void my_putstr(char *); On y ajoute ensuite les prototypes des fonctions utilisées... #endif /* _MY_H_ */ Enfin, on ferme notre "#ifndef" (if not defined). Nous y sommes, revoyons notre main à présent: #include "my.h" int main(void) { my_putstr("coucou\n"); return (0); } Compilons le: Et voilà! Plus aucun warnings ou erreurs! Nous pouvons passer à la suite! III- Les Structures. Une structure est un ensemble de données. Il permet de stocker plusieurs données sous un même typpage. (nous verrons en même temps la déclaration de typpages, grâce à typedef). Construction d'une structure: struct s_nom_de_ma_struct { int valeur_numerique; char valeur_caractere; typpage valeur_n'importe_quel_typpage; }; C'est aussi simple que cela. A savoir : Une structure doit être déclarée dans un .h!Nous allons créer une structure, qui contiendra une chaine de caractères "pseudo" et une autre chaine de caractère "texte": struct s_mastruct { char *pseudo; char *texte; }; Nous allons afficher les données de notre structure: void set_structure(struct s_mastruct *mastruct) { /* (*mastruct).pseudo = "AlexMog" est la même chose que mastruct->pseudo = "AlexMog" */ mastruct->pseudo = "AlexMog"; mastruct->texte = "Coucou! :)"; } int main(void) { struct s_mastruct mastruct; set_structure(&mastruct); my_putstr("Pseudo: "); my_putstr(mastruct.pseudo); my_putchar('\n'); my_putstr("Texte: "); my_putstr(mastruct.texte); my_putchar('\n'); return (0); } Ce code nous affichera: A SAVOIR: lorsque vous utilisez un pointeur sur structure (comme dans la fonction set_structure) les "." sont remplacés par "->". Voilà, vous savez à présent vous servir des structures. Mais vous avez remarqué que taper "struct s_mastruct mastruct" est tout de même long pour la ré-utilisation de cette structure... Nous allons donc créer notre propre typage! Rendez-vous dans le .h: struct s_mastruct { char *pseudo; char *texte; }; et créons notre nouveau typage: typedef struct s_mastruct { char *pseudo;char *texte; } t_mastruct; Nous pouvons à présent déclarer nos structures comme ceci: Cool n'est-ce pas ? Vous remarquerez, pour ceux qui font de la programmation orientée objet, qu'une structure ressemble énormément à un objet, il s'agit en faite de la maman de l'objet connu actuellement! Nous verrons cela plus en détails, lorsque j'aborderais les notions sur la programmation en C Modulaire dans un prochain cours (dans longtemps donc, puisque nous devons voir les pointeurs sur fonctions avant (un objet = une structure contenant un ensemble de données et de pointeurs sur fonctions allouée dans la mémoire)). Voilà! A très bientôt pour un prochain cours! Cours écrit par AlexMog. Contact: alexmog [at] live [point] fr
    1 point
  14. Bonjour à tous, Nous passons encore à un autre niveau aujourd'hui, et nous allons voir ensemble la Récursivité, et la notion de Stack. I- La récursivité, c'est quoi? La récursivité, c'est un autre moyen de provoquer une "boucle" dans une fonction. C'est totalement différent de ce que je vous ai expliqué avant. Nous avions vu la partie "itérative" du C, qui corresponds à exécuter un programme, ligne par ligne. Ici, nous allons apprendre un peux plus les fonctions de la récursivité, et comment elle réagit sur la stack. C'est une façon de faire, pour qu'une fonction se rappelle elle-même. Prenons l'exemple suivant: int test(int a) { a++; if (a < 12) test(a); return (a); } int main(void) { my_putnbr(test(1));// my_putnbr est une fonction permettant d'afficher une valeur numérique. Vous devez la re-créer ou utiliser printf (ce qui est interdit par la norme! Re-créez la, ça vous apprendra pas mal de choses!) } La fonction "test" est ici récursive. Ce code nous affichera: 12 Vous l'aurez compris, la récursivité peut être utile dans plusieurs cas (pour annecdote, my_putnbr peut être codé en 3 lignes avec de la récursivité). Les fonctions récursives peuvent êtres comparées à des poupées russes s'emboitant. Ne vous perdez pas! Et ne vous inquiétez pas! Je vais mieux vous l'expliquer en vous expliquant le fonctionnement de la stack. II- La stack? DAFUQ? Je vais pouvoir vous expliquer une notion qui est assez floue dans le cerveau de beaucoup de développeurs: la stack. La stack est une mémoire assignée à votre programme pour la prise en charge de tout ce qui est "static" dans votre programme (d'où le nom "stack"). Lors du lancement de votre programme, la stack est vide. Si vous appelez la fonction "test" celle-ci va se rajouter dans la stack. Si, de la fonction "test", vous appelez la fonction "my_putstr" celle-ci va se rajouter dans la stack, de même pour la fonction "my_putchar" contenue dans la fonction "my_putstr" qui fera elle-même appel à la fonction "write" qui se rajoutera à son tour à la stack. La stack a donc constitué une liste d'exécution. On peut re-définir l'ordre d'exécution précédent comme ceci: Il ne faut pas oublier que la stack est une mémoire, et qu'elle va stocker tout ce qui est statique dans notre programme. Donc, si nous la sur-utilisons (une boucle infinie de fonctions par exemple: surempiler les poupées russes), nous risquons de faire segfault (segmentation fault) notre programme (C'est souvent une explication pour les programme qui segfault sans raisons). Lorsqu'une fonction finit son exécution, elle est supprimée de la stack. Pour vous faire un schéma, imaginez un tas de vaisselle: à chaque fois, vous rajoutez une assiette sale sur le tat, et lorsque vous faites la vaiselle, vous enlevez vos assiettes dans l'ordre contraire de celui de l'empilation. Reprenons la théorie: Une fonction récursive est une fonction qui se rappelle elle-même. Elle se rajoute donc sur la stack, puis se rappelle. Elle se rajoute donc encore une fois sur la stack, puis se rappelle...etc... Et là, deux choses peuvent avoir lieu: Soit on atteint la taille maximum de la stack (définie par le système), et on provoque un segfault, sinon, et c'est ce que vous devrez faire la plupart du temps en utilisant les récursifs, vous devez prévoir une condition d'arrêt du rappel de cette fonction, donc à un moment de votre récursivité, vous dites STOP, cette fois je ne me rappelle pas, car mon rôle est terminé. A ce moment là, vous allez libérer la stack de toutes les fonctions que vous avez au préalable ajouté. Le mieux, reste encore de vous montrer un exemple de ce qu'il ne faut pas faire: Créons un programme qui va afficher "hello" indéfiniment: void my_putchar(char c) { write(1, &c, 1); } void fg() { my_putchar('h'); my_putchar('e'); my_putchar('l'); my_putchar('l'); my_putchar('o'); my_putchar('\n'); fg(); } int main(void) { fg(); } Effectivement, on voit "hello" s'afficher plusieurs fois, mais si on laisse tourner notre programme jusqu'à ce que la stack soit remplie, on remarque de notre programme crash, et qu'un segfault est apparu. C'est l'exemple typique de ce que l'on peut attendre au niveau des problèmes liés à la récursivité. Un autre exemple, c'est notre légendaire my_putnbr: void my_put_nbr(int nb) { if (nb <= 9 && nb >= 0) my_putchar(nb + '0'); else { my_put_nbr(nb / 10); my_put_nbr(nb % 10); } } c'est typiquement la bonne utilisation de la récursivité. Voilà, j'espère vous avoir encore aidé au niveau de votre apprentissage avancé du C. Rendez-vous au prochain cours! Cours écrit par AlexMog. Contact: alexmog [at] live [point] fr
    1 point
  15. Bonjour à tous, Aujourd'hui, nous allons apprendre à assimiler la notion de pointeurs, de tableaux et de chaines de caractères. Je vais tenter de vous expliquer le fonctionnement d'un pointeur par des exemples variés. Pourquoi? Eh bien, par mon expérience, la notion de pointeurs est une chose qui m'a fait fuir à l'époque ou j'avais commencé le C, mais uniquement à cause de la difficulté à comprendre le principe. Je vais donc essayer d'être le plus claire possible. Ce qui nous amène donc à la première partie: I- Un pointeur, Kesako? Alors, un pointeur, ça n'a rien de bien méchant. C'est une variable, comme les autres, qui permet de contenir une adresse (généralement, l'adresse d'une autre variable, on parle bien sur des adresses MEMOIRE) Pour mieux comprendre, voyons ce qui se passe lors de la création d'une variable. Lorsque vous créez une variable, de type char par exemple, lors de l'exécution de votre programme, votre programme va prévoir une place en mémoire pour contenir votre variable. Cette place va être reconnue grâce à une adresse, qui peut être contenue dans un pointeur. Il faut se dire qu'un type char permet de contenir 8 bit de données (soit en valeur maximale numérique de 255), et qu'un type int peut contenir jusqu'à 32 bits de données (en fonction de la version de votre machine, sur un 64 bits, il aura 64 bits). (Vous comprenez mieux pourquoi les typages ont été inventés, et pourquoi il est important de bien choisir son type de variable). Donc, lorsque vous lancer votre programme, la machine va allouer un endroit dans la mémoire et lui donner une adresse qui permettera de dire "ICI, la variable X est stockée". Un pointeur va donc contenir l'adresse d'une variable, pour pouvoir modifier le contenu de la dite variable (vous verrez que la gestion d'adresses est importante lorsqu'on va jouer avec les tableaux). Donc, comprenons par l'exemple. Je vais créer un programme, qui définit une variable de type char a, et une autre variable b, qui sera un pointeur sur la variable a. void my_putchar(char c) { write(1, &c, 1); // Voir cours 1 pour cette fonction } int main(void) { char a; // Déclaration de la variable a char *b; // Déclaration du pointeur B, le * permet de définir au PC qu'il s'agit d'un pointeur. a = 'b'; // On assigne une valeur à la variable a. my_putchar(a); // ceci affichera: b b = &a; // On assigne à b l'adresse de a, le signe & permet de définir que l'on récupère l'adresse de a dans b *b = 'c' // On assigne enfin à l'adresse choisie la valeur 'c'. le * définit que l'on modifie le contenu de l'adresse mémoire et NON PAS l'adresse en elle même! my_putchar(*; // On affiche la valeur de b my_putchar(a); // On affiche la valeur de a, et on se rends bien compte qu'elle a changée! return (0); } Vous savez à présent ce qu'est un pointeur et comment l'utiliser. II- Les tableaux statiques: WTF is that shiet? Pour faire simple, un tableau, c'est un "regroupement" de variables du même typage, dont les adresses sont suivies les unes après les autres. Prenons pour exemple une "mémoire" vide: |00| |01| |10| |11| | | | | | | | | Pour l'instant elle ne contient aucune données, déclarons alors notre tableau de char, et ayant 3 de grandeur |00| |01| |10| |11| |yy| |xx| |xx| | | Dans notre cas, yy corresponds au début du tableau, et les xx représentent les valeurs contenues dans ce tableau. Comme vous pouvez le constater, les valeurs se suivent. Donc, en théorie, si on a un pointeur qui pointe sur l'adresse de yy, on peut parcourir notre tableau. On peut donc affirmer qu'un tableau n'est rien d'autre qu'un pointeur qui pointe sur la première valeur de celui-ci. Pour mieux comprendre, imaginons que je déclare un tableau comme ceci: char tab[3]; Lorsque je vais tenter d'accéder à la première valeur, je vais donc faire ceci: tab[0]; Eh bien, vous pouvez aussi y accéder en utilisant directement l'adresse de ce tableau *(tab + (0 * sizeof(char)); Je viens donc de vous démontrer que le contenu des [] n'est que le multiplicateur d'accès à la "case" du tableau. Il suffit d'ajouter à notre pointeur un multiple de la taille de son typage pour accéder à la valeur suivante! La démonstration est faite: Un tableau est un pointeur. Mais attention! Un tableau est un pointeur, mais un pointeur n'est PAS toujours un tableau. III- Les chaines de caractères: Tu voulais pas plutôt dire chaines de vélo?! Nous allons essayer de comprendre ensemble comment fonctionne une chaine de caractères. Et il n'y a pas de meilleurs langages que le C pour vous expliquer le principe même d'une chaine de caractères. Nous avions déjà vu dans le cours #1 qu'une chaine de caractères n'était rien d'autre qu'un tableau contenant, dans chacune de ses cases, un caractère. Par exemple: char tab[6]; tab[0] = 'c'; tab[1] = 'o'; tab[2] = 'u'; tab[3] = 'c'; tab[4] = 'o'; tab[5] = 'u'; le tableau que je viens de déclarer contiendra "coucou". La déclaration ci-dessus est fausse. Le problème étant que lorsqu'on va vouloir afficher cette chaine, on ne saura JAMAIS lorsqu'elle va s'arrêter, et donc, on risquera de provoquer ce qu'on appelle un "segfault" ou "segmentation fault" (ce qui corresponds à une tentative d'accès mémoire qui n'appartiens pas à votre logiciel). Il est donc primordial de dire à votre programme quand est-ce que cette chaine s'arrête. C'est en faite très simple à mettre en place, il suffit de rajouter, dans la dernière case, le caractère '\0' (qui signifie 0 en valeur numérique). Par exemple: char tab[7]; tab[0] = 'c'; tab[1] = 'o'; tab[2] = 'u'; tab[3] = 'c'; tab[4] = 'o'; tab[5] = 'u'; tab[6] = '\0'; Nous venons de définir une fin à notre tableau. Il existe d'autres façon de déclarer un tableau de caractères, comme ceci: char tab[] = "coucou"; Vous savez à présent comment fonctionne une chaine de caractères. Allons encore plus loin à présent. Je vous ai démontré toute à l'heure qu'un tableau était un pointeur. Je peux donc affirmer que je peut utiliser l'adresse principale d'un tableau pour déclarer une chaine de caractères. Voyez plutôt: char *machaine; machaine = "coucou"; Que c'est il passé ici? C'est très simple. Nous avons déclaré un espace de type "tableau de char" dans notre mémoire grâce aux guillemets que nous avons ajouté lors de la définition de la valeur du pointeur "machaine". Le pointeur pointe donc sur l'adresse du premier caractère. (ici, le premier 'c') Notre pointeur va donc pouvoir se comporter comme un tableau, puisqu'un tableau est un pointeur. Voyez par exemple: machaine[0]; //renvois c machaine[4]; //Renvois o machaine[7]; // provoquera un segfault // Ici, je change la valeur d'une des cases par une autre valeur: machaine[0] = 'A';// Ma chaine vaut donc "Aoucou"! // Je peut aussi procéder comme ceci: *(machaine + (0 * sizeof(char)) = 'A'; Voila, je viens de vous inculquer les bases de la gestion de pointeurs et de tableaux. A bientôt pour un prochain cours! Cours écrit par AlexMog. Contact: alexmog [at] live [point] fr
    1 point
  16. Bonjour, je met dans ce sujet tout les liens nécessaires à la compréhension du Dogecoin, son interêt et comment le miner, en attendant de faire plus de sujets. [Tutoriel] Initiation, explication et guide pour miner le Dogecoin. Comment miner du dogecoin ? A compléter.
    1 point
×
×
  • Créer...