[Powered by Google Translate] [SECTION 5: Moins à l'aise]
[Nate Hardison, Université de Harvard]
[C'est CS50.] [CS50.TV]
Alors, bienvenue en arrière, les gars.
Bienvenue à la section 5.
À ce stade, après avoir terminé quiz 0 et après avoir vu comment vous avez fait,
j'espère que vous vous sentez vraiment bien parce que j'ai été très impressionné par les résultats de cette section.
Pour nos téléspectateurs en ligne, nous avons eu quelques questions
sur les deux derniers problèmes sur le problème posé - ou sur le quiz, plutôt.
Donc nous allons passer en revue les très rapidement afin que tout le monde voit ce qui s'est passé
et comment aller à travers la solution réelle plutôt que la seule visualisation de la solution elle-même.
Nous allons passer en revue les deux dernières de problèmes très rapidement, 32 et 33.
Juste, encore une fois, de sorte que les téléspectateurs en ligne peuvent le voir.
>> Si vous mettez à votre problème 32, qui est à la page 13,
13 sur 16, problème 32 est tout au sujet de swaps.
Il s'agissait de permuter deux entiers.
C'est le problème que nous étions partis sur une couple de fois dans conférence.
Et ici, ce que nous vous demandons de faire une trace de mémoire rapide.
Pour remplir les valeurs des variables comme ils sont sur la pile
que le code passe par cette fonction d'échange.
En particulier, ce que nous cherchons à - je vais mettre cette iPad vers le bas -
en particulier, ce que nous envisageons, c'est cette ligne numéro 6 ici.
Et il est numéroté 6 pour seulement contiguïté avec le problème précédent.
Ce que nous voulons faire est d'afficher ou étiqueter l'état de la mémoire
car il est à la fois lorsque nous exécutons ce nombre 6 lignes,
qui est effectivement un retour de notre fonction de permutation ici.
Si nous défiler vers le bas ici, nous avons vu que les adresses de tout ce que la mémoire a été prévu pour nous.
Cela est très essentiel, nous allons y revenir dans un instant.
Et puis, ici-bas au fond, nous avons eu un peu de mémoire schéma que nous allons faire référence.
En fait, j'ai fait ça sur mon iPad.
Donc, je vais alterner les deux sens entre l'iPad et ce code juste pour la référence.
>> Commençons. Tout d'abord, concentrons-nous sur les deux premières lignes de principal ici.
Pour commencer, nous allons initialiser à 1 x et y à 2.
Nous avons donc deux variables entières, ils sont tous les deux à être placé sur la pile.
Nous allons mettre un 1 et un 2 en eux.
Donc, si je retourne pour mon iPad, je l'espère, nous allons voir -
Apple TV en miroir, et voilà. D'accord.
Donc, si je retourne pour mon iPad,
Je tiens à initialiser à 1 x et y à 2.
Nous faisons cela tout simplement en écrivant un 1 dans la case x
et a 2 dans la case y. Assez simple.
Alors maintenant, revenons à l'ordinateur portable, voir ce qui se passe ensuite.
Donc cette ligne suivante est l'endroit où les choses se compliquent.
Nous passons l'adresse de x et de y l'adresse que les paramètres a et b de la fonction de permutation.
L'adresse de x et de y l'adresse sont des choses que nous ne pouvons pas calculer
sans se référer à ces points de balle juste ici.
Et heureusement, les deux premiers points nous dire exactement quelles sont les réponses.
L'adresse dans la mémoire de x est 10, et l'adresse de y dans la mémoire est 14.
Donc, ce sont les valeurs qui se transmises en tant que a et b là-haut dans notre fonction d'échange.
Encore une fois, le passage à notre schéma, je peux écrire un 10 dans une
et un 14 en b.
Maintenant, ce point est l'endroit où nous procédons à l'échange.
Alors renversant en arrière de l'ordinateur portable à nouveau,
nous voyons que la façon dont fonctionne le swap, c'est que je déréférencement d'abord un magasin et le résultat dans tmp.
Ainsi, l'opérateur de déréférencement dit: "Hé. Traiter le contenu de la variable a comme adresse.
Aller à tout ce qui est stocké à cette adresse, et le charger. "
Que vous chargez de la variable qui va être stocké dans notre variable tmp.
Retournement de retour à l'iPad.
Si nous allons à traiter 10, nous savons que l'adresse 10 est le x varible
parce qu'on nous a dit à notre point de balle que l'adresse de x dans la mémoire est de 10.
Ainsi, nous pouvons y aller, obtenir la valeur de celui-ci, qui est de 1, comme on le voit sur notre iPad,
et le recharger dans tmp.
Encore une fois, ce n'est pas le contenu final.
Nous allons marcher à travers et nous allons arriver à notre état final du programme à la fin.
Mais pour le moment, nous avons la valeur 1 stockée dans tmp.
>> Et il ya une petite question à poser ici.
[Alexander] Est l'opérateur de déréférencement - c'est juste le droit étoiles en face de la variable?
Oui >>. Ainsi, l'opérateur de déréférencement, que l'on s'apprête à notre ordinateur portable une fois de plus,
cette étoile est juste en face.
En ce sens, il est - vous l'opposer à l'opérateur de multiplication
ce qui nécessite deux choses: l'opérateur de déréférencement est un opérateur unaire.
Seulement appliqué à une valeur, par opposition à un opérateur binaire,
où vous appliquez à deux valeurs différentes.
Donc, c'est ce qui se passe dans cette ligne.
Nous avons chargé la valeur 1 et stockées dans notre variable entière temporaire.
La ligne suivante, nous stockons le contenu de b en -
ou, plutôt, nous stockons le contenu que b est en pointant vers l'endroit où une pointe.
Si nous analysons ce de droite à gauche, nous allons b déréférencement,
nous allons aborder 14, nous allons prendre l'entier qui est là,
puis nous allons aller à l'adresse 10,
et nous allons jeter le résultat de notre déréférencement de b dans cet espace.
Retournement à notre iPad, où nous pouvons faire cela un peu plus concret,
cela pourrait aider si je écrire les nombres sur toutes les adresses ici.
Donc, nous savons qu'à y, nous sommes à l'adresse 14, x est à l'adresse 10.
Quand nous commençons à b, nous déréférencer b, nous allons récupérer la valeur 2.
Nous allons saisir cette valeur parce que c'est la valeur qui vit à l'adresse 14.
Et nous allons le mettre dans la variable qui vit à l'adresse 10,
qui est là, ce qui correspond à nos variable x.
Ainsi, nous pouvons faire un peu d'écraser ici
où nous nous débarrassons de nos 1 et à la place nous écrire un 2.
Donc, tout va très bien dans le monde, même si nous avons x écrasés maintenant.
Nous avons stocké ancienne valeur x dans notre variable tmp.
Ainsi, nous pouvons terminer le swap avec la ligne suivante.
Retournement à notre ordinateur portable.
Maintenant tout ce qui reste est de prendre le contenu de notre variable entière temporaire
et de les stocker dans la variable qui vit à l'adresse que b est retenue.
Donc, nous allons b déréférencement efficace pour accéder à la variable
qui est à l'adresse que b possède en elle,
et nous allons farcir la valeur tmp tient en elle.
Retournement de retour à l'iPad une fois de plus.
Je peux effacer cette valeur ici, 2,
et à la place nous allons copier le droit 1 dans celui-ci.
Puis la ligne suivante qui exécute, bien sûr -
si nous retournons en arrière de l'ordinateur portable - est-ce 6 points,
qui est le point à partir duquel nous voulions avoir notre schéma complètement rempli.
Alors renversant en arrière de l'iPad une fois de plus, pour que vous puissiez voir le schéma terminé,
vous pouvez voir que nous avons un 10 dans un, un 14 en b, un 1 dans tmp, un 2 en x, et 1 en y.
Y at-il des questions à ce sujet?
Est-ce plus logique, après avoir marché à travers elle?
Moins de sens? J'espère que non. D'accord.
>> Les pointeurs sont un sujet très délicat.
Un des gars avec qui nous travaillons a un dicton très répandu:
«Pour comprendre les pointeurs, vous devez d'abord comprendre les pointeurs."
Je pense que c'est tout à fait vrai. Cela prend un certain temps pour s'habituer à elle.
Tirage au sort des photos, tirage au sort des schémas de mémoire comme celui-ci sont très utiles,
et après vous guidera à travers par exemple après exemple après exemple,
il va commencer à donner un sens un peu plus et le sentiment un peu plus et un peu plus de sens.
Enfin, un jour, vous aurez tout complètement maîtrisé.
Vous avez des questions avant de passer au problème suivant? Très bien.
Alors retournez à l'ordinateur portatif.
Le problème suivant est de 33, nous avons le problème numéro un dans le dossier I / O.
Zoom sur cela un peu petite.
Problème 33 - Oui?
>> [Daniel] Je viens d'avoir une petite question. Cette étoile, ou l'astérisque,
ça s'appelle le déréférencement lorsque vous utilisez un astérisque avant.
Comment ça s'appelle lorsque vous utilisez l'esperluette avant?
L'esperluette est >> avant l'adresse de l'opérateur.
Donc, nous allons faire défiler vers le haut.
Oops. Je suis en mode zoom donc je ne peux pas vraiment défilement.
Si nous regardons ce code très rapidement ici,
à nouveau, la même chose se passe.
Si nous regardons ce code ici, sur cette ligne où nous faisons l'appel à échanger,
l'esperluette est juste en disant "obtenir l'adresse à laquelle vit la variable x."
Lorsque votre compilateur compile votre code,
il doit marquer physiquement une place en mémoire pour toutes vos variables à vivre.
Et qu'est-ce que le compilateur peut alors faire une fois qu'il est compilé tout,
il sait: "Oh, je mets x à l'adresse 10. J'ai mis y à l'adresse 14."
Il peut ensuite remplir ces valeurs pour vous.
Ainsi, vous pouvez alors - il peut alors passer cette séquence et en y passe et en tant que bien.
Ces gars-là obtenir l'adresse, mais aussi, quand vous les croisez dans la fonction de permutation,
ce type d'information, cette int * ici, indique au compilateur,
«D'accord, nous allons interpréter cette adresse comme adresse d'une variable entière."
Comme une adresse d'un int, qui est différente de l'adresse d'une variable de caractère
parce qu'un int occupe, sur une machine 32-bit, occupe 4 octets d'espace,
alors qu'un personnage prend seulement 1 octet de l'espace.
Il est donc important de savoir aussi ce qui est - ce qui vit, ce type de valeur
vit à l'adresse que s'est passé po
Ou à l'adresse que vous avez à faire.
De cette façon, vous savez combien d'octets d'informations à charger réellement de votre RAM.
Et puis, oui, opérateur de déréférencement, comme vous me demandiez,
va et accède à des informations à une adresse donnée.
Donc, dit-il, avec cette variable a ici, de traiter le contenu d'un comme une adresse,
aller à cette adresse, et tirez, chargez-le dans le processeur, la charge dans un registre
les valeurs réelles ou les contenus qui vivent à cette adresse.
D'autres questions? Ce sont de bonnes questions.
C'est beaucoup trop d'une nouvelle terminologie.
C'est aussi un peu funky, voir & * et dans des lieux différents.
>> Très bien.
Mais revenons au problème 33, déposer I / O.
Ce fut l'un de ces problèmes que je pense d'un certain nombre de choses qui s'est passé.
Premièrement, il s'agit d'un sujet assez nouveau.
Il a été présenté très bientôt avant le test,
et puis je pense que c'était un peu comme un de ces problèmes de mots en mathématiques
où ils vous donnent beaucoup d'informations, mais vous avez réellement ne finissent pas avoir à utiliser une tonne de celui-ci.
La première partie de ce problème est de décrire ce qu'est un fichier CSV est.
Maintenant, un fichier CSV, selon la description, est un fichier de valeurs séparées par des virgules.
La raison pour laquelle celles-ci sont toutes intéressantes, et la raison pour laquelle vous jamais les utiliser,
est, parce que, combien d'entre vous ont déjà utilisé des trucs comme Excel?
Figure plupart d'entre vous ont, sans doute, ou utiliseront à un certain moment dans votre vie.
Vous allez utiliser quelque chose comme Excel.
Afin d'obtenir les données d'une feuille de calcul Excel ou de faire toute sorte de traitement avec elle,
si vous voulez écrire un programme C ou programme Python, programme Java,
pour traiter les données que vous avez stockées dedans,
l'une des façons les plus courantes pour le sortir est dans un fichier CSV.
Et vous pouvez ouvrir Excel et quand vous allez à la «Enregistrer sous» le dialogue,
vous pouvez sortir un fichier CSV réelle.
>> Bon à savoir comment faire face à ces choses.
La façon dont cela fonctionne est que c'est similaire à - je veux dire, c'est essentiellement imitant une feuille de calcul,
où, comme nous le voyons ici, dans la pièce très extrême gauche,
nous avons tous les noms de famille.
Nous avons donc Malan, puis Hardison, puis Bowden, MacWilliam, puis Chan.
Tous les noms de famille. Et puis, une virgule sépare les noms de famille des prénoms.
David, Nate, Rob, Tommy, et Zamyla.
J'ai toujours mélanger Robby et Tom.
Et puis, enfin, la troisième colonne est l'adresse e-mail.
Une fois que vous comprenez cela, le reste du programme est assez simple à mettre en œuvre.
Ce que nous avons fait dans le but d'imiter cette structure même de notre programme C
est que nous avons utilisé une structure.
Nous allons commencer à jouer avec ceux-ci un peu plus aussi.
Nous les avons vus pour le premier bit peu en 3 set problème, lorsque nous traitions avec les dictionnaires.
Mais cette structure du personnel enregistre un nom de famille, un prénom et un email.
Tout comme notre fichier CSV a été de le ranger.
Donc, ce n'est que de la conversion d'un format à un autre.
Nous devons convertir, dans ce cas, une structure du personnel dans une ligne,
une ligne séparée par des virgules, juste comme ça.
Est-ce logique? Vous avez tous répondu à ce questionnaire,
donc j'imagine que vous avez au moins eu le temps de réfléchir à ce sujet.
>> Dans la fonction de location, le problème nous demande de prendre en - Zoom sur cette we'll peu un peu -
prendre dans une structure de personnel, une structure du personnel, avec le nom de s,
et ajouter son contenu à notre fichier staff.csv.
Il s'avère que c'est assez simple à utiliser.
Nous allons sorte de jouer avec ces fonctions un peu plus aujourd'hui.
Mais dans ce cas, la fonction fprintf est vraiment la clé.
Donc, avec fprintf, nous pouvons imprimer, tout comme vous les gars ont eu recours à ce terme printf ensemble.
Vous pouvez printf une ligne dans un fichier.
Ainsi, au lieu de se contenter de l'appel d'habitude printf où vous lui donnez la chaîne de format
et puis vous remplacez toutes les variables avec les arguments suivants,
avec fprintf, votre argument premier est plutôt le fichier que vous souhaitez écrire.
Si nous devions examiner cette question dans l'appareil, par exemple, l'homme fprintf,
nous pouvons voir la différence entre printf et fprintf.
Je vais faire un zoom avant ici un peu.
Donc, avec printf, nous lui donnons une chaîne de format, puis les arguments suivants
sont toutes les variables de remplacement ou de substitution dans notre chaîne de format.
Alors qu'avec fprintf, le premier argument est en effet ce fichier * appelé un ruisseau.
>> De retour ici à notre location,
nous avons déjà eu notre flux * fichier ouvert pour nous.
C'est ce que cette première ligne fait, il ouvre le fichier staff.csv,
il l'ouvre en mode ajout, et tout ce qui nous reste à faire est
écrire la structure du personnel dans le fichier.
Et, voyons, ce que je veux utiliser l'iPad?
Je vais utiliser l'iPad. Nous avons void - Mettons cela sur la table pour que je puisse écrire un peu mieux -
annuler la location et la prend dans un argument, une structure du personnel appelé s.
Vous avez nos accolades, nous avons notre fichier appelé fichier *,
nous avons notre ligne fopen qui nous est donnée,
et je vais l'écrire sous forme de points, car il est déjà dans le pedia.
Et puis, sur notre ligne suivante, nous allons faire un appel à fprintf
et nous allons passer dans le fichier que l'on veut imprimer,
et puis notre chaîne de format, ce qui -
Je vais vous laisser me dire à quoi il ressemble.
Que diriez-vous, Stella? Savez-vous ce que la première partie de la chaîne de format ressemble?
[Stella] Je ne suis pas sûr. >> N'hésitez pas à demander Jimmy.
Savez-vous, Jimmy?
[Jimmy] Serait-il juste être le dernier? Je ne sais pas. Je ne suis pas entièrement sûr de.
Ok >>. Que diriez-vous, quelqu'un at-il obtenir ce bon à l'examen?
Non bien.
Il s'avère que ici tout ce que nous avons à faire, c'est que nous voulons que chaque partie de la structure du personnel
être imprimés sous forme de chaîne de caractères dans notre fichier.
Nous venons d'utiliser le caractère substitution de chaîne à trois reprises différentes parce que nous avons un nom de famille
suivi par une virgule, puis un prénom suivi d'une virgule,
et enfin l'adresse email qui est suivi - ce qui n'est pas
montage sur mon écran - mais il est suivi par un caractère de nouvelle ligne.
Donc, je vais l'écrire tout là-bas.
Et puis, à la suite de notre chaîne de format,
nous devons juste les substitutions, qui nous accédons à l'aide de la notation par points
que nous avons vu en 3 set problème.
Nous pouvons utiliser s.last, s.first et s.email
de substituer à ces trois valeurs dans notre chaîne de format.
Alors, comment ça s'est passé? Donner un sens?
Oui? Non? Peut-être? D'accord.
>> La dernière chose que nous faisons lorsque nous avons imprimé et après nous avons ouvert notre dossier:
chaque fois que nous avons ouvert un dossier, il faut toujours se rappeler pour la refermer.
Parce que sinon nous finirons par une fuite de la mémoire,
à l'aide des descripteurs de fichiers.
Donc, pour fermer, ce qui est la fonction que nous utilisons? Daniel?
[Daniel] fclose? Fclose >>, exactement.
Ainsi, la dernière partie de ce problème consistait à fermer correctement le fichier, utilisez la fonction fclose,
qui ressemble à cela.
Pas trop fou.
Cool.
Donc, c'est le problème 33 du quiz.
Nous aurons certainement plus de fichiers I / O à venir.
Nous allons faire un peu plus dans la conférence d'aujourd'hui, ou à l'article d'aujourd'hui,
parce que c'est ce qui va se former l'essentiel de cette pset à venir.
Passons à partir du questionnaire à ce stade. Oui?
>> [Charlotte]] Pourquoi fclose (fichier) à la place de fclose (staff.csv)?
Ah >>. Parce qu'il se trouve que - si la question, ce qui est un grand,
C'est pourquoi, lorsque nous écrivons fclose, nous écrit variables fclose astérisque (fichier)
plutôt que le nom du fichier, staff.csv? Est-ce exact? Ouais.
Donc, nous allons jeter un coup d'oeil. Si je reviens à mon ordinateur portable,
et regardons la fonction fclose.
Ainsi, la fonction fclose ferme un ruisseau et qu'il prend dans le pointeur sur le flux que nous voulons combler,
plutôt que le nom du fichier que l'on veut fermer.
Et c'est parce que dans les coulisses, lorsque vous effectuez un appel à fopen,
lorsque vous ouvrez un fichier, vous êtes en train de l'allocation de mémoire pour stocker des informations sur le fichier.
Donc, vous avez pointeur de fichier qui contient des informations sur le fichier,
comme il est ouvert, sa taille, où vous êtes actuellement dans le fichier,
de sorte que vous pouvez faire la lecture et l'écriture des appels à cet endroit particulier dans le fichier.
On finit par fermer le pointeur au lieu de fermer le nom du fichier.
>> Oui? [Daniel] Ainsi, afin d'utiliser de location, diriez-vous - comment ça se l'entrée utilisateur?
Est-ce que fprintf agir comme GetString dans le sens où elle va attendre pour l'entrée de l'utilisateur
et je vous demande de taper ce - ou attendre que vous tapiez ces trois choses?
Ou avez-vous besoin d'utiliser quelque chose pour mettre en œuvre la location?
Ouais >>. Donc, nous ne sommes pas - la question a été, comment pouvons-nous obtenir l'entrée utilisateur
pour mettre en œuvre la location? Et ce que nous avons ici est l'interlocuteur de la location,
passer dans cette structure personnel avec toutes les données stockées dans la structure déjà.
Il en est de fprintf en mesure de simplement écrire les données directement dans le fichier.
Il n'y a pas d'attente pour l'entrée utilisateur.
L'utilisateur a déjà donné l'entrée en bien de le mettre dans cette structure du personnel.
Et les choses, bien sûr, se briserait si l'un de ces pointeurs sont nulles,
donc nous revenir en arrière ici et nous regardons notre structure.
Nous avons la dernière chaîne, première chaîne, chaîne courriel.
Nous savons maintenant que tous ceux qui vraiment, sous le capot, sont variables char *.
Qui peuvent ou peuvent ne pas être pointant sur NULL.
Ils peuvent être pointant vers la mémoire sur le tas,
peut-être la mémoire sur la pile.
Nous ne savons pas vraiment, mais si l'un de ces pointeurs sont nuls ou invalides,
que cela va certainement planter notre fonction de la location.
C'était quelque chose qui était un peu au-delà de la portée de l'examen.
Nous ne sommes pas s'inquiéter à ce sujet.
Grande. D'accord. Ainsi, le déplacement à partir du quiz.
>> Fermons ce gars-là, et nous allons nous pencher sur pset 4.
Donc, si vous les gars regardez la spécification pset, une fois que vous pouvez y accéder, cs50.net/quizzes,
nous allons passer par quelques-uns des problèmes actuels section.
Je suis défilement vers le bas - section de questions débute à la troisième page de la spécification pset.
Et la première partie vous demande d'aller voir le court et sur la réorientation des tuyaux.
Qui était une sorte de court cool, vous montre quelques nouvelles fraîches, des tours de ligne de commande que vous pouvez utiliser.
Et puis nous avons quelques questions pour vous aussi.
Cette première question sur les flux, à laquelle printf écrit par défaut,
nous avons un peu parlé un peu tout à l'heure.
Cette fprintf que nous venons de discuter prend dans un flux de fichier * comme argument.
fclose prend dans un flux de fichier * ainsi,
et la valeur de retour de la fonction fopen vous donne un flux de fichier * ainsi.
La raison pour laquelle nous n'avons pas vu ceux d'avant, lorsque nous avons traité avec printf
C'est parce que printf est un flux par défaut.
Et le flux par défaut à laquelle il écrit
vous renseigner sur la courte.
Il faut absolument jeter un oeil à celui-ci.
>> Dans la section d'aujourd'hui, nous allons parler un peu de GDB,
car plus vous êtes familier avec elle, le plus pratique, vous obtenez avec elle,
le mieux vous serez réellement chasser les bugs dans votre propre code.
Cela accélère le processus de débogage énormément augmenté.
Ainsi, en utilisant printf, chaque fois que vous faites cela, vous devez recompiler votre code,
vous devez l'exécuter à nouveau, il faut parfois se déplacer autour de l'appel printf,
commenter le code, il faut du temps.
Notre objectif est d'essayer de vous convaincre que avec GDB, vous pouvez essentiellement
printf quoi que ce soit à n'importe quel point dans votre code et vous n'aurez jamais à le recompiler.
Vous n'avez jamais à démarrer et maintenir deviner où printf suivante.
La première chose à faire est de copier cette ligne et obtenir le code de section hors de la bande.
Je copie cette ligne de code qui dit: «http://cdn.cs50.net wget".
Je vais le copier.
Je vais aller à mon appareil, un zoom arrière afin que vous puissiez voir ce que je fais,
de le coller là-dedans, et quand je la touche Entrée, cette commande wget est littéralement une toile obtenir.
Il va tirer vers le bas de ce fichier à partir d'Internet,
et il va l'enregistrer dans le répertoire courant.
Maintenant, si je inscrire mon répertoire courant, vous pouvez voir que j'ai ce fichier section5.zip juste là.
La façon de traiter ce type est de le décompresser,
que vous pouvez faire en ligne de commande, juste comme ça.
Section5.zip.
Ça décompressez-le, créez le dossier pour moi,
gonfler la totalité du contenu, de les mettre là-dedans.
Alors maintenant, je peux aller dans mon article 5 du répertoire en utilisant la commande cd.
Effacer l'écran en utilisant clair. Donc effacer l'écran.
Maintenant, j'ai un bon terminal propre à traiter.
>> Maintenant, si je liste tous les fichiers que je vois dans ce répertoire,
vous voyez que j'ai quatre fichiers: buggy1, buggy2, buggy3 et buggy4.
J'ai aussi leurs correspondants. Fichiers c.
Nous n'allons pas regarder les fichiers c. Pour l'instant.
Au lieu de cela, nous allons les utiliser quand nous ouvrons GDB.
Nous les avons maintenus autour de sorte que nous avons accès au code source réelle lorsque nous utilisons GDB,
mais le but de cette partie de la section est de bricoler avec GDB
et voir comment nous pouvons l'utiliser pour comprendre ce qui ne va pas avec chacune de ces quatre programmes en buggy.
Donc, nous allons juste autour de la salle très rapidement,
et je vais demander à quelqu'un d'exécuter un des programmes buggy,
et ensuite nous passerons en tant que groupe par le biais de GDB, et nous verrons ce que nous pouvons faire pour corriger ces programmes,
ou au moins d'identifier ce qui va mal dans chacun d'eux.
Commençons ici avec Daniel. Est-ce que vous exécutez buggy1? Voyons voir ce qui se passe.
[Daniel] Il dit qu'il ya une erreur d'application. Ouais >>. Exactement.
Donc, si je lance buggy1, j'obtiens une erreur seg.
À ce stade, je pourrais continuer et ouvrir buggy1.c,
essayer de comprendre ce qui va mal,
mais l'une des choses les plus désagréables au sujet de cette erreur de défaillance de segments
c'est qu'il ne vous dit pas sur quelle ligne de ce programme fait a mal tourné et s'est cassé.
Vous devez sorte de regarder le code
et de déterminer à l'aide deviner et vérifier ou printf pour voir ce qui ne va pas.
L'une des choses les plus cool à propos de GDB, c'est qu'il est vraiment, vraiment facile
de comprendre la ligne à laquelle votre programme plante.
Il est tout à fait vaut la peine de l'utiliser, même si c'est juste pour ça.
Donc, pour démarrer GDB, je tape GDB, puis je lui donne le chemin vers l'exécutable que je veux courir.
Ici, je vais taper gdb ./buggy1.
Appuyez sur Entrée. Donne-moi toutes ces informations le droit d'auteur,
et ici vous verrez cette ligne qui dit: «symboles de lecture de / home /
jharvard/section5/buggy1. "
Et si tout se passe bien, vous verrez qu'il affichera un message qui ressemble à ceci.
Il va lire les symboles, il dira: «Je suis la lecture de symboles de votre fichier exécutable,"
et alors il aura ce message «done» ici.
Si vous voyez une autre variante de ce, ou voyez-vous il n'a pas pu trouver les symboles
ou quelque chose comme ça, qu'est-ce que cela signifie, c'est que vous n'avez tout simplement pas compilé votre exécutable correctement.
Quand nous compiler des programmes pour une utilisation avec GDB, nous devons utiliser ce drapeau spéciale-g,
et qui est fait par défaut si vous compilez vos programmes, simplement en tapant la commande make
ou faire buggy ou faire récupérer, l'un de ceux.
Mais si vous compilez manuellement avec Clang, alors vous aurez à aller à l'intérieur et inclure cette option-g.
>> À ce stade, maintenant que nous avons notre prompt de GDB,
il est assez simple pour exécuter le programme.
On peut soit saisir run, ou nous pouvons simplement taper r.
La plupart des commandes GDB peut être abrégée.
Habituellement, pour seulement un ou quelques lettres d'un couple, ce qui est assez agréable.
Alors Saad, si vous tapez r et appuyez sur Entrée, ce qui se passe?
[Saad] J'ai eu SIGSEGV, faute de segmentation, et puis tout ce charabia.
Ouais >>.
Comme nous voyons sur l'écran en ce moment, et comme Saad dit:
quand on tape RUN ou R et appuyez sur Entrée, on obtient encore la faute seg même.
Donc, en utilisant GDB ne résout pas notre problème.
Mais il nous donne une certaine langue de bois, et il s'avère que ce charabia
nous dit en fait que ça se passe.
Pour analyser ce bit un peu, ce premier bit est la fonction dans laquelle tout ce qui ne va pas.
Il ya ce __ strcmp_sse4_2, et il nous dit que cela se passe dans ce fichier
appelé sysdeps/i386, tout cela, à nouveau, une sorte de désordre - mais la ligne 254.
C'est un peu difficile à analyser. Habituellement, lorsque vous voyez des trucs comme ça,
ce qui signifie qu'il est seg failles dans l'une des bibliothèques du système.
Donc, quelque chose à voir avec strcmp. Les gars, vous avez vu avant strcmp.
Pas trop fou, mais cela signifie que strcmp est cassé ou qu'il ya un problème avec strcmp?
Que pensez-vous, Alexander?
[Alexander] Est-ce que - est de 254 la ligne? Et le - et non le binaire, mais ce n'est pas leurs plafonds,
et puis il ya une autre langue pour chaque fonction. Est-ce que 254 à cette fin, ou -?
>> C'est la ligne 254. On dirait que dans ce fichier. S, de sorte qu'il a probablement code assembleur.
>> Mais, je pense que la chose la plus pressante, parce que nous avons obtenu une faute seg,
et on dirait que ça vient de la fonction strcmp,
ce que cela signifie, alors, que strcmp est cassé?
Il ne devrait pas, je l'espère. Alors juste parce que vous avez une erreur de segmentation
dans l'une des fonctions du système, généralement cela signifie que vous n'avez tout simplement pas appelée correctement.
Le plus rapide chose à faire pour comprendre ce qui se passe réellement
quand vous voyez quelque chose de fou comme ça, quand vous voyez une faute seg,
surtout si vous avez un programme qui aide plus que simplement principale,
est d'utiliser un backtrace.
J'abrège trace en écrivant bt, par opposition au mot backtrace complet.
Mais Charlotte, qu'est-ce qui se passe quand vous tapez bt et appuyez sur Entrée?
[Charlotte] Il me montre deux lignes, 0 ligne et la ligne 1.
Ouais >>. Ainsi, la ligne 0 et la ligne 1.
Ce sont les cadres de pile réels qui sont actuellement en jeu lorsque votre programme s'est écrasé.
A partir de la trame la plus haute, la frame 0, et aller au fond plus, ce qui est l'image 1.
Notre cadre le plus élevé est le cadre strcmp.
Vous pouvez considérer cela comme semblable à ce problème, nous ne faisions que sur le questionnaire avec les pointeurs,
où nous avions échanger cadre de pile au-dessus du cadre de pile principale,
et nous avons eu les variables qui utilisent swap a été au-dessus des variables principale a été l'aide.
Voici notre accident s'est produit dans notre fonction strcmp, qui a été appelé par notre fonction principale,
et backtrace nous donne non seulement les fonctions dont les choses ont échoué,
mais c'est aussi nous dire où tout a été appelée.
Donc, si je défiler sur un peu plus vers la droite,
nous pouvons voir que oui, nous étions à la ligne 254 de ce fichier strcmp-sse4.s.
Mais l'appel a été fait à buggy1.c, ligne 6.
Cela signifie que nous pouvons faire - c'est que nous pouvons simplement aller vérifier et voir ce qui se passait
à buggy1.c, ligne 6.
Encore une fois, il ya deux manières de le faire. La première consiste à sortir du GDB
ou avoir votre code s'ouvrira dans une nouvelle fenêtre et des références croisées.
Cela, en soi, est très pratique parce que maintenant si vous êtes aux heures de bureau
et vous avez un défaut de segmentation et de votre carte de TF se demande où tout se brisait,
vous pouvez juste dire: "Oh, ligne 6. Je ne sais pas ce qui se passe,
mais quelque chose dans la ligne 6 est à l'origine de mon programme à briser. "
L'autre façon de le faire est que vous pouvez utiliser cette commande appelée liste de GDB.
Vous pouvez également l'abréger par l.
Donc, si nous avons atteint l, que faisons-nous ici?
Nous obtenons un tas de trucs bizarres.
Ceci est le code assembleur réelle
c'est-à strcmp_sse4_2.
Cet air un peu funky,
et la raison pour laquelle nous obtenons c'est parce qu'en ce moment,
GDB nous a dans la trame 0.
>> Donc, chaque fois que nous sommes à votre écoute, une fois que nous regardons le code source,
nous nous penchons sur le code source qui a trait à la trame de pile que nous sommes actuellement po
Ainsi, afin d'obtenir quelque chose de significatif, nous devons
passer à un cadre de pile qui est plus logique.
Dans ce cas, le cadre de pile principale serait logique un peu plus,
parce que c'était réellement le code que nous avons écrit.
Pas le code strcmp.
La façon dont vous pouvez vous déplacer entre les images, dans ce cas, parce que nous en avons deux,
on a 0 et 1,
vous faire avec le haut et vers le bas des commandes.
Si je déménage en place un cadre,
maintenant je suis dans la pile principale.
Je peux me déplacer vers le bas pour revenir à l'endroit où je me trouvais,
remonter, redescendre et remonter.
Si jamais vous faites votre programme dans GDB, vous obtenez un crash, vous obtenez la trace,
et vous voyez que c'est dans un fichier que vous ne savez pas ce qui se passe.
Vous essayez liste, le code ne semble pas familier pour vous,
jetez un oeil à vos cadres et de savoir où vous êtes.
Vous êtes probablement dans le cadre de pile mal.
Ou au moins vous êtes dans un cadre de pile qui n'est pas celui que vous pouvez vraiment déboguer.
Maintenant que nous sommes dans le cadre de pile appropriée, nous sommes en principal,
nous pouvons maintenant utiliser la commande list pour comprendre ce que la ligne était.
Et vous pouvez le voir, il a imprimé pour nous ici.
Mais nous pouvons atteindre la liste de tous les mêmes, et la liste nous donne cette impression agréable
du code source réel ce qui se passe ici.
>> En particulier, nous pouvons regarder à la ligne 6.
Nous pouvons voir ce qui se passe ici.
Et il semble que nous faisons une comparaison de chaînes
entre les chaînes "CS50" rochers et argv [1].
Quelque chose à propos de cette plantait.
Donc, Missy, avez-vous des idées sur ce qui pourrait se passer ici?
[Missy] Je ne sais pas pourquoi il se plantent. >> Tu ne sais pas pourquoi il s'écraser?
Jimmy, des idées?
[Jimmy] Je ne suis pas tout à fait sûr, mais la dernière fois que nous avons utilisé comparaison de chaîne,
ou strcmp, comme nous avons eu trois cas différents sous lui.
Nous n'avons pas d'==, je ne pense pas, raison de dire que la première ligne.
Au lieu de cela il a été séparé en trois, et celui-ci était == 0,
celui-ci était <0, je pense, et celui-ci était> 0.
Alors peut-être quelque chose comme ça? Ouais >>. Donc, il ya cette question
faisons-nous de la comparaison correctement?
Stella? Toute pensée?
[Stella] Je ne suis pas sûr. >> Je ne sais pas. Daniel? Pensées? D'accord.
Il s'avère ce qui se passe ici, c'est quand nous avons lancé le programme
et nous avons obtenu la faute seg, lorsque vous avez exécuté le programme pour la première fois, Daniel,
avez-vous lui donner tous les arguments de ligne de commande?
[Daniel] No. No. >> Dans ce cas, quelle est la valeur de argv [1]?
>> Il n'y a pas de valeur. Droit >>.
Eh bien, il n'ya pas de valeur de chaîne appropriée.
Mais il ya une certaine valeur. Quelle est la valeur qui est stockée là-dedans?
Une valeur >> des ordures? >> C'est soit une valeur ordures ou, dans ce cas,
la fin du tableau argv est toujours terminée par null.
Alors, que fait été stockées dans il est nul.
L'autre façon de résoudre ce problème, plutôt que de penser à travers,
est d'essayer de l'imprimer.
C'est là que je disais que l'utilisation de GDB est grande,
parce que vous pouvez imprimer toutes les variables, toutes les valeurs que vous souhaitez
en utilisant cette commande handy-dandy p.
Donc, si je tape p et ensuite je tape la valeur d'une variable ou le nom d'une variable,
dire, argc, je vois que argc est 1.
Si je veux imprimer argv [0], je peux le faire comme ça.
Et comme nous l'avons vu, argv [0] est toujours le nom de votre programme,
toujours le nom de l'exécutable.
Ici vous pouvez voir qu'il a le nom de chemin complet.
Je peux aussi imprimer argv [1] et de voir ce qui se passe.
>> Ici, nous avons eu ce genre de valeur mystique.
Nous avons eu cette 0x0.
Rappelez-vous au début de la période où nous avons parlé de nombres hexadécimaux?
Ou que peu de doute à la fin de pset 0 sur la façon de représenter 50 en hexadécimal?
La façon d'écrire les nombres hexadécimaux dans CS, juste pour ne pas nous confondre
avec des nombres décimaux, c'est que nous toujours les préfixer par 0x.
Donc, ce préfixe 0x toujours signifie simplement interpréter le numéro suivant comme un nombre hexadécimal,
non pas comme une chaîne et non comme un nombre décimal, et non comme un nombre binaire.
Comme le nombre 5-0 est un nombre en hexadécimal valide.
Et c'est un nombre en décimal, 50.
Donc, c'est juste la façon dont nous lever l'ambiguïté.
Donc 0x0 moyens hexadécimal compris entre 0, ce qui est également décimale 0, binaire 0.
C'est juste la valeur 0.
Il s'avère que c'est ce nul est, en fait, dans la mémoire.
Null est juste 0.
Ici, l'élément stocké à argv [1] est nulle.
Nous essayons donc de comparer notre "CS50 rochers" chaîne à une chaîne vide.
Donc déréférencement nulle, essayez d'accéder à des choses nulles,
ceux-ci sont généralement va provoquer une sorte de défaut de segmentation ou d'autres mauvaises choses se produisent.
Et il s'avère que strcmp ne vérifie pas
si oui ou non vous avez passé dans une valeur qui est nulle.
Au contraire, il va juste devant, tente de faire sa chose,
et si elle seg défauts, il seg défauts, et c'est votre problème. Tu dois aller le réparer.
Très rapidement, comment pourrions-nous résoudre ce problème? Charlotte?
[Charlotte] Vous pouvez vérifier si l'aide.
Donc, si argv [1] est nulle, == 0, puis revenez 1, ou quelque chose [inintelligible].
Ouais >>. Donc, c'est une excellente façon de le faire, comme on peut le vérifier,
la valeur que nous sommes sur le point de passer dans strcmp, argv [1], est-elle nulle?
Si c'est nul, alors on peut dire ok, avorter.
>> Une façon plus courante de le faire est d'utiliser la valeur argc.
Vous pouvez voir ici au début du principal,
que nous avons omis premier test que nous faisons quand nous généralement utiliser des arguments de ligne de commande,
qui consiste à tester si oui ou non la valeur de notre argc est ce que nous attendons.
Dans ce cas, nous nous attendons au moins deux arguments,
le nom du programme, plus une autre.
Parce que nous sommes sur le point d'utiliser le second argument ici.
Donc, avoir une sorte de test au préalable, avant notre appel strcmp
que les tests si oui ou non argv est au moins 2, serait aussi faire le même genre de chose.
Nous pouvons voir si cela fonctionne en exécutant à nouveau le programme.
Vous pouvez toujours redémarrer votre programme dans GDB, qui est vraiment agréable.
Vous pouvez courir, et quand vous passez des arguments à votre programme,
vous les croisez lorsque vous appelez courir, pas lorsque vous démarrez GDB.
De cette façon, vous pouvez garder votre programme en invoquant des arguments différents à chaque fois.
Alors courez, ou encore, je peux taper r, et voyons ce qui se passe si on tape "bonjour".
Il vous demandera toujours si vous voulez le lancer depuis le début.
Habituellement, vous ne souhaitez démarrer depuis le début.
Et à ce stade, il redémarre à nouveau, il imprime
le programme que nous utilisons, buggy1, avec l'argument bonjour,
et l'imprime sur cette norme, il dit, "Vous obtenez un D," visage triste.
Mais nous n'avons pas seg faute. Il a déclaré que le processus s'est terminé normalement.
Alors que semble assez bon.
Pas de défaut plus seg, nous en avons fait passé,
de sorte qu'il ressemble tel était effectivement le bug faute seg que nous recevions.
Malheureusement, il nous dit que nous obtenons un D.
>> Nous ne pouvons revenir en arrière et regarder le code et voir ce qui se passe là-bas
de comprendre ce qui était - pourquoi il nous a dit que nous avons obtenu un D.
Voyons voir, ici a ce printf dire que tu as un D.
Si nous tapons la liste, que vous gardez liste tapant, il conserve une itération à travers de votre programme,
donc ça va vous montrer les premières lignes de votre programme.
Ensuite, il va vous montrer les quelques lignes qui suivent, et le morceau suivant et le morceau suivant.
Et ça va continuer à essayer de descendre.
Et maintenant, nous allons arriver à «aligner le numéro 16 est hors de portée."
Parce qu'il a seulement 15 lignes.
Si vous arrivez à ce point, et vous vous demandez, "Qu'est-ce que je fais?" vous pouvez utiliser la commande help.
Utilisez aider et puis de lui donner le nom d'une commande.
Et vous voyez le GDB nous donne tout ce genre de choses.
Il dit, "Sans argument, énumère dix lignes plus après ou autour de la liste précédente.
Liste - énumère les dix lignes avant - "
Alors essayons d'utiliser moins de liste.
Et qui répertorie les 10 lignes précédentes, vous pouvez jouer avec la liste un peu.
Vous pouvez faire la liste, la liste -, vous pouvez même donner la liste d'un certain nombre, comme la liste des 8,
et il va dresser la liste des 10 lignes autour de la ligne 8.
Et vous pouvez voir ce qui se passe ici est que vous avez un simple if else.
Si vous tapez CS50 roches, il imprime "Vous obtenez un A.»
Sinon, il affiche "Vous obtenez un D."
Bummer ville. Très bien. Oui?
>> [Daniel] Alors, quand j'ai essayé de faire CS50 rochers sans les guillemets,
il dit: «Vous obtenez un D."
J'avais besoin des guillemets pour le faire fonctionner, pourquoi est-ce?
Ouais >>. Il s'avère que lorsque - c'est une autre friandise peu de plaisir -
lorsque vous exécutez le programme, si nous l'exécutons et nous taper CS50 roches,
tout comme Daniel a dit qu'il a fait, et que vous appuyez sur Entrée,
il dit encore nous obtenons un D.
Et la question est, pourquoi est-ce?
Et il s'avère que les deux notre terminal et GDB analyser ces que deux arguments distincts.
Parce que quand il ya un espace, qui est sous-entendu que
le premier argument pris fin; l'argument suivant est sur le point de commencer.
La façon de combiner les en deux, ou désolé, en un seul argument,
est d'utiliser les guillemets.
Alors maintenant, si nous le mettre entre guillemets et l'exécuter à nouveau, on obtient un A.
Donc, pour récapituler, sans guillemets, CS50 et des roches sont analysés comme deux arguments distincts.
Avec des guillemets, il est analysé comme un argument tout à fait.
>> Nous pouvons voir cela avec un point d'arrêt.
Jusqu'à présent, nous avons mis en place notre programme, et il a été en cours d'exécution
jusqu'à ce qu'il seg défauts ou frappe une erreur
ou jusqu'à ce qu'il soit sorti et tout a été tout à fait bien.
Ce n'est pas forcément la chose la plus utile, parce que parfois,
vous avez une erreur dans votre programme, mais ce n'est pas de provoquer une erreur de segmentation.
Ce n'est pas la cause de votre programme pour arrêter ou quelque chose comme ça.
La manière d'obtenir GDB pour mettre en pause votre programme à un moment donné
est de mettre un point d'arrêt.
Vous pouvez faire cela en mettant un point d'arrêt sur un nom de fonction
ou vous pouvez définir un point d'arrêt sur une ligne de code.
Je tiens à mettre des points d'arrêt sur les noms de fonctions, parce que - facile à retenir,
et si vous allez réellement et de modifier le code source d'un petit peu,
alors votre point d'arrêt sera effectivement rester au même endroit dans votre code.
Alors que si vous utilisez des numéros de ligne et les numéros de ligne changer
parce que vous ajoutez ou supprimez un code, alors vos points d'arrêt sont tous totalement foiré.
L'une des choses les plus communes que je fais est un point d'arrêt sur la fonction principale.
Souvent, je vais démarrer GDB, je vais taper b principal, appuyez sur Entrée, et ça va mettre un point d'arrêt
sur la fonction principale qui dit simplement, «en pause le programme dès que vous commencez à courir»,
et de cette façon, quand je lance mon programme avec, disons, CS50 rochers que deux arguments
et appuyez sur Entrée, il arrive à la fonction principale et il s'arrête juste à la toute première ligne,
juste avant qu'elle évalue la fonction strcmp.
>> Depuis que je suis en pause, je peux maintenant commencer à bidouillent et de voir ce qui se passe
avec toutes les différentes variables qui sont passés dans mon programme.
Ici, je peux imprimer argc et voir ce qui se passe.
Voir à ce que argc vaut 3, parce que c'est a 3 valeurs différentes en elle.
Il a le nom du programme, il a le premier argument et le second argument.
Nous pouvons imprimer sur ceux en consultant d'argv [0], argv [1], et argv [2].
Alors maintenant, vous pouvez aussi voir pourquoi cet appel strcmp est voué à l'échec,
parce que vous voyez que cela n'a diviser la CS50 et les roches en deux arguments distincts.
À ce stade, une fois que vous avez atteint un point d'arrêt, vous pouvez continuer à exécuter votre programme
ligne par ligne, au lieu de commencer votre programme.
Donc, si vous ne voulez pas commencer votre programme à nouveau et juste continuer à partir d'ici,
vous pouvez utiliser la commande continue et continuera va exécuter le programme jusqu'à la fin.
Tout comme il l'a fait ici.
Cependant, si je redémarre le programme, CS50 rochers, il frappe mon point d'arrêt à nouveau,
et cette fois, si je ne tiens pas à aller tout le chemin à travers le reste du programme,
Je peux utiliser la commande suivante, que j'ai également abréger avec n.
Et cela va parcourir le programme ligne par ligne.
Ainsi, vous pouvez regarder les choses d'exécuter, en tant que variables de changement, que les choses sont mises à jour.
Ce qui est assez agréable.
L'autre chose cool, c'est plutôt que de répéter la même commande à plusieurs reprises, encore et encore,
si vous appuyez simplement sur Entrée - alors là, vous voyez, je n'ai pas tapé dans quoi que ce soit -
si je viens d'appuyer sur Entrée, il répétera la commande précédente,
ou la commande GDB précédente que je viens de mettre po
Je peux continuer à appuyer sur Enter et ça va continuer pas à pas dans mon code ligne par ligne.
J'encourage vous les gars pour aller voir les programmes bogués autres aussi.
Nous n'avons pas eu le temps de passer à travers tous aujourd'hui dans la section.
Le code source est là, de sorte que vous pouvez sorte de voir ce qui se passe
dans les coulisses si vous êtes vraiment coincé,
mais à tout le moins, juste pratiquer démarrer GDB,
l'exécution du programme jusqu'à ce qu'il se brise sur vous,
obtenir la trace, déterminer ce qui fonctionne le crash était,
quelle ligne c'est sur, imprimer certaines valeurs des variables,
juste pour vous faire une idée pour elle, parce que cela va vraiment vous aider à aller de l'avant.
À ce stade, nous allons arrêter de sortir de GDB, qui vous aide ou tout simplement arrêter de q.
Si votre programme est au milieu de courir encore, et il n'a pas quitté,
il sera toujours vous demander: «Etes-vous sûr que vous voulez vraiment quitter?"
Vous pouvez appuyez simplement oui.
>> Maintenant, nous allons nous pencher sur le problème suivant que nous avons, qui est le programme de chat.
Si vous regardez le court et sur la réorientation des tuyaux, vous verrez que Tommy utilise ce programme
qui imprime essentiellement toute la sortie d'un fichier à l'écran.
Donc, si je lance chat, il s'agit en fait d'un programme intégré à l'appareil,
et si vous avez Mac, vous pouvez le faire sur votre Mac aussi, si vous ouvrez terminal.
Et nous - chat, disons, cp.c, et appuyez sur Entrée.
Ce que cela fait, si nous défiler vers le haut un peu et de voir où nous avons couru de la ligne,
ou lorsque nous avons couru la commande cat, il est littéralement juste imprimé sur le contenu de cp.c à notre écran.
On peut l'exécuter à nouveau et vous pouvez mettre plusieurs fichiers en même temps.
Ainsi, vous pouvez faire cp.c chat, et alors nous pouvons également concaténer le fichier Cat.C,
qui est le programme que nous sommes sur le point d'écrire,
et il va imprimer les deux fichiers dos à dos à notre écran.
Donc, si nous défiler vers le haut un peu, on voit que quand nous avons eu cette cp.c chat, Cat.C,
abord imprimé sur le fichier cp, puis en dessous, elle imprimée sur le fichier Cat.C juste ici.
Nous allons l'utiliser pour simplement remettre les pieds mouillés.
Jouez avec la simple impression à la borne, voir comment ça fonctionne.
Si vous les gars ouvrir avec gedit Cat.C, appuyez sur Entrée,
vous pouvez voir le programme que nous sommes en train d'écrire.
Nous avons inclus cette plaque de la chaudière agréable, donc nous n'avons pas à passer du temps en tapant tout cela.
Nous vérifions également le nombre d'arguments passé po
Nous imprimons sur un message d'utilisation agréable.
>> C'est le genre de chose qui, encore une fois, comme nous venons de parler,
c'est presque comme si la mémoire musculaire.
N'oubliez pas de continuer à faire le même genre de trucs
et toujours l'impression à une sorte de message utile
de sorte que les gens sachent comment exécuter votre programme.
Avec un chat, c'est assez simple, nous allons juste passer en revue tous les arguments différents
qui ont été transmis à notre programme, et nous allons imprimer
leur contenu sur l'écran à une à la fois.
Pour imprimer des fichiers en dehors de l'écran, nous allons faire quelque chose de très similaire
à ce que nous avons fait à la fin du quiz.
A la fin du questionnaire, qui embauchent programme, nous avons dû ouvrir un fichier,
et puis nous avons eu à imprimer.
Dans ce cas, nous allons ouvrir un fichier, et nous allons lire à partir de ce lieu.
Ensuite, nous allons imprimer, au lieu d'un fichier, nous allons afficher à l'écran.
Ainsi, l'impression à l'écran que vous avez tous fait avant avec printf.
Donc, ce n'est pas trop fou.
Mais la lecture d'un fichier est un peu bizarre.
Nous allons passer par un petit peu à la fois.
Si vous les gars revenir à ce dernier problème sur votre quiz, problème 33,
la première ligne que nous allons faire ici, en ouvrant le fichier, est très similaire à ce que nous faisions là.
Donc, Stella, qu'est-ce que ce regard ligne comme, quand on ouvre un fichier?
[Stella] FILE * Capital, fichier - >> Okay. >> - Est égale à la fonction fopen. Yup >>.
Qui dans ce cas est? C'est dans le commentaire.
>> C'est dans le commentaire? argv [i] et r?
>> Exactement. Tout à fait. Donc, Stella est tout à fait raison.
C'est ce que la ligne ressemble.
Nous allons obtenir une variable flux de fichier, le stocker dans un FILE *, afin que tous les capuchons,
FILE *, et le nom de cette variable sera de fichier.
Nous pourrions l'appeler comme on veut. Nous pourrions l'appeler first_file ou file_i, tout ce que nous aimerions.
Et puis le nom du fichier a été adoptée en ligne de commande pour ce programme.
Donc, il est stocké dans argv [i], et ensuite nous allons ouvrir ce fichier en mode lecture.
Maintenant que nous avons ouvert le fichier, quelle est la chose que nous avons toujours se rappeler de faire
chaque fois que nous avons ouvert un dossier? Fermez-le.
Donc, Missy, comment pouvons-nous fermer un fichier?
[Missy] fclose (fichier) >> fclose (fichier). Exactement.
Grande. D'accord. Si nous regardons ce commentaire à faire ici,
dit-il, «Ouvrir argv [i] et imprimer son contenu sur la sortie standard."
>> La sortie standard est un nom bizarre. Stdout est juste notre façon de dire
nous voulons imprimer à la borne, nous voulons l'imprimer sur le flux de sortie standard.
On peut réellement se débarrasser de ce commentaire ici.
Je vais le copier et le coller puisque c'est ce que nous avons fait.
À ce stade, nous avons maintenant de lire le fichier binaire à peu.
Nous avons discuté quelques moyens de lecture des fichiers.
Lesquels sont vos favoris si loin?
Quels moyens avez-vous vu ou avez-vous souvenir, de lire les fichiers?
[Daniel] fread? Fread >>? Donc, fread est un. Jimmy, savez-vous d'autres?
[Jimmy] No. >> Okay. Nope. Charlotte? Alexander? Toutes les autres? D'accord.
Alors que les autres sont fgetc, est celui que nous allons utiliser beaucoup.
Il ya aussi fscanf, vous avez vu un modèle ici?
Ils commencent tous par f. Rien à voir avec un fichier.
Il ya fread, fgetc, fscanf. Ce sont toutes les fonctions de lecture.
Pour l'écriture, nous avons fwrite, nous avons fputc au lieu de fgetc.
Nous avons également fprintf comme nous l'avons vu sur le quiz.
Comme il s'agit d'un problème qui implique la lecture d'un fichier,
nous allons utiliser l'une de ces trois fonctions.
Nous n'allons pas utiliser ces fonctions ici-bas.
Ces fonctions se trouvent tous dans la norme E / S de la bibliothèque.
Donc, si vous regardez en haut de ce programme,
vous pouvez voir que nous avons déjà inclus le fichier d'en-tête pour l'E / S standard de la bibliothèque.
Si nous voulons savoir laquelle nous voulons utiliser,
on peut toujours ouvrir les pages de manuel.
Donc, on peut taper stdio homme
et lire tout sur l'entrée stdio et fonctions de sortie en C.
Et nous pouvons déjà voir oh, regarde. Il est mentionné fgetc, il est mentionné fputc.
Ainsi, vous pouvez descendre un peu et de regarder, par exemple, fgetc
et de regarder sa page de manuel.
Vous pouvez voir qu'il va de pair avec tout un tas d'autres fonctions:
fgetc, fgets, getc, getchar, ungetc, et son entrée de caractères et les chaînes.
Voilà donc comment on peut lire en caractères et les chaînes de fichiers à partir de l'entrée standard,
qui est essentiellement de l'utilisateur.
Et c'est ainsi que nous le faisons en effectif C.
Donc, ce n'est pas en utilisant les méthodes GetString et fonctions getchar
que nous avons utilisé à partir de la bibliothèque CS50.
Nous allons faire de ce problème dans un couple des manières
de sorte que vous pouvez voir deux façons différentes de le faire.
Tant la fonction fread que Daniel dit et fgetc sont de bons moyens pour le faire.
Je pense que fgetc est un peu plus facile, car il ne dispose que d', comme vous voyez,
un argument, le FILE * que nous essayons de lire le caractère de,
et sa valeur de retour est un int.
Et c'est un peu déroutant, non?
>> Parce que nous obtenons un personnage, alors pourquoi ne pas ce retour d'un char?
Les gars, vous avez des idées sur le pourquoi cela pourrait ne pas retourner un char?
[Missy réponses, inintelligible] >> Oui. Donc, Missy est tout à fait raison.
Si c'est ASCII, alors cet entier pourrait être associé à un caractère réel.
Peut-être un caractère ASCII, et c'est vrai.
C'est exactement ce qui se passe.
Nous utilisons un int simplement parce qu'il a plus de bits.
C'est plus grand que un char, notre chevalier ne comporte que 8 bits, que 1 octet sur nos machines 32-bits.
Et un int a une valeur toutes les 4 octets de l'espace ».
Et il se trouve que la façon dont fonctionne fgetc,
si nous défiler vers le bas dans notre résumé dans cette page de manuel un peu,
faites défiler la liste vers le bas. Il s'avère qu'ils utilisent cette valeur spéciale appelée EOF.
C'est une constante spéciale que la valeur de retour de la fonction fgetc
chaque fois que vous frappez la fin du fichier, ou si vous obtenez une erreur.
Et il se trouve que pour faire ces comparaisons avec EOF correctement,
vous voulez avoir ce montant supplémentaire de l'information que vous avez dans un int
plutôt que d'utiliser une variable de type char.
Même si fgetc est effectivement d'obtenir un caractère depuis un fichier,
vous voulez vous rappeler qu'il est de retour quelque chose qui est de type int pour vous.
Cela dit, il est assez facile à utiliser.
Ça va nous donner un caractère, donc tout ce que nous avons à faire est de continuer à demander le fichier,
«Donnez-moi le caractère suivant, donnez-moi le caractère suivant, donnez-moi le caractère suivant,"
jusqu'à ce que nous arrivons à la fin du fichier.
Et qui va tirer dans un caractère à la fois de notre fichier,
et alors nous pouvons faire ce que nous voulons avec elle.
Nous pouvons stocker, nous pouvons l'ajouter à une chaîne, on peut l'imprimer.
Faire de tout ça.
>> Zoom arrière sur et de revenir à notre programme Cat.C,
si nous allons utiliser fgetc,
comment pourrions-nous aborder cette ligne de code suivante?
Nous allons utiliser - fread fera quelque chose de légèrement différent.
Et cette fois, nous allons juste utiliser fgetc pour obtenir un caractère à la fois.
Pour traiter un fichier entier, que peut-on faire?
Combien de caractères y at-il dans un fichier?
Il ya beaucoup de choses. Donc, vous voulez probablement obtenir un
puis obtenir un autre et obtenir un autre et obtenir un autre.
Quel type d'algorithme pensez-vous que nous pourrions avoir à utiliser ici?
Quel type de -? [Alexander] Une boucle for? >> Exactement.
Certains type de boucle.
Une boucle for est réellement grand, dans ce cas.
Et comme vous le disiez, il semble que vous voulez une boucle sur l'ensemble du dossier,
obtenir un caractère à la fois.
Toutes les suggestions sur ce qui pourrait ressembler?
[Alexander, inintelligible]
Ok >>, dites-moi en anglais ce que vous essayez de faire? [Alexander, inintelligible]
Donc dans ce cas, il semble que nous essayons juste de faire une boucle sur l'ensemble du dossier.
[Alexander] Donc i > -?
Je suppose que la taille du fichier, non? La taille - nous allons simplement écrire comme ça.
Taille du fichier pour le moment, i + +.
Ainsi, il s'avère que la façon dont vous faites cela avec fgetc, et c'est nouveau,
c'est qu'il n'y a pas de moyen facile pour obtenir exactement la taille d'un fichier
avec cette «sizeof» type de construction que vous avez vu avant.
Lorsque nous utilisons cette fonction fgetc, nous introduisons une sorte de
nouvelle syntaxe funky à cette boucle for, où au lieu d'utiliser simplement un compteur de base
aller caractère par caractère, nous allons tirer un caractère à la fois,
un caractère à la fois, et la façon dont nous savons que nous sommes à la fin
n'est pas quand nous avons compté un nombre donné de caractères,
mais quand le personnage nous nous retirons, c'est que la fin particulière de caractères du fichier.
Donc, nous pouvons le faire par - C'est ce que j'appelle ch, et nous allons l'initialiser
avec notre premier appel pour obtenir le premier caractère à partir du fichier.
Donc cette partie ici, cela va avoir un personnage hors du fichier
et le stocker dans le ch variable.
Nous allons continuer à le faire jusqu'à ce que nous arrivons à la fin du fichier,
ce que nous faisons en testant le caractère n'étant pas égale à celle du caractère EOF spéciale.
Et puis, au lieu de faire ch + +, ce qui serait tout simplement incrémenter la valeur,
si nous lisons un sur A du dossier, un A majuscule, par exemple,
ch + + nous donnerait b, puis nous obtiendrions c et d.
Ce n'est clairement pas ce que nous voulons. Ce que nous voulons ici
dans ce dernier bit est que nous voulons obtenir le caractère suivant dans le fichier.
>> Alors, comment pouvons-nous obtenir le caractère suivant dans le fichier?
Comment pouvons-nous obtenir le premier caractère du fichier?
[Étudiants] fgetfile? Fgetc >>, ou, désolé, vous étiez tout à fait raison.
Je l'ai mal orthographié là. Donc, oui.
Ici, au lieu de faire ch + +,
nous allons juste d'appeler fgetc (fichier) nouveau
et stocker le résultat dans notre variable même ml.
[Question étudiants, inintelligible]
>> C'est là que ces gars-là sont spéciaux * FILE.
La façon dont ils travaillent, c'est qu'ils - lorsque vous ouvrez pour la première - quand vous d'abord que l'appel fopen
l'FILE * sert effectivement comme un pointeur vers le début du fichier.
Et puis, chaque fois que vous appelez fgetc, il se déplace d'un personnage à travers le fichier.
Donc, lorsque vous appelez cela, vous êtes en incrémentant le pointeur de fichier par un caractère.
Et quand vous fgetc encore une fois, vous vous déplacez un autre caractère
et un autre caractère et un autre caractère et un autre caractère.
[Question étudiants, inintelligible] >> Et C'est - oui.
C'est un peu de cette magie sous le capot.
Vous continuerait d'incrémenter travers.
À ce stade, vous êtes en mesure de travailler effectivement avec un personnage.
Alors, comment pouvons-nous imprimer ceci à l'écran, maintenant?
Nous pouvons utiliser la même chose printf que nous avons utilisé auparavant.
Que nous avons utilisé tout le semestre.
Nous pouvons appeler printf,
et on peut passer dans le caractère juste comme ça.
Une autre façon de le faire est plutôt que d'utiliser printf et d'avoir à faire cette chaîne de format,
on peut aussi utiliser l'une des autres fonctions.
Nous pouvons utiliser fputc, qui imprime un caractère à l'écran,
sauf si l'on regarde fputc - permettez-moi de faire un zoom arrière un peu.
Nous voyons ce qui est bien c'est qu'il prend dans le caractère que nous avons lu à l'aide d'fgetc,
mais alors il faut lui donner un flux d'imprimer.
Nous pouvons également utiliser la fonction putchar, qui mettra directement à la sortie standard.
Donc, il ya tout un tas d'options différentes que nous pouvons utiliser pour l'impression.
Ils sont tous dans la norme E / S de la bibliothèque.
Chaque fois que vous voulez imprimer - afin printf, par défaut, permet d'imprimer à la norme spéciale sur cours d'eau,
qui est ce que stdout.
Ainsi, nous pouvons simplement se référer à lui comme type de cette valeur magique, stdout ici.
Oops. Placez le point-virgule à l'extérieur.
>> C'est beaucoup de nouvelles informations géniale ici.
Beaucoup de ce qui est très idiomatique, dans le sens où il s'agit du code
ce qui est écrit de cette façon simplement parce que c'est propre à lire, facile à lire.
Il ya beaucoup de façons différentes de le faire, de nombreuses fonctions que vous pouvez utiliser,
mais nous avons tendance à vous suffit de suivre ces mêmes modèles à plusieurs reprises.
Alors ne soyez pas surpris si vous voyez un code comme celui à venir encore et encore.
Très bien. À ce stade, nous avons besoin de faire une pause pour la journée.
Merci d'être venu. Merci de regarder si vous êtes en ligne. Et nous nous reverrons la semaine prochaine.
[CS50.TV]