Leçon 1
- C
- hello, world
- Compilateurs
- Chaîne de caractères
- Blocs Scratch en C
- Types, formats, opérateurs
- Autres exemples
- Mémoire, imprécision, et dépassement
C
-
Aujourd'hui, nous allons apprendre un nouveau langage, C : un langage de programmation qui possède toutes les fonctionnalités de Scratch et plus encore, mais peut-être un peu moins convivial puisqu'il est uniquement en texte :
#include <stdio.h> int main(void) { printf("hello, world\n"); }
- Bien que les mots soient nouveaux, les idées sont exactement les mêmes que les blocs « lorsque le drapeau vert est cliqué » et « dire (hello, world) » dans Scratch :
-
Bien que cryptique, n'oubliez pas que 2/3 des étudiants de CS50 n'ont jamais suivi de CS auparavant, alors ne soyez pas intimidé ! Et bien qu'au début, pour emprunter une expression du MIT, essayer d'absorber tous ces nouveaux concepts puisse vous donner l'impression de boire à même une bouche d'incendie, soyez assuré qu'à la fin du semestre, nous aurons acquis les compétences et l'expérience nécessaires pour apprendre et appliquer ces concepts.
- Nous pouvons comparer de nombreuses constructions en C avec des blocs que nous avons déjà vus et utilisés dans Scratch. La syntaxe est bien moins importante que les principes, auxquels nous avons déjà été initiés.
bonjour, le monde
-
Le bloc « quand le drapeau vert est cliqué » dans Scratch démarre le programme principal ; le clic sur le drapeau vert déclenche l'exécution du bon jeu de blocs ci-dessous. En C, la première ligne pour le même est « int main(vide) », que nous apprendrons plus en détail au cours des prochaines semaines, suivie d'une accolade ouvrante « { » et d'une accolade fermante « } », englobant tout ce qui devrait être dans le programme.
int main(vide) { }
-
Le bloc « dire (bonjour, le monde) » est une fonction, et correspond à « printf("bonjour, le monde"); ». En C, la fonction pour imprimer quelque chose à l'écran est « printf », où « f » signifie « format », ce qui signifie que nous pouvons formater la chaîne imprimée de différentes manières. Ensuite, nous utilisons des parenthèses pour transmettre ce que nous voulons imprimer. Nous devons utiliser des guillemets doubles pour encadrer notre texte afin qu'il soit compris comme du texte, et enfin, nous ajoutons un point-virgule « ; » pour terminer cette ligne de code.
-
Pour que notre programme fonctionne, nous avons également besoin d'une autre ligne en haut, une ligne d'en-tête « #include
» qui définit la fonction « printf » que nous voulons utiliser. Quelque part sur notre ordinateur, il existe un fichier, « stdio.h », qui inclut le code qui nous permet d'accéder à la fonction « printf », et la ligne « #include » indique à l'ordinateur d'inclure ce fichier dans notre programme. -
Pour écrire notre premier programme en Scratch, nous avons ouvert le site Web de Scratch. De la même manière, nous utiliserons le [bac à sable CS50] (https://sandbox.cs50.io/) pour commencer à écrire et à exécuter du code de la même manière. Le bac à sable CS50 est un environnement virtuel basé sur le cloud avec les bibliothèques et les outils déjà installés pour écrire des programmes dans différentes langues. En haut, il y a un simple éditeur de code, dans lequel nous pouvons taper du texte. Ci-dessous, nous avons une fenêtre de terminal, dans laquelle nous pouvons taper des commandes :
![deux panneaux, le haut intitulé hello.c, le bas intitulé Terminal] (https://cs50.harvard.edu/x/2020/notes/1/cs50_sandbox.png) -
Nous taperons notre code précédent dans la partie supérieure, après avoir utilisé le signe « + » pour créer un nouveau fichier appelé « hello.c » :
![bonjour, le monde dans l'éditeur] (https://cs50.harvard.edu/x/2020/notes/1/editor.png) -
Nous terminons le fichier de notre programme par « .c » par convention, pour indiquer qu'il est destiné à un programme C. Notez que notre code est coloré, de sorte que certaines choses sont plus visibles.
Compilateurs
- Une fois que nous avons enregistré le code que nous avons écrit, qui est appelé code source, nous devons le convertir en code machine, des instructions binaires que l'ordinateur comprend directement.
- Nous utilisons un programme appelé compilateur pour compiler notre code source en code machine.
- Pour ce faire, nous utilisons le panneau Terminal, qui dispose d'une invite de commande. Le
$
à gauche est une invite, après laquelle nous pouvons taper des commandes.- Nous tapons
clang hello.c
(oùclang
signifie « langages C », un compilateur écrit par un groupe de personnes). Mais avant d'appuyer sur Entrée, nous cliquons sur l'icône de dossier en haut à gauche de CS50 Sandbox. Nous voyons notre fichier,hello.c
. Nous appuyons donc sur Entrée dans la fenêtre du terminal et voyons que nous avons maintenant un autre fichier, appeléa.out
(abréviation de « sortie d'assemblage »). À l'intérieur de ce fichier se trouve le code de notre programme, en binaire. Nous pouvons maintenant taper./a.out
dans l'invite du terminal pour exécuter le programmea.out
dans notre dossier actuel. Nous venons d'écrire, de compiler et d'exécuter notre premier programme !
- Nous tapons
Chaîne de caractères
-
Cependant, après avoir exécuté notre programme, nous voyons
hello, world$
, avec la nouvelle invite sur la même ligne que notre sortie. Il s'avère que nous devons spécifier précisément que nous avons besoin d'une nouvelle ligne après notre programme, nous pouvons donc mettre à jour notre code pour inclure un caractère de nouvelle ligne spécial,\n
:#include <stdio.h> int main(void) { printf("hello, world\n"); }
- Maintenant, nous devons penser à recompiler notre programme avec
clang hello.c
avant de pouvoir exécuter cette nouvelle version.
- Maintenant, nous devons penser à recompiler notre programme avec
-
La ligne 2 de notre programme est intentionnellement vide puisque nous voulons démarrer une nouvelle section de code, un peu comme on commence de nouveaux paragraphes dans les essais. Ce n'est pas strictement nécessaire pour que notre programme s'exécute correctement, mais cela aide les humains à lire plus facilement des programmes plus longs.
- Nous pouvons également changer le nom de notre programme de
a.out
à autre chose. Nous pouvons passer des arguments de ligne de commande, ou des options supplémentaires, aux programmes dans le terminal, en fonction de ce que le programme est écrit pour comprendre. Par exemple, nous pouvons taperclang -o hello hello.c
, et-o hello
indique au programmeclang
d'enregistrer la sortie compilée en tant quehello
. Ensuite, nous pouvons simplement exécuter./hello
. -
Dans notre invite de commande, nous pouvons exécuter d'autres commandes, comme
ls
(liste), qui affiche les fichiers dans notre dossier actuel :$ ls a.out* hello* hello.c
- L'astérisque,
*
, indique que ces fichiers sont exécutables, ou qu'ils peuvent être exécutés par notre ordinateur.
- L'astérisque,
-
Nous pouvons utiliser la commande
rm
(supprimer) pour supprimer un fichier :$ rm a.out rm: supprimer le fichier régulier 'a.out' ?
- Nous pouvons taper
y
ouyes
pour confirmer, et utiliser à nouveauls
pour voir qu'il a effectivement disparu.
- Nous pouvons taper
-
Maintenant, essayons d'obtenir des informations de l'utilisateur, comme nous l'avons fait dans Scratch lorsque nous voulions dire « bonjour, David » :
string answer = get_string("What's your name?\n"); printf("hello, %s\n", answer);
- Tout d'abord, nous avons besoin d'une chaîne de caractères, ou d'un morceau de texte (plus précisément, zéro ou plusieurs caractères dans une séquence entre guillemets doubles, comme
""
,"ba"
ou "bananes"), que nous pouvons demander à l'utilisateur, avec la fonctionget_string
. Nous passons l'invite, ou ce que nous voulons demander à l'utilisateur, à la fonction avec"What is your name?\n"
entre parenthèses. Sur la gauche, nous voulons créer une variable,answer
, dont la valeur sera ce que l'utilisateur saisit. (Le signe égal=
définit la valeur de droite à gauche.) Enfin, le type de variable que nous voulons eststring
, nous précisons donc cela à gauche deanswer
. - Ensuite, à l'intérieur de la fonction
printf
, nous voulons la valeur deanswer
dans ce que nous imprimons en retour. Nous utilisons un espace réservé pour notre variable de chaîne,%s
, dans la phrase que nous voulons imprimer, comme"hello, %s\n"
, puis nous donnons àprintf
un autre argument, ou une option, pour lui dire que nous voulons que la variableanswer
soit remplacé.
- Tout d'abord, nous avons besoin d'une chaîne de caractères, ou d'un morceau de texte (plus précisément, zéro ou plusieurs caractères dans une séquence entre guillemets doubles, comme
-
Si nous avons fait une erreur, comme écrire
printf("hello, world"\n);
avec\n
en dehors des guillemets doubles pour notre chaîne, nous verrons une erreur de notre compilateur :$ clang -o hello hello.c hello.c:5:26: error: expected ')' printf("hello, world"\n); ^ hello.c:5:11: note: to match this '(' printf("hello, world"\n); ^ 1 error generated.
- La première ligne de l'erreur nous dit de regarder
hello.c
, ligne 5, colonne 26, où le compilateur attendait une parenthèse fermante, au lieu d'une barre oblique inverse.
- La première ligne de l'erreur nous dit de regarder
-
Pour simplifier les choses (du moins pour le début), nous inclurons une bibliothèque, ou un ensemble de code, de CS50. La bibliothèque nous fournit le type de variable
string
, la fonctionget_string
, et plus encore. Il suffit d'écrire une ligne en haut pourinclure
le fichiercs50.h
:#include <cs50.h> #include <stdio.h> int main(void) { string name = get_string("What's your name?\n"); printf("hello, name\n"); }
-
Créons donc un nouveau fichier,
string.c
, avec ce code :#include <stdio.h> int main(void) { string name = get_string("What's your name?\n"); printf("hello, %s\n", name); }
-
Maintenant, si nous essayons de compiler ce code, nous obtenons de nombreuses erreurs. Parfois, une erreur signifie que le compilateur commence alors à interpréter le code correct de manière incorrecte, générant plus d'erreurs qu'il n'y en a réellement. Nous commençons donc par notre première erreur :
$ clang -o string string.c string.c:5:5: error: use of undeclared identifier 'string'; did you mean 'stdin'? string name = get_string("What's your name?\n"); ^~~~~~ stdin /usr/include/stdio.h:135:25: note: 'stdin' declared here extern struct _IO_FILE *stdin; /* Standard input stream. */
- Nous ne voulions pas dire
stdin
(« entrée standard ») au lieu destring
, donc ce message d'erreur n'était pas utile. En fait, nous devons importer un autre fichier qui définit le typestring
(en réalité une roue d'entraînement de CS50, comme nous le découvrirons dans les semaines à venir).
- Nous ne voulions pas dire
-
Nous pouvons donc inclure un autre fichier,
cs50.h
, qui inclut également la fonctionget_string
, entre autres.#include <cs50.h> #include <stdio.h> int main(void) { string name = get_string("What's your name?\n"); printf("hello, %s\n", name); }
-
Maintenant, lorsque nous essayons de compiler notre programme, nous n'avons qu'une seule erreur :
$ clang -o string string.c /tmp/string-aca94d.o: In function `main': string.c:(.text+0x19): undefined reference to `get_string' clang-7: error: linker command failed with exit code 1 (use -v to see invocation)
- Il s'avère que nous devons également indiquer à notre compilateur d'ajouter notre fichier de bibliothèque CS50 spécial, avec
clang -o string string.c -lcs50
, avec-l
pour « lier ».
- Il s'avère que nous devons également indiquer à notre compilateur d'ajouter notre fichier de bibliothèque CS50 spécial, avec
-
Nous pouvons même faire abstraction de cela et simplement taper
make string
. Nous voyons que, par défaut dans le bac à sable CS50,make
utiliseclang
pour compiler notre code à partir destring.c
enstring
, avec tous les arguments, ou drapeaux, nécessaires transmis.
Blocs Scratch en C
-
Le bloc “mettre [compteur] à (0)” crée une variable ; en C, nous écririons
int compteur = 0 ;
, oùint
précise que notre variable est un nombre entier :
-
“incrémenter [compteur] de (1)” équivaut en C à
compteur = compteur + 1 ;
. (En C, le symbole=
n’est pas un signe d’égalité, comme dans une équation oùcompteur
serait égal àcompteur + 1
. Au lieu de cela,=
est un opérateur d’affectation qui signifie « copier la valeur de droite dans la valeur de gauche ».) Et remarquez que nous n’avons plus besoin de préciserint
, car nous supposons que nous avons déjà spécifié auparavant quecompteur
est unint
, doté d’une valeur existante. Nous pouvons également écrirecompteur += 1 ;
oucompteur++ ;
qui sont tous deux du « sucre syntaxique », à savoir des raccourcis qui ont le même effet avec moins de caractères à taper.
-
Une condition correspondrait à :
if (x < y) { printf("x est inférieur à y\n"); }
- Remarquez qu’en C nous utilisons les caractères
{
et}
(ainsi que l’indentation) pour indiquer comment les lignes de code doivent être imbriquées.
- Remarquez qu’en C nous utilisons les caractères
-
Nous pouvons également avoir des conditions if-else :
if (x < y) { printf("x est inférieur à y\n"); } else { printf("x n'est pas inférieur à y\n"); }
- Remarquez que les lignes de code qui ne constituent pas elles-mêmes une action (
if...
et les accolades) ne se terminent pas par un point-virgule.
- Remarquez que les lignes de code qui ne constituent pas elles-mêmes une action (
-
Et même
else if
:if (x < y) { printf("x est inférieur à y\n"); } else if (x > y) { printf("x est supérieur à y\n"); } else if (x == y) { printf("x est égal à y\n"); }
- Remarquez que pour comparer deux valeurs en C, nous utilisons
==
, soit deux signes égal. - Et logiquement, nous n’avons pas besoin du
if (x == y)
dans la condition finale, car c’est le seul cas restant et nous pouvons simplement direelse
.
- Remarquez que pour comparer deux valeurs en C, nous utilisons
-
Les boucles peuvent être écrites comme suit :
while (true) { printf("bonjour, monde\n"); }
- Le mot-clé
while
nécessite également une condition, nous utilisons donctrue
comme expression booléenne pour nous assurer que notre boucle fonctionnera indéfiniment. Notre programme vérifiera si l’expression est évaluée àtrue
(ce qui sera toujours le cas ici), puis exécutera les lignes à l’intérieur des accolades. Ensuite, il répétera cette opération jusqu’à ce que l’expression ne soit plus vraie (ce qui ne changera pas dans ce cas).
- Le mot-clé
-
Nous pourrions faire quelque chose un certain nombre de fois avec
while
:
int i = 0; while (i < 50) { printf("bonjour, monde\n"); i++; }
- Nous créons une variable
i
et la définissons sur 0. Ensuite, tant quei < 50
, nous exécutons des lignes de code, et nous ajoutons 1 ài
après chaque exécution. - Les accolades autour des deux lignes à l’intérieur de la boucle
while
indiquent que ces lignes se répéteront, et nous pouvons ajouter des lignes à notre programme après si nous le souhaitons.
- Nous créons une variable
-
Pour effectuer la même répétition, nous pouvons plus couramment utiliser le mot-clé
for
:for (int i = 0; i < 50; i++) { printf("bonjour, monde\n"); }
- Encore une fois, nous créons d’abord une variable nommée
i
et la définissons sur 0. Ensuite, nous vérifions quei < 50
à chaque fois que nous atteignons le début de la boucle, avant d’exécuter le code à l’intérieur. Si cette expression est vraie, alors nous exécutons le code à l’intérieur. Enfin, après avoir exécuté le code à l’intérieur, nous utilisonsi++
pour ajouter un ài
et la boucle se répète.
- Encore une fois, nous créons d’abord une variable nommée
Types, formats, operateurs
- On peut utiliser d'autres types pour nos variables :
bool
, une expression booléenne de valeurtrue
oufalse
char
, un seul caractère commea
ou2
double
, une valeur à virgule flottante avec encore plus de chiffresfloat
, une valeur à virgule flottante, ou un nombre réel avec une valeur décimaleint
, des entiers jusqu'à une certaine taille, ou nombre de bitslong
, des entiers avec plus de bits, ce qui leur permet de compter plus hautstring
, une chaîne de caractères
- Et la bibliothèque CS50 dispose des fonctions correspondantes pour obtenir des entrées de différents types :
get_char
get_double
get_float
get_int
get_long
get_string
- Pour
printf
aussi, il existe différents espaces réservés pour chaque type :%c
pour les caractères%f
pour les flottants, doubles%i
pour les entiers%li
pour les longs%s
pour les chaînes
- Et il existe des opérateurs mathématiques que nous pouvons utiliser :
+
pour l'addition-
pour la soustraction*
pour la multiplication/
pour la division%
pour le reste
Plus d'exemples
- Pour chacun de ces exemples, vous pouvez cliquer sur les liens du bac à sable pour lancer et modifier vos propres copies.
-
Dans
int.c
, nous récupérons et affichons un entier :#include <cs50.h> #include <stdio.h> int main(void) { int age = get_int("Quel âge avez-vous ?\n"); int jours = age * 365; printf("Vous avez au moins %i jours.\n", jours); }
- Notez que nous utilisons
%i
pour afficher un entier. - Nous pouvons maintenant lancer
make int
et lancer notre programme avec./int
. -
Nous pouvons combiner des lignes et supprimer la variable
jours
avec :int age = get_int("Quel âge avez-vous ?\n"); printf("Vous avez au moins %i jours.\n", age * 365);
-
Ou même tout combiner sur une ligne :
printf("Vous avez au moins %i jours.\n", get_int("Quel âge avez-vous ?\n") * 365);
-
Cependant, si une ligne devient trop longue ou trop compliquée, il peut être préférable de conserver deux ou trois lignes pour plus de lisibilité.
- Notez que nous utilisons
-
Dans
float.c
, nous pouvons récupérer des nombres décimaux (ce qu'on appelle des valeurs à virgule flottante en informatique, parce que la virgule décimale peut « flotter » entre les chiffres, en fonction du nombre ):#include <cs50.h> #include <stdio.h> int main(void) { float prix = get_float("Quel est le prix ?\n"); printf("Votre total est de %f.\n", prix * 1,0625); }
- Maintenant, si nous compilons et lançons notre programme, nous verrons un prix affiché avec la taxe.
- Nous pouvons spécifier le nombre de chiffres imprimés après la virgule décimale avec un espace réservé tel que
%.2f
pour deux chiffres après la virgule décimale.
-
Avec
parity.c
, nous pouvons vérifier si un nombre est pair ou impair :#include <cs50.h> #include <stdio.h> int main(void) { int n = get_int("n : "); if (n % 2 == 0) { printf("pair\n"); } else { printf("impair\n"); } }
- Avec l'opérateur
%
(modulo), nous pouvons obtenir le reste den
après sa division par 2. Si le reste est 0, nous savons quen
est pair. Sinon, nous savons quen
est impair. - De plus, des fonctions comme
get_int
de la bibliothèque CS50 effectuent une vérification des erreurs, où seules les entrées de l'utilisateur correspondant au type que nous voulons sont acceptées.
- Avec l'opérateur
-
Dans
conditions.c
, nous transformons les extraits de conditions précédents en un programme :// Conditions et opérateurs relationnels #include <cs50.h> #include <stdio.h> int main(void) { // Demander à l'utilisateur de saisir x int x = get_int("x : "); // Demander à l'utilisateur de saisir y int y = get_int("y : "); // Comparer x et y if (x < y) { printf("x est inférieur à y\n"); } else if (x > y) { printf("x est supérieur à y\n"); } else { printf("x est égal à y\n"); } }
- Les lignes commençant par
//
sont des commentaires ou des notes pour les humains que le compilateur ignorera. - Pour que David compile et exécute ce programme dans son bac à sable, il devait d'abord exécuter
cd src1
dans le terminal. Cela change le répertoire, ou dossier, en celui dans lequel il a enregistré tous les fichiers sources du cours. Ensuite, il pouvait exécutermake conditions
et./conditions
. Avecpwd
, il peut voir qu'il se trouve dans un dossiersrc1
(à l'intérieur d'autres dossiers). Etcd
tout seul, sans arguments, nous ramènera à notre dossier par défaut dans le bac à sable.
- Les lignes commençant par
-
Dans
agree.c
, nous pouvons demander à l'utilisateur de confirmer ou d'infirmer quelque chose :// Opérateurs logiques #include <cs50.h> #include <stdio.h> int main(void) { // Demander à l'utilisateur d'accepter char c = get_char("Êtes-vous d'accord ?\n"); // Vérifier si l'utilisateur est d'accord if (c == 'Y' || c == 'y') { printf("D'accord.\n"); } else if (c == 'N' || c == 'n') { printf("Pas d'accord.\n"); } }
- Nous utilisons deux barres verticales,
||
, pour indiquer un « ou » logique, indiquant que l'une ou l'autre expression peut être vraie pour que la condition soit suivie. - Et si aucune des expressions n'est vraie, rien ne se passera puisque notre programme n'a pas de boucle.
- Nous utilisons deux barres verticales,
-
Mettons en oeuvre le programme de toux depuis la semaine 0 :
#include <stdio.h> int main(void) { printf("toux\n"); printf("toux\n"); printf("toux\n"); }
-
Nous pourrions utiliser une boucle
for
:#include <stdio.h> int main(void) { for (int i = 0; i < 3; i++) { printf("toux\n"); } }
- Par convention, les programmeurs ont tendance à commencer à compter à 0, ainsi
i
aura le valeurs0
,1
et2
avant de s'arrêter, pour un total de trois itérations. Nous pourrions également écrirefor (int i = 1; i <= 3; i++)
pour le même effet final.
- Par convention, les programmeurs ont tendance à commencer à compter à 0, ainsi
-
Nous pouvons déplacer la ligne
printf
vers sa propre fonction:#include <stdio.h> void toux(void); int main(void) { for (int i = 0; i < 3; i++) { toux(); } } void toux(void) { printf("toux\n"); }
- Nous déclarons une nouvelle fonction avec
void toux(void);
, avant que notre fonctionmain
ne l'appelle. Le compilateur C lit notre code de haut en bas, nous devons donc lui indiquer que la fonctiontoux
existe, avant que nous l'utilisions. Puis, après notre fonctionmain
, nous pouvons implémenter la fonctiontoux
. De cette façon, le compilateur sait que la fonction existe, et nous pouvons garder notre fonctionmain
près du début. - Et notre fonction
toux
ne prend aucune entrée, nous avons donctoux(void)
.
- Nous déclarons une nouvelle fonction avec
-
Nous pouvons abstraire d'avantage
toux
:#include <stdio.h> void toux(int n); int main(void) { toux(3); } void toux(int n) { for (int i = 0; i < n; i++) { printf("toux\n"); } }
- Maintenant, quand nous voulons afficher "toux" un certain nombre de fois, nous pouvons simplement appeler la même fonction. Notez qu'avec
void toux(int n)
, nous indiquons que la fonctiontoux
prend en entrée unint
, auquel nous nous référons commen
. Et à l'intérieur detoux
, nous utilisonsn
dans notre bouclefor
pour afficher "toux" le bon nombre de fois.
- Maintenant, quand nous voulons afficher "toux" un certain nombre de fois, nous pouvons simplement appeler la même fonction. Notez qu'avec
-
Regardons
positive.c
:#include <cs50.h> #include <stdio.h> int get_positive_int(void); int main(void) { int i = get_positive_int(); printf("%i\n", i); } // Demander à l'utilisateur un entier positif int get_positive_int(void) { int n; do { n = get_int("%s", "Entier positif : "); } while (n < 1); return n; }
- La bibliothèque CS50 n'a pas de fonction
get_positive_int
, mais nous pouvons en écrire une nous-mêmes. Notre fonctionint get_positive_int(void)
demande à l'utilisateur unint
et retourne cetint
, que notre fonctionmain
stocke commei
. Dansget_positive_int
, nous initialisons une variable,int n
, sans encore lui assigner de valeur. Puis, nous avons une nouvelle construction,do ... while
, qui fait quelque chose en premier, puis vérifie une condition, et répète cela jusqu'à ce que la condition ne soit plus vraie. - Une fois que la boucle se termine parce qu'on a un
n
qui n'est pas< 1
, on peut le retourner avec le mot-cléreturn
. Et de retour dans notre fonctionmain
, on peut définirint i
sur cette valeur.
- La bibliothèque CS50 n'a pas de fonction
Écrans
-
Nous pourrions avoir besoin d'un programme qui imprime une partie d'écran d'un jeu vidéo comme Super Mario Bros. Dans
mario0.c
, nous avons :// Affiche une ligne de 4 points d'interrogation #include <stdio.h> int main(void) { printf("????\n"); }
-
Nous pouvons demander à l'utilisateur un nombre de points d'interrogation, puis les imprimer avec
mario2.c
:#include <cs50.h> #include <stdio.h> int main(void) { int n; do { n = get_int("Largeur : "); } while (n < 1); for (int i = 0; i < n; i++) { printf("?"); } printf("\n"); }
-
Et nous pouvons imprimer un ensemble bidimensionnel de blocs avec
mario8.c
:// Affiche une grille de briques n-par-n avec une boucle #include <cs50.h> #include <stdio.h> int main(void) { int n; do { n = get_int("Taille : "); } while (n < 1); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { printf("#"); } printf("\n"); } }
-
Notez que nous avons deux boucles imbriquées, où la boucle externe utilise
i
pour tout faire à l'intérieurn
fois, et la boucle interne utilisej
, une variable différente, pour faire quelque chosen
fois pour chacune de ces fois. En d'autres termes, la boucle externe imprimen
« lignes » ou lignes, et la boucle interne imprimen
« colonnes » ou caractères#
dans chaque ligne. -
D'autres exemples non abordés en cours sont disponibles sous « Code source » pour Semaine 1.
Mémoire, imprécision et dépassement de capacité
- Notre ordinateur possède une mémoire, dans des puces de matériel appelées RAM, mémoire à accès aléatoire. Nos programmes utilisent cette RAM pour stocker des données pendant leur exécution, mais cette mémoire est finie. Donc, avec un nombre fini de bits, nous ne pouvons pas représenter tous les nombres possibles (dont il existe un nombre infini). Ainsi, notre ordinateur dispose d'un certain nombre de bits pour chaque flottant et entier, et doit arrondir à une valeur décimale donnée à un certain moment.
-
Avec
floats.c
, nous pouvons voir ce qui se passe lorsque nous utilisons des flottants :#include <cs50.h> #include <stdio.h> int main(void) { // Invite l'utilisateur à saisir x float x = get_float("x: "); // Invite l'utilisateur à saisir y float y = get_float("y: "); // Effectue une division printf("x / y = %.50f\n", x / y); }
- Avec
%50f
, nous pouvons spécifier le nombre de décimales affichées. -
Hum, maintenant nous obtenons …
x: 1 y: 10 x / y = 0.10000000149011611938476562500000000000000000000000
-
Il s'avère que cela s'appelle l'imprécision en virgule flottante, où nous n'avons pas assez de bits pour stocker toutes les valeurs possibles, de sorte que l'ordinateur doit stocker la valeur la plus proche possible de 1 divisé par 10.
- Avec
-
Nous pouvons voir un problème similaire dans
overflow.c
:#include <stdio.h> #include <unistd.h> int main(void) { for (int i = 1; ; i *= 2) { printf("%i\n", i); sleep(1); } }
- Dans notre boucle
pour
, nous définissonsi
sur1
et le multiplions par*= 2
. (Et nous allons continuer à le faire indéfiniment, donc il n'y a pas de condition que nous vérifions.) - Nous utilisons également la fonction
sleep
deunistd.h
pour permettre à notre programme de faire une pause à chaque fois. -
Maintenant, lorsque nous exécutons ce programme, nous voyons le nombre augmenter de plus en plus, jusqu'à :
1073741824 overflow.c:6:25: erreur d'exécution: dépassement d'entier signé: 1073741824 * 2 ne peut pas être représenté dans le type 'int' -2147483648 0 0 ...
-
Il s'avère que notre programme a reconnu qu'un entier signé (un entier avec un signe positif ou négatif) ne pouvait pas stocker cette prochaine valeur et a imprimé une erreur. Ensuite, puisqu'il essayait de le doubler quand même,
i
est devenu un nombre négatif, puis 0. - Ce problème est appelé dépassement de capacité d'entier, où un entier ne peut être que si grand avant de manquer de bits et de « déborder ». Nous pouvons imaginer ajouter 1 à 999 en décimal. Le dernier chiffre devient 0, nous reportons le 1 pour que le chiffre suivant devienne 0, et nous obtenons 1000. Mais si nous n'avions que trois chiffres, nous nous retrouverions avec 000 car il n'y a pas de place pour mettre le 1 final !
- Dans notre boucle
-
Le problème Y2K est survenu parce que de nombreux programmes stockaient l'année civile avec seulement deux chiffres, comme 98 pour 1998 et 99 pour 1999. Mais à l'approche de l'an 2000, les programmes auraient stocké 00, ce qui entraînerait une confusion entre les années 1900 et 2000.
- Un avion Boeing 787 avait également un bug où un compteur dans le générateur débordait après un certain nombre de jours de fonctionnement continu, car le nombre de secondes de fonctionnement ne pouvait plus être stocké dans ce compteur.
- Donc, nous avons vu quelques problèmes qui peuvent survenir, mais maintenant nous comprenons pourquoi et comment les éviter.
- Avec le problème de la semaine, nous utiliserons le CS50 Lab, basé sur le CS50 Sandbox, pour écrire quelques programmes avec des procédures pas à pas pour nous guider.