Introduction
Bienvenue :) Ce cours va vous servir à découvrir et développer des fondations en programmation informatique !
Le but est de vous apprendre les bases, et d'amener doucement l'abstraction et les raccourcis ensuite. Aucun langage spécifique n'est traité, même si une forte ressemblance au C dans les concepts et les détails peut se faire ressentir.
Chaque section est composée de Niveaux. Certaines sections seront plus copieuses que d'autres, mais ne vous laissez pas avoir. Parfois, il faut plusieurs paragraphes et quelques images pour transmettre une idée qui semble simple, et à l'inverse une seule phrase peut résonner avec vous pour assimiler un concept très abstrait.
A chaque fin de Niveau, une partie Exercice vous attends avec des réponses rédigées pour mieux comprendre les syntaxes, concepts, etc...
important
N'oubliez pas qu'au début, c'est normal d'être perdu dans les explications et de se tromper tout le temps. C'est pareil pour tout le monde au début et même plus tard !
Tout au long du parcours, il est fortement conseillé de vous former sur plusieurs axes :
- Apprendre à chercher les informations, le plus tôt possible, pour devenir autonome et performant
- Apprendre l'anglais pour débloquer de nouvelles ressources et partager vos idées dans une communauté mondiale. Savoir également chercher en anglais est un outil très puissant. Bien que ce cours restera en français pour des soucis d'accessibilité, l'anglais est requis pour atteindre certains niveaux en informatique, et dans la vie !
- Rester curieux et humble
Pour toute question ou feedback sur ce projet, n'hésitez pas à me contacter sur leoleocharreau@gmail.com
Plan du cours
Bien que le cours soit fragmenté sans réelle structure, il est recommandé de suivre l'ordre suivant, et de compléter tous les Niveaux 1. Libre à vous d'apprendre autrement !
- Programmation informatique
- Préparer l'environnement de code
- Apprendre à chercher
- Les variables et les types de données - Niveau 1
- Les opérateurs - Niveau 1
- Les fonctions - Niveau 1
- Les opérateurs - Niveau 2
- Les structures de contrôle - Niveau 1
Préparer l'environnement
Les exercices seront en C et en Python.
Tous les exercices proposés peuvent être fait en codant à la main sur papier, si vous aimez vous faire du mal. Sinon, je vous recommande d'installer à l'aide de ressources externes sur vos machines des outils comme gcc pour pouvoir compiler du C, et python, pour faire du python...
Il existe pléthore de tutoriels, et même des compilateurs en ligne !
Goodbye world :(
Vous allez créer votre premier programme ! Le tout premier programme qu'on enseigne aux débutants, et c'est normal.
Editeur de texte
Pour faire un programme, il faut l'écrire. Sur papier ça marche, mais on ne peut pas l'exécuter, c'est juste de l'encre et un bout d'arbre très plat. Du coup il nous faut un éditeur de texte.
Bloc-note, ça fonctionne.
Notepad, ça fonctionne pas mal.
L'écrire sur un terminal, ça fonctionne aussi.
Mais si je vous disait qu'on peut rédiger du code dans une application spécialisée, qui auto-corrige, suggère et signale les erreurs avant même de compiler ?
Je vous ai vendu du rêve, peut-être que je vous ai perdu. Mais dans tout les cas, téléchargez VSCode.
VSCode c'est comme bloc-note mais plus joli et intelligent, avec une grosse communauté derrière, et c'est l'application "par défaut", et c'est très bien comme ça.
Rappelez-vous qu'un éditeur de texte, aussi sophistiqué soit-il, ne fait principalement qu'écrire du texte. Certains sont plus robustes, avec des fonctionnalités qui brillent, mais restez basique pour le moment.
Programme C
Dans l'éditeur de texte de votre choix, collez ça :
#include <stdio.h>
int main() {
printf("Goodbye world :(\n");
return 0;
}
On n'expliquera pas maintenant pourquoi ça marche, et comment.
Si ? Vraiment vous insistez ? Vous n'arriverez plus à dormir avant de savoir la recette magique qui fait que ce code marche ?
Pour vraiment tout comprendre il faudrait se pencher sur Variables et types de données - Niveau 1, et [[COURS - Structure de contrôle]]... mais voilà un résumé utile pour la suite des exercices :
int main() {
// vos instructions vont ici, entre la première accolade et le "return"
return 0;
}
Ce morceau de code est la fonction qui s'appelle main (qui veut dire "principale" en Anglais).
Le code doit se trouver entre les deux accolades, c'est comme ça il faut l'accepter. Dans ce bloc, on va donc y mettre nos instructions, comme par exemple, une fonction (et oui encore une !) qui affiche du texte !
Cette fonction, imbriquée dans la fonction main, s'appelle printf. Elle affiche du texte. printf("Goodbye world :(\n");
Exécution
Sauvegardez le fichier sur votre machine avec un nom type "program.c" ou bien "et_ouais_classe.c", et exécutons-le :
gcc program.c -o program
./program
ou bien si vous avez choisi le deuxième nom
gcc et_ouais_classe.c -o et_ouais_classe
./et_ouais_classe
Normalement, vous verrez dans le terminal affiché Goodbye world :( !
Dingue.
Exercices
Pour le reste des exercices de ce cours, utilisez la fonction main :
int main() {
//blablabla
return 0;
}
et remplacez le blabla par le code que vous souhaitez exécuter.
Si vous avez des erreurs de compilations, erreur de syntaxe, erreur tout court, il faut chercher de soit même sur internet, idéalement en anglais.
Apprendre à chercher
Savoir chercher est un outil indispensable dans ce domaine, car on ne peut jamais tout savoir.
Les professionnels les plus performants le sont non seulement par leurs compétences, mais aussi par leur capacité à apprendre rapidement en trouvant les ressources de façon efficace.
Aujourd'hui l'informatique évolue constamment et rapidement, il va falloir apprendre à chercher de soi-même ! Certains disent même que l'auto-formation et la veille sont quasiment obligatoires pour rester à jour et pas périmer dans le marché du travail.
Vous appliquerez directement ces principes pendant votre apprentissage de la programmation, et pour la suite également. Personnellement, je cherche constamment
Niveau 1
La programmation c'est créer des instructions, qui sont exécutées pour réaliser une tâche.
Tout comme une recette de cuisine, l'ordinateur exécute les opérations l'une après l'autre, de façon séquentielle.
La programmation est une notion abstraite, comme décrite plus haut, mais en pratique on la trouve dans des langages de programmations bien connus : C, Python, Java, SQL. Chacun de ces langages implémente à sa façon la programmation, et à ses avantages, inconvénients, et syntaxes différentes.
Certains langages sont littéralement des recettes de cuisine, ou parfois même invisible.
On peut imaginer la programmation comme des recettes de cuisine :
- Un objectif à atteindre
- Des ingrédients à préparer
- Un ordre d'instructions à suivre
- Un résultat à déguster
important
Un élément crucial est la distinction entre les concepts théoriques et leur application.
Il faut toujours prendre en compte qu'une notion peut être implémentée de façon différente d'un langage à un autre.
C'est comme dire "J'ai faim" en espagnol, ou avec un fort accent du sud, le concept reste le même, on a faim, mais la manière de parler est différente.
warning
TOUJOURS penser au concept global, au concept général.
TOUJOURS respecter la syntaxe exacte du langage, si c'est comme ça qu'il faut l'écrire, alors c'est comme ça et c'est tout. Les langages ne corrigent pas les fautes de frappes.
Pourquoi n'existe-t-il pas un seul et unique langage de programmation magique qui fait tout ?
Tout comme les langues parlées, il n'existe pas de langage universel.
Chaque "langage" né de besoins particuliers, et certaines cultures amènent d'uniques besoins.
Tout comme les dizaines de mots esquimaux pour désigner la neige ou les centaines de mots pour manquer de respect à ta maman, certaines tâches informatiques requièrent différents types de langages informatiques pour répondre au mieux à ses besoins.
On dit souvent : "a jack of all trades is master of none", "valet de tous les métiers, maître d'aucun". Un langage qui saurait un peut tout faire ne pourrait pas faire une tâche particulière avec brio.
Pour commencer, je vous conseille d'entamer Les variables et types de données.
Variables et types de données
Niveau 1 - Variables de base
Sommaire
Une variable en programmation exprime une boîte dans laquelle on range quelque chose.
Par exemple, a = 1 va affecter la valeur 1 à la variable a. C'est à dire qu'on range 1 dans la boîte nommée a.
En pseudo-code, on peut tout à fait écrire a = 0.3 ou a = "Bonjour", c'est à dire que la boîte peut prendre n'importe quelle valeur.
On peut utiliser plusieurs termes pour décrire ça, on dit qu'on affecte une valeur à une boîte, qu'on déclare une variable, ou bien qu'on l'initialise.
Type de la variable
Certains langages de programmation ont une syntaxe et des exigences différentes.
Comme parfois en maths, certains langages vont vous demander de ne pas additionner des pommes avec des bananes.
Pour ces langages moins permissifs, ou parfois bas niveau, il faudra plutôt spécifier le types des variables que l'on manipule.
C'est à dire que la boite peut être faite pour recevoir des bananes, mais pas des pommes !
Mais qu'est ce qu'un type concrètement ?
"Bonjour" est un mot, "C'est le meilleur cours de programmation !" est une phrase, et 1 est un nombre. Un type en langage français, c'est ça.
Mais qu'en est-il des ordinateurs ?
Bien que les mots, les phrases, soit importants pour nous les humains, les ordinateurs ont besoin de types encore plus simples pour calculer et se souvenir de choses.
Pour la plupart des langages, on reconnait des types basiques comme :
- L'entier naturel
- int : signifie en anglais integer, traduit en français par : entier.
- 1, 2, -45, 69, -8080, ...
- int : signifie en anglais integer, traduit en français par : entier.
- Le nombre réel
- float : signifie en français flottant, c'est à dire un nombre réel à virgule. Utilisé quand on ne veut pas perdre de précision dans les calculs.
- 0.875, -2.4, 0.01, ...
- float : signifie en français flottant, c'est à dire un nombre réel à virgule. Utilisé quand on ne veut pas perdre de précision dans les calculs.
- Le booléen
- bool : signifie boule. Non c'est pas vrai. Presque. Ca veut dire soit vrai soit faux, c'est la logique de George Boole.
- vrai, faux / 1, 0
- La c'est un peu étrange, mais certains langages traitent l'entier 1 comme vrai, et le 0 comme faux.
- bool : signifie boule. Non c'est pas vrai. Presque. Ca veut dire soit vrai soit faux, c'est la logique de George Boole.
- Le caractère ou chaîne de caractère (parfois présent, absent en C par exemple)
- char : signifie caractère pour représenter une lettre par exemple.
- 'a', 'b', 'c', ...
- "bonjour", "comment ça va ?", "ça va", "au revoir", ...
- char : signifie caractère pour représenter une lettre par exemple.
warning
ATTENTION, certains langages incluent certains types et d'autres non ! Cela dépend de leur flexibilité et s'ils sont orientés pour être accessibles (haut niveau) ou pas (bas niveau).
Nom de la variable
Cette magnifique convention de nommage des variables est importante. En résumé :
- Les noms de variables doivent être lisibles et clairs, ils doivent au maximum décrire leur but
- Ne doit pas contenir d'espace
- Contenir des lettres, des tirets, et idéalement c'est tout Si vous voulez lancer un débat ou même une altercation physique avec vos camarades de classe, pourquoi pas s'approprier un de ces style d'écriture et le revendiquer comme étant le meilleur ? Choisissez-en un avec pour example le nom de variable "nombre_petale_tournesol"
- Le Pascal case :
NombrePetaleTournesol - Le Camel case :
nombrePetaleTournesol - Le Snake case :
nombre_petale_tournesol - Le Kebab case :
nombre-petale-tournesol - Le Voyelle case :
nOmbrE_pEtAlE_tOurnEsOl(ne faites jamais ça c'est trop moche je viens de l'inventer)
To be or not to be
En C comme dans la grande majorité des langages, les variables doivent exister pour être manipulées. Ce concept revient dans d'autres notions alors accrochez-vous !
Initialiser, c'est rendre réel. Créons une variable chenille, sans y mettre de valeur explicite.
On demande: Ordinateur, créée une boîte qui s'appelle chenille
int chenille;
Affecter, c'est l'action de donner une valeur à une variable à condition qu'elle existe !
On demande: Ordinateur, mets 5 dans la boite chenille et 1 dans la boite mammouth
chenille = 5; // chenille existe déjà on l'a créé précédemment !\
mammouth = 1; // erreur, car 'mammouth' n'a pas été initialisée avant !
S'il y a un type qui précède le nom, je déclare, sinon j'accède.
Définir, initialiser ou déclarer, j'utilise le type.
Accéder, lire, modifier, je n'utilise pas le type.
Code
La syntaxe pour l'affectation est la suivante :
type_de_la_variable nom_de_variable = valeur_de_la_variable
C'est tout moche, mais quand on remplace les termes, en jouant avec les définitions des types vus précédemment, on peut écrire du code valide :
int a = 1;
float baobab = 0.123;
char p_majuscule = 'p';
int *s = (int*) malloc(100 * sizeof(int));
Et là vous vous dites wow, j'ai tout compris ! Si c'est le cas c'est que vous avez pas lu, ou que vous êtes déjà trop fort, parce que la dernière ligne fait peur... mais elle respecte la structure !
Effectivement la théorie semble simple, les entiers, les nombres réels, certes. Mais certains langages cachent encore des concepts plus avancés que nous verrons dans les prochains niveaux !
Exercices !
Exercices - Variable de base
Q1 : Citez quelques types d'une variable
Réponse
-
Entier naturel : int
-
Nombre réel : float
-
Booléen : bool
-
Caractère : char
Q2 : Quelle est la syntaxe de la déclaration d'une variable en C ?
Réponse
Attention à cette subtilité, le langage C à besoin d'un point virgule quand on termine une instruction ! C'est normal de ne pas le savoir.
type_var nom_var = valeur_var;, par exemple int a = 1;
Q3 : Déclarez trois variables en C avec les valeurs 9.835, 5, et False respectivement, et choisissez les types les plus adéquat
Réponse
float variable1 = 9.835;
- Comme 9.835 n'est pas un entier mais un nombre réel, on utilise le type float.
int variable2 = 5;
- Comme 5 est un entier, on utilise le type int. Notez qu'on aurait aussi pu utiliser le type float, on n'aurait eu aucune perte d'information, ça aurait été l'équivalent de 5.00000 ...
bool variable3 = 0;
- Attention ici, "False", ou "false", n'est pas reconnu en C ! Cependant dans d'autres langages, comme en Python,
a = Falseest tout à fait valide. Comprennez bien le principe, retenez bien la syntaxe.
Q4 : Déclarez trois variables en Python avec les valeurs 5, 0.01 et "Bonjour" respectivement.
Réponse
a, b, c = 5, 0.01, "Bonjour"
Wow. Si simple, et si pratique. C'est aussi un sacré piège car Python est très permissif, et permet d'omettre non seulement les types, mais aussi de déclarer sur la même ligne plusieurs variables !
Mais ne vous laissez pas trop séduire par son accessibilité, l'apprentissage du C en parallèle aidera à consolider les bases. Gardez en tête la vaste différence des syntaxes !
Q5 : Quelle sera la valeur de la variable mouton à la fin de l'exécution du programme ?
int mouton = 1;
int berger = 2;
mouton = berger + 10;
Réponse
mouton sera égal à berger + 10, c'est à dire 2 + 10 car berger = 2 donc mouton = 11 !
Syntaxe à part, la logique est là. Les noms de variables sont remplacés par l'ordinateur à l'exécution par leur valeur respective. Berger, devient 2. Mouton, devient 11.
Si on regarde la syntaxe, on relève plusieurs choses, le mot mouton à la 4ème ligne n'a pas de type ! (il n'est pas précédé par le terme int)
On étudie ça dans la partie Opérateurs - Niveau 1 !
Niveau 2 - Types primitifs et tableaux ?
Types primitifs
Maintenant que les types de base sont posés, on va parler des types primitifs.
long double etc
Niveau 3 - Objets(struct) et pointeurs ?
Pointeur
Les opérateurs
Niveau 1 - Opérateurs de base
Pré-requis : Variables et types de données - Niveau 1
Sommaire
Opérateur =
Calculer c'est bien. Mais sans stocker la valeur, on ne peut pas faire grand chose !
Souvenez-vous que l'ordinateur remplace à l'exécution les variables par leurs valeurs.
Nous avons déjà vu dans le cours des variables certaines notions, normalement le code suivant est acquis :
int poulailler;
poulailler = 2;
Alors allons plus loin. Pouvez-vous deviner la valeur de loup à la fin de l'exécution ?
float loup = 0.8; // On initialise / déclare et affecte une valeur en même temps
float mouton = 0.2;
loup = mouton;
Que s'est-il passé ? On a écrasé la valeur de la variable loup par la valeur de la variable mouton.
Donc loup vaut 0.2 et mouton aussi !
En python, on omet les types, mais le principe et les résultats sont les mêmes
loup = 0.8
mouton = 0.2
loup = mouton
Opérateurs +, -, /, *, %
Ces opérateurs sont les mêmes qu'en mathématiques, avec addition, soustraction, division, multiplication et modulo respectivement.
Voyons comment ils fonctionnent :
int cagette = 1;
int pissenlit = 2;
cagette = (pissenlit + cagette) / 2;
Un peu complexe, mais on fait un calcul de maths. Souvenez-vous l'ordinateur remplace les variables.
Ici, pissenlit vaut 2 et cagette vaut 1, du point de vue de l'ordinateur, la dernière ligne lui demande :
mets dans la variable cagette la valeur (2 + 1) / 2
Exercices !
Exercices - Opérateurs
Q1 : Quelle est la valeur de la variable b à la fin de l'exécution du programme ?
int a = 1;
int b = 0;
b = 3;
b = a;
Réponse
b = 1
On va comprendre ligne par ligne
1ere ligne, a = 1 et b n'existe pas encore !
2eme ligne, b est initialisé à 0 (et a existe toujours)
3eme ligne, b vaut 3, c'est à dire qu'on met la valeur 3 dans la boîte b
4eme ligne, b vaut a, et comme a vaut 1 aux dernière nouvelles, alors b vaut 1 !
Il faut vraiment comprendre cette exécution séquentielle, ligne par ligne.
Q2 : Si la variable escargot vaut 4, quel est le résultat de escargot = escargot + 1; ?
Réponse
5
Vous comprenez le principe ?
On aurait pu ranger la valeur escargot + 1 dans une autre boîte, mais c'est pratique de le ranger dans elle-même, ça prends moins de travail à faire.
Avec cette méthode je peux même deviner votre âge dans 5 ans ! C'est simplement votre âge aujourd'hui + 5 ans. C'est de la triche, mais ça marche ! Peut importe ce que je mets dans la boîte escargot, je vais juste ajouter 1 à la boîte.
Q3 : Quelle est la valeur de la variable palmier après l'exécution du code ?
int palmier = 5;
int poire = 3;
palmier = poire + palmier
Réponse
palmier = 8
Encore une fois ligne par ligne
1ere et 2eme ligne on déclare les deux variables avec les valeurs 5 et 3
3eme ligne, on prends la boîte palmier et on y met sa propre valeur avec poire en plus.
On range donc dans cette boîte palmier la valeur palmier + poire, c'est à dire 5 + 3 donc au final palmier vaut 8 !
Petit tips bonus, que ce soit palmier ou une autre variable, quand on met un opérateur =, le contenu de la boîte est effacé et remplacé par la
valeur du membre à droite.
Niveau 2 - Opérateurs relationnels
Pré-requis :
Sommaire
Vous les avez surement déjà vu en mathématiques, les opérateurs relationnels sont là pour comparer deux expressions, deux valeurs.
La syntaxe change selon le langage et ne correspondent pas exactement aux signes mathématiques, mais la logique reste la même.
Attention car ce sont des opérateurs logiques, leur résultat donne Vrai ou Faux
Opérateur !
L'opérateur point d'exclamation signifie NON.
Non quoi ? Non à tout. Pas très fun, c'est la négation booléenne.
Prenons comme exemple vous, vous êtes magnifique. On pourrait créer une variable booléenne, que l'on appellerait "magnifique" et à laquelle on affectera la valeur True (en français: "Vrai").
Si j'affiche la variable magnifique, je devrais voir True ! Jusque là c'est normal.
Si j'affiche en revanche !magnifique, je devrais voir False !
Même en langage courant, si c'est NON vrai, alors c'est faux. Si c'est NON faux, alors c'est vrai.
Opérateurs == et !=
Pour tester l'égalité entre deux membres, on utilise l'opérateur == pour savoir s'ils sont égaux, ou l'opérateur != pour savoir s'ils sont inégaux (!= veut littéralement direNON égal ?)
C'est littéralement la question "est ce que c'est égal ?" ou "est ce que ce n'est pas égal ?"
Il peut prendre plusieurs formes:
===en Javascriptequals()(syntaxe d'appel de fonction !), ou==en Java- etc ...
Exemple
En C:
int a = 1;
int b = 2;
printf("%d\n", a == b);
On demande a égal b ?. Le programme affiche 0 pour dire False.
En python:
a = 1
b = 2
print(a == b)
Opérateurs >, <, >=, <=
Ceux là sont assez explicites, mais on va quand même les étudier rapidement:
a > b:aest-il plus grand queb?a < b:aest-il plus petit queb?a >= b:aest-il plus grand ou égal àb?a <= b:aest-il plus petit ou égal àb?
Niveau 3 - Opérateurs logiques, incrément décrément
&&, ||, !
+=, -=, ++, --
Niveau 4 - Opérateurs pointeurs et référence
&, *
Niveau 5 - Opérateurs bitwise
&, |, ^, ~, <<,
Fonctions
Niveau 1 - Déclaration et appel
Pré-requis :
Sommaire
Les fonctions sont des éléments primordiaux de la plupart des langages de programmation, elles structurent le code quand elles sont bien gérées, le rendent modulable et surtout compréhensible et lisible.
A quoi sert-elle et comment elle marche ?
Pour répondre à cette question on va séparer les concepts de la syntaxe.
Les fonctions sont comme des recettes de cuisine, on commence une recette, on fait les étapes, puis on a un résultat comestible, on espère.
Une autre jolie image c'est que les fonctions sont comme de multiples chefs d'orchestres avec chacun leur partition à suivre. Quand un chef d'orchestre est appelé, il joue sa pièce de musique, puis quand il a fini, il s'arrête.\
Dans tout les cas, essayez de créer votre propre analogie ou compréhension une fois que vous aurez appris son comportement.
Une fonction peut être appelée, et elle peut retourner un résultat.
Si vous avez mis en place votre environnement de code avec Préparer l'environnement, vous avez déjà vu comment exécuter la fonction main.
Quand on exécute la commande gcc on appelle la fonction main qui retourne 0.
Souvenez-vous que 0 en C signifie Vrai, ou True (voir La logique informatique)
C'est juste pour s'assurer que la fonction s'est correctement exécutée sans erreur.
Puisqu'on en a déjà manipulé sans s'en rendre compte, créons-en d'autres et apprenons leur comportement !
Pour la suite des consignes souvenez-vous aussi que l'exécution est séquentielle, ligne par ligne. Ca deviendra important.
Déclaration de revenus fonctions
Souvenez-vous de la structure de la fonction main et servons-nous en pour construire la fonction arroser_les_plantes:
int arroser_les_plantes() {
return 0;
}
Cette fonction marche parfaitement ! Elle ne fait rien de spécial, puisque si on l'appelle, elle renvoie 0, c'est à dire "aucun problème".
On appelle la façon d'écrire une fonction sa signature.
Notre fonction à :
- Un type de retour, ici, int
- Un nom, ici arroser_les_plantes
- Des paramètres, ici les parenthèses vides, c'est à dire aucun paramètre. Pour l'instant on ne s'y attarde pas.
important
Vous avez remarqué les accolades ? Souvenez vous ! C'est le corps de la fonction, le bloc, sa portée.
Appel d'une fonction
Maintenant qu'on a déclaré une fonction, on va l'implémenter dans le code et l'appeler depuis la fonction main.
int arroser_les_plantes() {
return 0;
}
int main() {
arroser_les_plantes();
return 0;
}
On peut déjà relever plusieurs choses :
- La fonction arroser_les_plantes est déclarée en dehors du corps de la fonction main. Rappelez vous des blocs et de la portée du code.
- Cette nouvelle fonction retourne 0, comme la fonction main, pour nous dire que tout s'est bien déroulé.
- On appelle la fonction arroser_les_plantes depuis la fonction main, avec une syntaxe très courante :
nom_de_la_fonction(). Ces parenthèses sont souvent la clé pour deviner si on appelle une fonction ! On verra plus tard que ces parenthèses sont synonymes de paramètres d'une fonction.
important
Parfois, les appels de fonctions ont une syntaxe différente, rappelez-vous bien de séparer concept et syntaxe !
Exécution
Que va-t-il se passer lors de l'exécution ? On demande à la fonction ce qu'elle veut pour son dernier repas avant de passer à la chaise électrique ? Ca aurait été beaucoup plus fun comme explication, malheureusement c'est totalement hors sujet et on est partie en roue libre.
On se concentre, pensons positif, pensons recette de cuisine. Là, il va falloir s'accrocher un peu mais faites moi confiance. Inspire, expire, inspire, inspire, inspire ...
J'ai déjà dit plusieurs fois que l'exécution est séquentielle, est c'est toujours vrai, mais cette fois-ci il va falloir discerner déclaration (ou définition, initialisation) d'une fonction et appel d'une fonction. Pas de panique en réalité, on l'a déjà vu avec les variables, et c'est plutôt intuitif avec les bons exemples.
Replongeons dans les profondeurs du code précédent :
int arroser_les_plantes() {
return 0;
}
Ici, ma fonction est définie, c'est à dire que j'ai modelé sa forme, sa structure.
Pour imager, ces lignes signifient "je créée la recette arroser_les_plantes, elle existe, et fera ça et ça, puis retournera 0" mais à aucun moment on ne l'appelle !
Certes, arroser les plantes est un nom de recette étrange, mais c'est comme faire à manger pour les plantes :)
Si on omet la ligne arroser_les_plantes() dans la fonction main, elle ne sera jamais appelée, mais elle existera.
C'est bon, expire.
Conclusion
La fonction est d'abord définie, puis elle peut être appelée ou non ! Définie, c'est juste qu'elle existe.
On ne pourra en revanche jamais appeler une fonction non définie !
Maintenant que vous connaissez les fonctions, on peut en faire partout. Si vous souhaitez rendre votre code modulable, ou bien le segmenter, rien ne vous empêche de prendre des opérations et les placer dans une fonction annexe pour clarifier les choses.
C'est plus simple de lire la fonction main suivante, si on imagine que chaque fonctions qu'on a créé englobe des dizaines de lignes d'opérations !
int main() {
afficher_bienvenue();
char lettre = generer_lettre_aleatoire();
char nombre = generer_nombre_aleatoire();
afficher_au_revoir();
}
Exercices !
Exercices - Fonctions
Q1 : Une fonction, peut-elle être définie mais pas appelée ?
Réponse
Oui.
Une fonction peut être définie, mais pas forcément appelée, pas forcément utilisée !
La fonction est bien là, elle existe dans le programme, mais dans ce cas ne servira pas pour le moment.
En revanche, on ne peut pas appeler une fonction si elle n'est pas définie !
Q2 : Qu'affichera ce code ?
Tips : L'exécution commence dans la fonction main.
La fonction printf sert à afficher des éléments, et le \n à la fin des phrases sert à marquer une fin de ligne (il ne s'affichera pas)
#include <stdio.h>
int seBallader() {
printf("je me ballade\n");
return 0;
}
int sePerdre() {
printf("je suis perdu dans la forêt\n");
return 0;
}
int main() {
sePerdre();
seBallader();
return 0;
}
Réponse
je suis perdu dans la forêt
je me ballade
En effet, la fonction main commence et exécute la première instruction, c'est à dire l'appel de la fonction sePerdre.
La baguette d'exécution est donnée à la fonction sePerdre, pendant que main attends la fin de l'exécution de celle-ci.
sePerdre exécute ses instructions et "je suis perdu dans la forêt" s'affiche en marquant une fin de ligne, puis retourne 0 à l'ordinateur, qui rends la baguette de l'exécution à la fonction main.
De là, la fonction main exécute sa prochaine instruction, c'est l'appel de la fonction seBallader, qui vous l'avez deviné, affiche "je me ballade", retourne 0, rends la baguette d'exécution à main qui elle aussi retourne 0, et tout est terminé.
Q3 : Qu'affichera ce code ?
#include <stdio.h>
int babord() {
printf("a gauche!\n");
return 0;
}
int tribord() {
babord();
printf("a droite!\n");
return 0;
}
int main() {
tribord();
return 0;
}
Réponse
a gauche!\
a droite!
La fonction main appelle tribord.
tribord exécute donc ses instructions. La première est un appel vers la fonction babord, et lui donne donc la baguette d'exécution. Cela signifie que l'ordinateur n'exécute pas la ligne printf("a droite!\n"); pour le moment !
La fonction babord s'exécute, et affiche a gauche!, puis elle rend le contrôle de l'exécution à la fonction tribord qui affichera a droite!, qui elle-même rend le contrôle de l'exécution à la fonction main, qui s'arrête ensuite.
Niveau 2 - Paramètres et types de retours
Pré-requis :
Sommaire
Vous vous souvenez que les fonctions sont comme des recettes de cuisine ? Il est l'heure de vous passer à la casserole. Vous allez voir que l'analogie de la cuisine va être vraiment utile.
A table
Imaginons que nous voulons cuisiner un poulet cajun. On dresse notre plan d'attaque !
- Un objectif à atteindre :
- Faire un POULET CAJUN 🍽️
- Des ingrédients à préparer :
- Poulet 100g
- Sauce cajun 10cl
- Un ordre d'instructions à suivre (simplifiées) :
- Cuire le poulet
- Ajouter la sauce cajun
- Un résultat à déguster
Jusque là c'est simple et efficace... mais OH NON ! Sauce cajun ? On en a plus.
Puisqu'on a les ingrédients, on peut les prendre un par un, les mélanger, et le résultat c'est une sauce cajun !
Oui mais pourquoi le faire à la main si j'ai une fonction qui traîne au fond du placard, qui s'appelle : faireSauceCajun ?
On a qu'à mettre les ingrédients en tant que paramètres dans ma fonction, la regarder mixer toute seule, et nous retourner le résultat, qui est une sauce cajun !
Si seulement ça existait dans la vraie vie...
int faireSauceCajun()
Structures de contrôle
Niveau 1 - Blocs et portées
Pré-requis :
Sommaire
Rappelez-vous que pour l'instant on définit le code comme une recette de cuisine qui s'exécute de façon séquentielle.
Pour organiser un code, on le place dans des "blocs". Les structures de contrôle sont une famille de concepts qui vont nous permettre de contrôler le flot de l'exécution ! Très stylé.
[!IMPORTANT] Un code à une "portée", il existe dans un contexte d'exécution. Si on sort du bloc où il est définit, on ne peut plus y accéder.
Bloc par bloc
Beaucoup de blabla et pas beaucoup de sens peut-être. Voyons un exemple en C:
{
//ceci est un bloc délimité par des accolades
}
Comme toujours, la syntaxe change beaucoup entre les langages, mais beaucoup utilisent les accolades. Ce bloc là est très joli, on va lui ajouter une variable !
{
int a;
}
La portée de cette variable commence à partir de la ligne où on l'initialise, et la fin du bloc.
{
int a;
}
a = 1;
Ce code donne une erreur ! On accède à une variable en dehors de sa portée, aux yeux de l'ordinateur elle n'existe plus !
Attrapez les blocs
Si vous avez compris le principe derrière ces blocs, c'est gagné. Bravo.
A partir de maintenant, selon le langage et sa syntaxe, repérez ces blocs pour mieux comprendre l'exécution du code.
Si je vous jette la fonction main en C dans les mains, vous verriez ça :
int main() {
int a = 1;
return 0;
}
Pas la peine de tout comprendre, on voit les accolades, on comprends le bloc.
Pourquoi pas une fonction main en Python !
def main():
a = 1
Bizarre, du côté du serpent les blocs sont gérés par l'indentation, et seulement au niveau des fonctions. Peut importe c'est le même principe !
Je vous donne maintenant une structure de condition en C. Attrape !
if (condition) {
int a;
} else {
//code
}
a = 2; //ERREUR
On voit aussi les accolades, si une variable est définie entre les premières accolades, elle n'existera pas entre les deuxièmes (puisqu'on est sorti de sa portée et qu'elle n'existe plus !)
Exercices !
Exercices - Opérateurs
Q1 : Est ce que ce code renvoie une erreur ?
int main() {
{
int a = 1;
}
a = 2;
return 0;
}
Réponse
Le code est invalide.
On a vu ensemble les blocs de code, et qu'en C par exemple c'est les accolades qui les délimitent.
Ici a est initialisée dans un bloc, puis en dehors de celui ci on essaie d'accéder à la variable a, mais dans ce contexte d'exécution, elle n'existe pas !
Un code valide serait
int main() {
//on initialise et accède à la variable dans le meme bloc, c'est ok !
{
int a = 1;
a = 2;
}
return 0;
}
ou bien
int main() {
//ici on s'est complètement affranchi du bloc qui posait problème
//on remarque qu'on reste quand meme dans une fonction qui elle aussi est un bloc
int a = 1;
a = 2;
return 0;
}
Q2 : Plus difficile, est ce que ce code renvoie une erreur ?
int main() {
int a = 1;
{
a = 2;
}
}
Réponse
Le code est valide.
Ca peut paraître étrange, mais la ligne int a = 1 est dans le bloc de la fonction. Donc pour toute la portée de la fonction, et donc les lignes suivantes jusqu'à la fin de la fonction, la variable existe !
Quand on ouvre ensuite le petit bloc où l'on place a = 2, on hérite des objets déjà existants dont la variable a déjà existante, et on peut la modifier !
Q3 : Encore plus dur, est ce que ce code renvoie une erreur ?
int main() {
{int a = 1;} a = 2;
}
Réponse
Le code est invalide, et moche.
Ici le plus important c'est de comprendre qu'il n'y a pas vraiment de règles en C pour le formatage, comparé à d'autres langages. Regardez Les bonnes pratiques qui à un bon exemple.
En corrigeant le formatage, on peut réécrire la fonction comme ceci:
int main() {
{
int a = 1;
}
a = 2;
}
Là on comprends mieux que la ligne a = 2 ne peut pas accéder à la variable.
Niveau 2 - Conditions if/then/else
Pré-requis :
Sommaire
Les structures de contrôle de condition sont en réalité intuitifs, on l'emploie même dans le langage courant : "si tu laisses ta glace par terre, les fourmis vont se régaler". C'est une condition qui est soit vraie, soit fausse. Il y a deux possibilités, si on laisse la glace alors les fourmis vont se régaler, sinon, elles ne se régaleront pas !
Avec des if, on refait le monde
La structure "if / then / else" ou en français "si, alors, sinon", sert à poser des conditions logiques.
Imaginons que votre programme vérifie si il y a une présence de polluant dans l'eau filtrée, il faut bien prendre une décision ensuite ! C'est bien joli d'afficher un message, mais on aimerait que l'ordinateur puisse exécuter des instructions qui alertent le responsable.
Prenons un exemple réaliste, imaginons cette fois-ci que je sois allergique à la vanille, mais que je décide quand même d'acheter une glace surprise.
Si ma glace est à la vanille, alors je vais faire une réaction et mourir. Sinon, je vais me régaler.
stateDiagram-v2
[*] --> glaceVanille?
glaceVanille? --> Déceder: True
glaceVanille? --> Régaler: False
On peut traduire ce comportement en code C:
if (glace == "vanille") { // ici on demande 'la glace est à la vanille ?'
// mourir
} else {
// se régaler
}
Si glace est à la vanille, alors on exécute le code pour mourir, sinon on exécute le code pour se régaler !
Voyons voir le même code en python:
if glace == "vanille":
# mourir
else:
# se régaler
On remarque que le bloc if / then / else, tout comme le bloc de fonction est délimité différemment en C et en python.
Peut importe, on accepte les règles, et on essaie de comprendre le principe.
Exercices !
Exercices - Opérateurs
Q1 : Qu'affiche ce programme ?
int main() {
if (1 == 1) { printf("1 = 1"); }
if (1 == 2) {
printf("1 = 2");
}
else { printf("1 != 2"); }
return 0;
}
Réponse
Le code affiche
1 = 1
1 != 2
On voit quelque chose de nouveau. Le premier if n'a pas de clause else à sa suite ! Et oui ça arrive. Ici, 1 est égal à 1 ! Donc on rentre dans la boucle if, et on affiche 1 = 1.
Ensuite, on teste si 1 est égal à 2. Comme ce n'est pas le cas, la condition n'est pas satisfaite, donc on passe dans la structure de contrôle alternative else, qui nous demande d'afficher 1 != 2
Q2 : Plus dur, qu'affiche ce programme ?
int main() {
int a = 5;
if (a < 1) {
if (a == 5) {
printf("ta\n");
} else {
printf("ren\n");
}
printf("tu\n");
} else {
printf("le!\n");
}
return 0;
}
Réponse
Il affiche le!.
Si vous avez pas compris pourquoi, alors BLOCS BLOCS BLOCS BLOCS !
Désolé si je vous ai fait extrêmement peur, mais au moins ça restera gravé à jamais dans votre mémoire jusqu'à vos derniers instants.
La clé, c'est le raisonnement par blocs, regardez comment c'est joli. Vous voyez la fonction main ? Écrivons là en repliant ses instructions sur elle-même, en version compacte si vous préférez :
int main() {
// code ...
}
En pseudo-code, j'ai le droit d'écrire comme ça, parce que j'ai envie. Pour l'instant rien de difficile, on a juste replié la fonction principale. Et pourtant c'est la clé pour le reste. On peut aussi replier les blocs if, et tous les autres :
int main() {
int a = 5;
if (a < 1) {
//code ...
} else {
printf("le!");
}
return 0;
}
Ici j'ai replié la première instruction if sur elle-même, c'est le premier test que l'ordinateur fait puisque c'est la première ligne qui arrive après l'affectation int a = 5.
L'ordinateur teste si a < 1, comme c'est faux (car 5 < 1 est faux), on ne rentre meme pas dans le bloc du if ! Pas la peine de s'embêter. Puisqu'on a un else, on rentre dedans, et on affiche le!.
En revanche, si la condition était vraie, alors on serait rentré dans l'instruction if et là il aurait fallu exécuter le reste, etc...
Q2.2 : Bonus question 2 ! la valeur de a change, qu'affiche le programme ?
int main() {
int a = -5;
if (a < 1) {
if (a == 5) {
printf("ta\n");
} else {
printf("ren\n");
}
printf("tu\n");
} else {
printf("le!\n");
}
return 0;
}
Réponse
Il affiche
ren
tu
Alors ? Si c'est encore compliqué, on peut essayer d'aller plus doucement.
L'ordinateur voit d'abord int a = -5. Ca c'est ok on connaît.
Ensuite il doit faire le test : a < 1. Dans notre cas, c'est vrai car -5 < 1. Comme la condition est vraie, on rentre dans le bloc du if, et on se rappelle que l'on éxécutera pas son bloc else ! C'est soit l'un soit l'autre.
Une fois dedans, l'ordinateur voit ça :
if (a == 5) {
printf("ta\n");
} else {
printf("ren\n");
}
printf("tu\n");
Donc on doit tester une autre condition : a == 5 dans notre cas est faux donc on ne rentre pas dans la boucle if, mais on exécute le code dans else, c'est à dire qu'on affiche ren !
Et là, après le else, on a encore une dernière instruction à éxécuter. Elle ne fait pas partie du if/else imbriqué, elle est en réalité dans le premier if dans lequel on est rentré. On affiche donc tu.
Niveau 3 - Boucles itératives
Pré-requis :
Sommaire
Boucles for / while
Les structures "for" et "while" sont des boucles qui vont itérer. Elles sont particulièrement utiles pour les tâches répétitives.
Pour comprendre leurs différences et leur fonctionnement, on va prendre deux exemples.
Boucle for
La boucle "for" (qui signifie en français "pour"), demande à l'ordinateur :
Ordinateur, pour un nombre définit de répétition, fait ça.
Imaginons que nous voulons faire pousser des fleurs, plus précisément des soucis, dans votre jardin. Votre poche a 12 graines de soucis, et vous voulez les planter en ligne, pour faire joli.
A quoi ressemblerait la logique de la recette pour planter toutes ces plantes ?
En langage naturel, on peut dire qu'on se place au début de la ligne, on sort une graine de sa poche, on fait un trou dans la terre, on plante la graine, on recouvre de terre, et on arrose, puis on vérifie qu'on peut toujours avancer d'un pas et on recommence.
Du 1er pas au 12eme pas, on fait des actions à chaque pas, c'est exactement le fonctionnement d'une boucle for !
Les syntaxes changent beaucoup selon les langages, mais souvenez vous des principes :
- Au début on fixe une valeur de départ, et une condition de fin. Dans notre cas on commence au pas 1 et on finis au 12eme pas. A chaque itération, l'ordinateur vérifiera si la variable
passera plus petite que 12. Si c'est le cas, c'est qu'on est à la fin du jardin donc la boucle s'arrête. - Enfin on choisit l'incrément ou décrément de la variable, à chaque fois que la boucle se termine, l'ordinateur va modifier la variable
pas. Dans notre cas, on avance d'un pas à chaque fois alors on l'incrémente de 1.
En C, la syntaxe est la suivante :
for (initialisation variable ; test condition ; mise a jour variable) {
//code
}
Et ci-dessous notre exemple concret :
for (int i = 1; i < 12; i++) {
planter_soucis();
}
Ici, la variable i représente les pas, par convention on appelle cette variable i dans les boucles.
Le code dit que i commence à 1, et qu'à chaque itération, on vérifie que i < 12. Si c'est le cas, alors on fait l'opération i++ et on exécute le code à l'intérieur du corps de la boucle. Sinon, on sort de la boucle.
Boucle while
La boucle "while" (qui signifie en français "tant que"), demande à l'ordinateur :
Ordinateur, tant que la condition n'est pas remplie, fait ça.
C'est différent de la boucle for, car là on ne définit pas explicitement de nombre de répétition, simplement le fait qu'on s'arrête quand la condition est remplie.
Imaginons qu'on veuille faire pousser un citronnier, et qu'on veuille l'arroser tant qu'il n'a pas atteint 1 mètre 30.
A quoi ressemblerait la logique de la recette pour le faire pousser ?
En langage naturel, on regarde la taille du citronnier, s'il n'est pas à 1 mètre 30, alors on l'arrose s'il en a besoin. Si en revanche il fait 1 mètre 30 ou plus, on ne l'arrose plus ! Tant pis pour lui.
En C, la syntaxe est la suivante :
while (test condition) {
//code
}
Et voila ci-dessous note exemple concret :
while (taille_citronnier_cm < 130) {
arroser_citronnier();
}
Ici on n'a pas de variable incrémentée contrairement à la boucle for. A chaque itération, l'ordinateur teste la condition et si elle est vraie, exécute le code. Sinon, on sort de la boucle.
Exercices !
Bas niveau
Les concepts dits de bas niveau sont une façon d'exprimer une plus forte granularité sur le contrôle qu'on peut avoir. A l'inverse on parle de concepts de haut niveau.
Ce terme n'est pas clairement définit, mais évoque un principe de hiérarchie dans le fonctionnement d'un objet.
Par exemple, savoir conduire une voiture est un concept de haut niveau. Il est fondé sur d'autres concepts plus bas niveau comme la boite de vitesse, les essieux, ou bien les logiciels ordinateurs de la voiture.
Mais l'essieu, la boîte de vitesse, sont eux aussi haut niveau par rapport à d'autres concepts comme l'écrou ou les engrenages qui les composent ?
- Oui. En jouant à ce jeu là on atteint très vite l'infiniment petit et le mot perds de son sens. On cherche a conserver une notion du "bas niveau" proche du domaine de départ. Ici, l'écrou n'est plus vraiment dans le domaine de l'automobile, et ce concept s'applique aussi aux ordinateurs !
Un concept dit "bas niveau" fait communément référence à des concepts qui sont quasi-universels à toute les machines.
Là ou le fond d'écran ou le thème d'un ordinateur change pour beaucoup d'utilisateur, la manière qu'à un processeur pour communiquer avec d'autres composants peut être la même pour la plupart des machines.
On parle souvent de l'assembleur comme étant la référence des [[langages_de_programmation]] de bas niveau.
Haut niveau
Un concept dit de haut niveau est généralement construit sur des blocs dit de bas niveau, souvent pour faciliter son utilisation, ou la rendre plus accessible.
Prenons par exemple l'une des plus belle invention dans le monde, l'eau courante. Quand on ouvre le robinet, la poignée qu'on utilise a été imaginée, dessinée puis créée en usine pour arriver chez nous, sans parler de la tuyauterie ni des traitements de sa qualité en amont.
Bonnes pratiques
Vous connaissez l'IOCCC ? C'est le concours international d'obfuscation de code en C.
C'est des personnes brillantes qui font des programmes tout simples, mais de la manière la plus compliquée.
Bizarre d'en parler dans la partie "Bonne pratique", pourtant ça suit un raisonnement que j'aime bien et qui s'applique à beaucoup de choses.
[!IMPORTANT] Pour devenir maître, il faut apprendre les codes. Pour surpasser les maîtres, il faut casser les codes.
Ce concours est la matérialisation (digitale...) de ce concept.
Pour bien commencer, et bien progresser, il faut apprendre les codes, rendre son travail accessible, et rester dans le partage et l'entraide. C'est pour cela que les bonnes pratiques sont essentielles, elles donnent un cadre et nous rend rigoureux !
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <sys/ioctl.h>
main() {
short a[4];ioctl
(0,TIOCGWINSZ,&a);int
b,c,d=*a,e=a[1];float f,g,
h,i=d/2+d%2+1,j=d/5-1,k=0,l=e/
2,m=d/4,n=.01*e,o=0,p=.1;while (
printf("\x1b[H\x1B[?25l"),!usleep(
79383)){for (b=c=0;h=2*(m-c)/i,f=-
.3*(g=(l-b)/i)+.954*h,c<d;c+=(b=++
b%e)==0)printf("\x1B[%dm ",g*g>1-h
*h?c>d-j?b<d-c||d-c>e-b?40:100:b<j
||b>e-j?40:g*(g+.6)+.09+h*h<1?100:
47:((int)(9-k+(.954*g+.3*h)/sqrt
(1-f*f))+(int)(2+f*2))%2==0?107
:101);k+=p,m+=o,o=m>d-2*j?
-.04*d:o+.002*d;n=(l+=
n)<i||l>e-i?p=-p
,-n:n;}}
Peter Eastman, IOCCC 2011.
Si vous voulez tester, copiez le code, et lancez la commande :
gcc eastball.c -o eastball -lm && ./eastball
Fonctions
Languages de programmation
Un langage de programmation est
Pseudo-code
Le pseudo-code est une façon d'écrire du code de façon simplifiée, pour le rendre compréhensible et accessible.
Ce "language" est sémantiquement correct (il est facilement lisible), mais il n'est pas forcément syntaxiquement correct (s'il est exécuté, il ne correspond pas à la syntaxe d'un language précis).
Cette façon d'écrire le code sert souvent à expliquer des concepts dits de haut niveau, ou simplement à écrire une logique de code qui peut être lue par d'autres personnes et implémentée ensuite dans d'autres languages.