[Powered by Google Translate] [Ενότητα 5 - Άνετα Περισσότερα] [Rob Bowden - Πανεπιστήμιο του Χάρβαρντ] [Αυτό είναι CS50. - CS50.TV] Όπως είπα και στο e-mail μου, υπάρχουν πολλά πράγματα που μπορείτε να χρησιμοποιήσετε εκτός από τη συσκευή για να κάνουν πραγματικότητα τα σύνολα πρόβλημα. Σας προτείνουμε να το κάνουμε με τη συσκευή μόνο και μόνο επειδή τότε μπορούμε πιο εύκολα να σας βοηθήσει και γνωρίζουμε πως ό, τι πρόκειται να λειτουργήσει. Αλλά ως ένα παράδειγμα όπου μπορείτε να κάνετε τα πράγματα αν, ας πούμε, δεν έχετε πρόσβαση σε μια συσκευή ή θέλετε να εργαστείτε στο υπόγειο Κέντρο Διάδοσης Επιστημών - που πραγματικά έχουν τη συσκευή πάρα πολύ - αν θέλετε να εργαστείτε οπουδήποτε. Ένα παράδειγμα είναι έχετε δει / ακούσει SSH; SSH είναι βασικά ακριβώς όπως σύνδεση με κάτι. Στην πραγματικότητα, αυτή τη στιγμή είμαι SSHed στη συσκευή. Ποτέ δεν εργάζονται απευθείας στη συσκευή. Εδώ είναι η συσκευή, και αν κοιτάξετε εδώ κάτω να δείτε αυτήν τη διεύθυνση IP. Ποτέ δεν εργάζονται στην ίδια τη συσκευή? Πάντα έρχονται πάνω σε ένα παράθυρο παράθυρο iTerm2 / τερματικό. Μπορείτε να SSH σε αυτήν τη διεύθυνση IP, το ssh jharvard@192.168.129.128. Θυμάμαι ότι ο αριθμός είναι πολύ εύκολα γιατί είναι τόσο ωραίο μοτίβο. Αλλά ότι θα με ρωτήσετε για τον κωδικό μου, και τώρα είμαι στη συσκευή. Βασικά, σε αυτό το σημείο, εάν ανοίξει ένα τερματικό στο εσωτερικό της ίδιας της συσκευής, αυτή η διασύνδεση, όμως θα το χρησιμοποιήσετε, είναι ακριβώς το ίδιο ως διεπαφή είμαι με τη χρήση πάνω από εδώ, αλλά τώρα είστε SSHed. Δεν χρειάζεται να SSH στη συσκευή. Ένα παράδειγμα από ένα άλλο μέρος που θα μπορούσε να είναι SSH είμαι σίγουρος ότι έχετε από προεπιλογή - Αχ. Μεγαλύτερο. Όλοι θα πρέπει να έχετε από τους λογαριασμούς FAS προεπιλογή στους διακομιστές FAS. Για μένα, θα ήθελα να rbowden@nice.fas.harvard.edu SSH. Είναι πρόκειται να σας ρωτήσω ότι είναι η πρώτη φορά, και θα πω ναι. Κωδικό μου είναι ακριβώς πρόκειται να είναι FAS τον κωδικό μου. Και έτσι τώρα, είμαι SSHed με τα ωραία servers, και μπορώ να κάνω οτιδήποτε θέλω εδώ. Πολλά μαθήματα που θα μπορούσε να λάβει, όπως η 124, θα έχετε να ανεβάσετε τα πράγματα εδώ να υποβάλουν πραγματικά σύνολα πρόβλημά σας. Αλλά λένε ότι δεν έχουν πρόσβαση στην συσκευή σας. Στη συνέχεια, μπορείτε να κάνετε τα πράγματα, όπως για εδώ θα πω - Αυτό είναι ακριβώς το τμήμα των ερωτήσεων. Θα σας ζητήσει να το κάνετε αυτό στη συσκευή. Αντ 'αυτού θα το κάνω μόνο στο διακομιστή. Πάω να αποσυμπιέσετε αυτό. Το πρόβλημα θα είναι ότι έχετε συνηθίσει να χρησιμοποιείτε κάτι σαν το gedit ή οτιδήποτε άλλο στο εσωτερικό της συσκευής. Δεν πρόκειται να έχουμε ότι στο διακομιστή FAS. Είναι όλα ακριβώς πρόκειται να είναι αυτό το περιβάλλον κειμένου. Έτσι, θα μπορούσατε είτε το ένα, προσπαθήστε να μάθετε ένα πρόγραμμα επεξεργασίας κειμένου που έχουν. Έχουν Nano. Nano είναι συνήθως αρκετά εύκολο στη χρήση. Μπορείτε να χρησιμοποιήσετε τα βέλη σας και πληκτρολογήστε κανονικά. Έτσι, αυτό δεν είναι δύσκολο. Αν θέλετε να πάρετε πραγματικά φανταχτερό, μπορείτε να χρησιμοποιήσετε τον Emacs, που εγώ κατά πάσα πιθανότητα δεν θα πρέπει να έχουν ανοίξει γιατί δεν γνωρίζουν καν πώς να κλείσει το Emacs. Έλεγχος X, C Ελέγχου; Ναι. Ή μπορείτε να χρησιμοποιήσετε Vim, το οποίο είναι αυτό που μπορώ να χρησιμοποιήσω. Και έτσι αυτές είναι οι επιλογές σας. Αν δεν θέλετε να το κάνετε αυτό, μπορείτε επίσης, αν κοιτάξετε manual.cs50.net-- Αχ. Σε έναν υπολογιστή, μπορείτε να χρησιμοποιείτε SSH PuTTY, που θα πάμε να πρέπει να κατεβάσετε ξεχωριστά. Σε Mac, μπορείτε απλά με τη χρήση τερματικού προεπιλογή ή μπορείτε να κατεβάσετε iTerm2, η οποία είναι σαν ένα ωραίο, φανταχτερό Terminal. Αν πάτε να manual.cs50.net θα δείτε ένα σύνδεσμο για να Notepad + +, το οποίο είναι αυτό που μπορείτε να χρησιμοποιήσετε σε έναν υπολογιστή. Σας επιτρέπει να SFTP από Notepad + +, το οποίο είναι ουσιαστικά SSH. Αυτό θα σας επιτρέψει να κάνετε είναι να επεξεργαστείτε τα αρχεία σας σε τοπικό επίπεδο, και στη συνέχεια, κάθε φορά που θέλετε να τους σώσει, θα εξοικονομήσει να nice.fas, όπου μπορείτε να εκτελέσετε στη συνέχεια. Και το ισοδύναμο σε ένα Mac θα είναι TextWrangler. Γι 'αυτό σας επιτρέπει να κάνετε το ίδιο πράγμα. Αυτό σας επιτρέπει να επεξεργαστείτε αρχεία τοπικά και να τα αποθηκεύσετε σε nice.fas, όπου μπορείτε να εκτελέσετε στη συνέχεια. Έτσι, εάν είστε ποτέ κολλήσει χωρίς συσκευή, έχετε αυτές τις επιλογές ακόμα να κάνετε σετ πρόβλημά σας. Το μόνο πρόβλημα θα είναι ότι δεν πρόκειται να έχουν την CS50 βιβλιοθήκη nice.fas επειδή δεν έχουν από προεπιλογή αυτό. Μπορείτε να κατεβάσετε είτε το CS50 βιβλιοθήκη - Δεν νομίζω ότι χρειάζομαι σε αυτό το σημείο. Μπορείτε να κατεβάσετε είτε το CS50 βιβλιοθήκη και να το αντιγράψετε πάνω σε nice.fas, ή Νομίζω ότι σε αυτό το σημείο δεν το χρησιμοποιούν πια ούτως ή άλλως. Ή αν το κάνουμε, μπορείτε προς το παρόν να το αντικαταστήσει με οι υλοποιήσεις των λειτουργιών της βιβλιοθήκης CS50 ούτως ή άλλως. Έτσι, αυτό δεν θα πρέπει να είναι ότι ένα μεγάλο μέρος ενός περιορισμού. Και αυτό είναι αυτό. Θα πάω πίσω στη συσκευή τώρα? Θα κάνουμε τα πάντα μέσα στη συσκευή. Κοιτάζοντας το τμήμα των ερωτήσεων, στην αρχή, όπως είπα και στο e-mail μου, έχουμε να μιλήσουμε για το ένα κοντό που έπρεπε να παρακολουθήσουν. Έχουμε τον αναπροσανατολισμό & Σωλήνες και αυτά τα τρία ερωτήματα. Σε ποια ρεύμα δεν λειτουργεί όπως printf γράψει από προεπιλογή; Έτσι ρεύμα. Τι είναι το ρεύμα; Ένα ρεύμα είναι βασικά σαν να είναι μόνο μερικά - Δεν είναι καν μια πηγή του 1s και 0s. Το ρεύμα αυτό είναι ζητώντας εδώ είναι τυποποιημένα. Και έτσι η τυπική έξω είναι ότι ένα ρεύμα όταν γράφετε σε αυτό, εμφανίζεται στην οθόνη. Πρότυπο έξω, με ρεύμα, αυτό σημαίνει ότι μπορείτε απλά γράψτε 1s και 0s σε αυτό, και το άλλο άκρο του προτύπου έξω διαβάζει μόνο από το ρεύμα. Είναι απλά μια σειρά από 1 και 0. Μπορείτε να γράψετε σε ρέματα ή μπορείτε να διαβάσετε από τις ροές ανάλογα με το τι το ρεύμα είναι στην πραγματικότητα. Τα άλλα δύο ρεύματα προεπιλογή είναι στάνταρ σε και τυπικό σφάλμα. Πρότυπο είναι κάθε φορά που κάνετε GetString, σας περιμένει για να σας πράγματα εισόδου. Γι 'αυτό σας περιμένει, είναι στην πραγματικότητα περιμένει για πρότυπο, η οποία είναι πραγματικά αυτό που παίρνετε όταν πληκτρολογείτε στο πληκτρολόγιο. Είσαι πληκτρολογώντας στο πρότυπο μέσα Τυπικό σφάλμα είναι βασικά ισοδύναμη με τις τυποποιημένες, αλλά είναι εξειδικευμένη σε ότι όταν εκτυπώνετε σε τυπικό σφάλμα, είστε υποτίθεται για να εκτυπώσετε μόνο μηνύματα λάθους στο ότι έτσι ώστε να μπορεί να γίνει διάκριση μεταξύ τακτικών μηνύματα στην οθόνη έναντι μηνύματα λάθους, ανάλογα με το αν πήγαν στο τυποποιημένες ή τυπικό σφάλμα. Αρχεία πάρα πολύ. Πρότυπο έξω, πρότυπο, και τυπικό σφάλμα είναι μόνο ειδικές κατηγορίες, αλλά πραγματικά οποιοδήποτε αρχείο, όταν ανοίγετε ένα αρχείο, γίνεται ένα ρεύμα από bytes όπου μπορείτε απλά να διαβαστεί από αυτό το ρεύμα. Εσείς, ως επί το πλείστον, μπορεί απλά σκεφτείτε ένα αρχείο ως ένα ρεύμα byte. Έτσι ρεύματα τι γράφουν από προεπιλογή; Πρότυπο έξω. Ποια είναι η διαφορά μεταξύ> και >>; Μήπως κάποιος δείτε το βίντεο εκ των προτέρων; Εντάξει. > Πρόκειται να είναι το πώς θα ανακατευθύνει σε αρχεία, >> και είναι, επίσης, πρόκειται να ανακατευθύνετε την έξοδο σε αρχεία, αλλά είναι αντ 'αυτού πρόκειται να επισυνάπτει στον φάκελο. Για παράδειγμα, ας υποθέσουμε ότι τυχαίνει να έχουν δικαίωμα dict εδώ, και η μόνη ουσία στο εσωτερικό του dict είναι, γάτα, σκύλος, ψάρι, σκύλος. Μια εντολή που έχετε στη γραμμή εντολών είναι η γάτα, το οποίο είναι ακριβώς πρόκειται να εκτυπώσετε ό, τι είναι σε ένα αρχείο. Έτσι, όταν λέω dict γάτα, πρόκειται να εκτυπώσετε γάτα, γάτα, σκύλος, τα ψάρια, το σκυλί. Αυτό είναι το μόνο γάτα κάνει. Αυτό σημαίνει ότι το έντυπο με το πρότυπο από γάτα, γάτα, σκύλος, τα ψάρια, το σκυλί. Αν αντί να θέλετε να ανακατευθύνετε ότι σε ένα αρχείο, μπορώ να χρησιμοποιήσω> και να τις κατευθύνουν σε ό, τι είναι το αρχείο. Θα καλέσω το αρχείο αρχείο. Έτσι τώρα, αν μου ls, θα δω έχω ένα νέο αρχείο με το όνομα αρχείου. Και αν το ανοίξουμε, πρόκειται να έχουν ό, τι ακριβώς γάτα που τίθεται στη γραμμή εντολών. Έτσι τώρα, αν το κάνω αυτό και πάλι, τότε πρόκειται να ανακατευθύνετε την έξοδο σε αρχείο, και είμαι πρόκειται να έχουν το ίδιο ακριβώς πράγμα. Έτσι, από τεχνική άποψη, το αγνόησε εντελώς ό, τι είχαμε. Και θα δούμε αν μπορώ να αλλάξω dict, έβγαλα το σκυλί. Τώρα, αν η γάτα dict σε αρχείο και πάλι, θα πάμε να έχουν τη νέα έκδοση αφαιρεθεί με το σκυλί. Γι 'αυτό αντικαθιστά εντελώς. Αντ 'αυτού, αν χρησιμοποιήσουμε >>, πρόκειται να προσθέσετε το αρχείο. Τώρα, το άνοιγμα αρχείων, βλέπουμε έχουμε ακριβώς το ίδιο πράγμα δύο φορές τυπωμένο διότι υπήρχε κάποτε, τότε προσαρτάται στο πρωτότυπο. Έτσι, ό, τι αυτό είναι> και >> κάνουν. Μήπως η επόμενη ζητήσει - Δεν ρωτήσω κάτι σχετικά με αυτό. Το άλλο που έχουμε είναι <, το οποίο αν> ανακατευθύνει πρότυπο έξω, <Πρόκειται να ανακατευθύνει πρότυπο μέσα Ας δούμε αν έχουμε ένα παράδειγμα. Μπορώ να γράψω ένα πραγματικά γρήγορα. Ας πάρουμε οποιοδήποτε αρχείο, hello.c. Σχετικά απλό αρχείο. Παίρνω απλώς ένα string και μετά την εκτύπωση "Hello" ό, τι η σειρά που μόλις εισήλθε ήταν. Γι 'αυτό και στη συνέχεια, γεια. / Γεια. Τώρα είναι μου ζητά να εισάγετε κάτι, πράγμα που σημαίνει ότι είναι σε αναμονή για τα πράγματα που πρέπει να τεθεί σε πρότυπο μέσα Έτσι αρχίζει ό, τι θέλω μέσα στο πρότυπο Είμαστε ακριβώς πρόκειται να πω Γεια σας, Rob! Στη συνέχεια εκτυπώνεται από το πρότυπο Γεια σας, Rob! Αν το κάνω. / Γεια και στη συνέχεια ανακατευθύνει, Προς το παρόν μπορείτε μόνο να ανακατευθύνει από ένα αρχείο. Έτσι, αν έβαλα σε κάποιο αρχείο, txt, και έβαλα τον Rob, αν τρέξω γεια και στη συνέχεια ανακατευθύνει το αρχείο txt σε. / γειά σου, πρόκειται να πω Γεια σας, Rob! αμέσως. Όταν παίρνει πρώτος που θα GetString και σας περιμένει στο πρότυπο, πρότυπο δεν είναι πλέον περιμένει στο πληκτρολόγιο για να πάρει τα στοιχεία εισαχθεί. Αντ 'αυτού, έχουμε ανακατευθύνονται πρότυπο για να διαβάσετε από το αρχείο txt. Και γι 'αυτό πρόκειται να διαβάσει από το αρχείο txt, το οποίο είναι ακριβώς η γραμμή Rob, και στη συνέχεια πρόκειται να εκτυπώσετε Γεια σας, Rob! Και αν ήθελα, θα μπορούσα να κάνω επίσης. / Γεια κάνεις 2>, Αυτός είναι ο αναπροσανατολισμός τυπικό σφάλμα. Έτσι, αν κάτι πήγε σε τυπικό σφάλμα, δεν θα πάρει θέσει σε txt2. Αλλά αν παρατηρήσετε κάνω 2>, τότε είναι ακόμα εκτύπωση Γεια σας, Rob! στη γραμμή εντολών επειδή είμαι ανακατεύθυνση μόνο τυπικό σφάλμα, δεν είμαι από τον αναπροσανατολισμό πρότυπο. Τυπικό σφάλμα και έξω προτύπου είναι διαφορετικά. Αν πραγματικά ήθελε να γράψει σε τυπικό σφάλμα, τότε θα μπορούσε να αλλάξει αυτό είναι να fprintf stderr. Έτσι printf, από προεπιλογή, εκτυπώνει σε τυποποιημένες. Αν θέλετε να εκτυπώσετε σε τυπικό σφάλμα το χέρι, τότε θα πρέπει να χρησιμοποιήσετε fprintf και να καθορίσετε τι θέλετε να εκτυπώσετε σε. Αν, αντίθετα, έκανα fprintf stdout, τότε αυτό είναι ουσιαστικά ισοδύναμο με printf. Αλλά fprintf σε τυπικό σφάλμα. Έτσι τώρα, αν αυτό ανακατευθύνει σε txt2, Γεια σας, Rob! εξακολουθεί να πάρει τυπωμένο στη γραμμή εντολών δεδομένου ότι είναι να πάρει τυπωμένο σε τυπικό σφάλμα και είμαι ανακατεύθυνσης μόνο του πρότυπου έξω. Αν τώρα ανακατευθύνει τυπικό σφάλμα, τώρα δεν πάρει τυπωμένα, και txt2 πρόκειται να είναι Γεια σας, Rob! Έτσι τώρα, μπορείτε να εκτυπώσετε την πραγματική λάθη σας σε τυπικό σφάλμα και να εκτυπώσετε κανονικά μηνύματα σας σε τυποποιημένες. Και έτσι, όταν εκτελείτε το πρόγραμμα σας, μπορείτε να το εκτελέσετε ως. / Γειά σου αυτό το είδος με το 2> έτσι ώστε το πρόγραμμά σας θα τρέξει κανονικά, αλλά όλα τα μηνύματα λάθους που μπορείτε να πάρετε να ελέγξετε αργότερα στο αρχείο καταγραφής σφαλμάτων σας, έτσι τα λάθη, και στη συνέχεια να εξετάσουμε αργότερα και λάθη αρχείο σας θα έχουν τυχόν λάθη που συνέβη. Ερωτήσεις; Το τελευταίο είναι σωλήνα, το οποίο μπορείτε να σκεφτείτε τη λήψη του ως πρότυπο έξω από μία εντολή και καθιστώντας το πρότυπο στην από την επόμενη εντολή. Ένα παράδειγμα είναι εδώ ηχώ είναι ένα πράγμα της γραμμής εντολών που είναι ακριβώς πρόκειται να επαναλάβω ό, τι έχω θέσει ως επιχείρημα. Δεν θα βάλει εισαγωγικά. Echo μπλα, μπλα, μπλα είναι ακριβώς πρόκειται να εκτυπώσετε μπλα, μπλα, μπλα. Πριν, όταν είπα ότι έπρεπε να βάλει Rob σε ένα αρχείο txt γιατί μπορώ να ανακατευθύνει μόνο txt αρχεία, αντ 'αυτού, / αν συμμερίζομαι Rob σωλήνα και στη συνέχεια σε αυτό. / γειά σου, που θα κάνει επίσης το ίδιο είδος του πράγματος. Αυτό παίρνει την έξοδο της εντολής αυτής, echo Rob, και το χρησιμοποιεί ως είσοδο για. / γεια. Μπορείτε να σκεφτείτε από το ως ανακατεύθυνση πρώτη εντολή echo Rob σε ένα αρχείο και στη συνέχεια εισαγωγή στο. / γειά σου ότι το αρχείο που μόλις εξάγεται. Αλλά παίρνει το προσωρινό αρχείο από την εικόνα. Ερωτήσεις σχετικά με αυτό; Η επόμενη ερώτηση θα περιλαμβάνει αυτό. Τι αγωγός θα μπορούσατε να χρησιμοποιήσετε για να βρείτε τον αριθμό των μοναδικών ονομάτων σε ένα αρχείο που ονομάζεται names.txt; Οι εντολές θα πάμε να θέλετε να χρησιμοποιήσετε εδώ είναι μοναδικό, έτσι uniq, και στη συνέχεια, wc. Μπορείτε να κάνετε uniq άνθρωπος πραγματικά να δούμε τι κάνει, και αυτό είναι ακριβώς πρόκειται να φιλτράρει γραμμές που ταιριάζουν δίπλα από την είσοδο. Και ο άνθρωπος wc πρόκειται να εκτυπώσετε τη νέα γραμμή, λέξη, και byte Η για κάθε αρχείο. Και το τελευταίο που θα πάμε να θέλετε να χρησιμοποιήσετε είναι το είδος, η οποία πρόκειται να ταξινομήσετε μόνο γραμμές του αρχείου txt. Αν κάνω κάποιο αρχείο txt, names.txt, και είναι ο Rob, Tommy, Ιωσήφ, Tommy, Ιωσήφ, RJ, Rob, αυτό που θέλω να κάνω εδώ είναι να βρείτε τον αριθμό των μοναδικών ονομάτων σε αυτό το αρχείο. Έτσι τι θα έπρεπε να είναι η απάντηση; >> [Φοιτητής] 4. Ναι >>. Θα πρέπει να είναι 4 από Rob, Tommy, Ιωσήφ, RJ είναι τα μοναδικά ονόματα μόνο σε αυτό το αρχείο. Το πρώτο βήμα, αν το κάνω μόνο αριθμό λέξεων για names.txt, αυτό είναι πραγματικά να μου λέει τα πάντα. Αυτό είναι στην πραγματικότητα εκτύπωση - Ας δούμε, ο άνθρωπος wc - νέες γραμμές, οι λέξεις, και πλήθος bytes. Αν το μόνο που νοιάζονται για τις γραμμές, τότε μπορώ να κάνω ακριβώς wc-l names.txt. Έτσι, αυτό είναι το βήμα 1. Αλλά δεν θέλω να wc-l names.txt names.txt επειδή περιέχει ακριβώς όλα τα ονόματα, και θέλω να φιλτράρει όλα τα μη μοναδικά αυτά. Έτσι, αν κάνω uniq names.txt, αυτό δεν μου δίνει ακριβώς αυτό που θέλω επειδή τα διπλά ονόματα είναι ακόμα εκεί. Γιατί συμβαίνει αυτό; Γιατί δεν είναι uniq κάνει ό, τι θέλω; [Φοιτητής] Τα αντίγραφα δεν είναι [δεν ακούγεται] >> Ναι. Θυμηθείτε τη σελίδα man για uniq λέει φίλτρο γειτονικών γραμμών που ταιριάζουν. Δεν είναι δίπλα, και έτσι δεν θα τα φιλτράρει. Αν μπορώ να ταξινομήσω για πρώτη φορά, names.txt είδος πρόκειται να θέσει όλες τις διπλές γραμμές μεταξύ τους. Έτσι τώρα names.txt είδος είναι αυτό. Πάω να θέλετε να χρησιμοποιήσετε ότι ως την είσοδο στην uniq, η οποία είναι | uniq. Αυτό μου δίνει Ιωσήφ, RJ, Rob, Tommy, και θέλω να το χρησιμοποιήσουμε αυτό ως την είσοδο στην wc-l, η οποία πρόκειται να μου δώσει 4. Όπως λέει εδώ, τι θα μπορούσε να αγωγού που χρησιμοποιείτε; Μπορείτε να κάνετε πολλά πράγματα, όπως η χρήση μια σειρά από εντολές όπου μπορείτε να χρησιμοποιήσετε την έξοδο από μία εντολή, όπως την είσοδο στην επόμενη εντολή. Μπορείτε να κάνετε πολλά πράγματα, πολλά έξυπνα πράγματα. Ερωτήσεις; Εντάξει. Αυτό είναι το για σωλήνες και ανακατεύθυνση. Τώρα πάμε για την πραγματική ουσία, η κωδικοποίηση πράγματα. Μέσα από αυτό το PDF, θα δείτε αυτή την εντολή, και θα θέλετε να εκτελέσετε αυτήν την εντολή στη συσκευή σας. wget είναι η εντολή για να πάρει μόνο κάτι από το Internet, βασικά, wget έτσι και αυτό το URL. Αν πήγε σε αυτή τη διεύθυνση URL στο πρόγραμμα περιήγησής σας, θα κατεβάσετε το αρχείο. Μόλις κάνετε κλικ σε αυτό, έτσι ώστε να κατεβάσει το αρχείο για μένα. Αλλά γράφοντας wget από αυτό το πράγμα μέσα από το τερματικό πρόκειται ακριβώς για να το κατεβάσετε σε τερματικό σας. Έχω section5.zip, και θα θέλετε να αποσυμπιέσετε section5.zip, η οποία πρόκειται να σας δώσει ένα φάκελο που ονομάζεται σημείου 5, το οποίο θα έχει όλα τα αρχεία θα πάμε να χρησιμοποιούν σήμερα μέσα από αυτό. Όπως δείχνουν τα ονόματα των αρχείων αυτών των προγραμμάτων, είναι λίγο buggy, έτσι ώστε η αποστολή σας είναι να καταλάβω γιατί η χρήση gdb. Μήπως όλοι τους έχουν κατεβάσει / ξέρουν πώς να πάρουν τη λήψη τους στην συσκευή τους; Εντάξει. Τρέχοντας ./buggy1, θα πει σφάλμα Segmentation (πυρήνας ντάμπινγκ), την οποία κάθε φορά που θα πάρετε μια segfault, αυτό είναι ένα κακό πράγμα. Κάτω από ποιες συνθήκες θα πάρετε μια segfault; [Φοιτητής] Αποαναφορά ένα null δείκτη. Ναι >>. Έτσι, αυτό είναι ένα παράδειγμα. Έμμεση αναφορά σε ένα κενό δείκτη θα πάμε για να πάρετε μια segfault. Τι σημαίνει segfault είστε αγγίζει μνήμη σας δεν θα πρέπει να αγγίζουν. Έτσι, μια εύρεση τιμών null δείκτη αγγίζει διεύθυνση 0, και ουσιαστικά, όλοι οι υπολογιστές στις μέρες μας λένε ότι το 0 είναι η διεύθυνση μνήμης που δεν θα πρέπει να αγγίζουν. Έτσι, γι 'αυτό ένα εύρεση τιμών null αποτελέσματα δείκτη σε μια segfault. Όταν συμβεί να μην προετοιμαστεί ένα δείκτη, τότε έχει αξία σκουπίδια, και έτσι όταν προσπαθείτε να dereference αυτό, κατά πάσα πιθανότητα είστε αγγίζει μνήμη που είναι στη μέση του πουθενά. Αν συμβεί να πάρετε τυχεροί και η αξία σκουπίδια συνέβη στο σημείο κάπου στη στοίβα ή κάτι, στη συνέχεια, όταν dereference ότι ο δείκτης που δεν έχουν προετοιμαστεί, τίποτα δεν θα πάει στραβά. Αλλά αν είναι κατάδειξης, ας πούμε, κάπου μεταξύ της στοίβας και του σωρού, ή αυτό είναι μόνο για να δείχνει κάπου που δεν έχει χρησιμοποιηθεί από το πρόγραμμα σας ακόμα, τότε είστε αγγίζοντας μνήμη σας δεν θα πρέπει να αγγίζει και να segfault. Όταν γράφετε μια αναδρομική συνάρτηση και recurses πάρα πολλές φορές και το stack σας μεγαλώνει πάρα πολύ μεγάλο και οι συγκρούεται σε στοίβα τα πράγματα ότι δεν θα πρέπει να συγκρούονται με, είστε αγγίζει μνήμη σας δεν θα πρέπει να αγγίζουν, έτσι ώστε να segfault. Αυτό είναι ό, τι είναι ένα segfault. Είναι, επίσης, για τον ίδιο λόγο ότι αν έχετε μια σειρά σαν - ας πάμε πίσω στο προηγούμενο πρόγραμμα. Σε hello.c--Είμαι ακριβώς πρόκειται να κάνει κάτι άλλο. char * s = "Γειά σου Κόσμε!"? Αν μπορώ να χρησιμοποιήσω * s = s ή κάτι [0] = 'Χ'? Γι 'αυτό γειά σου,. / γειά σου, γιατί ότι segfault; Γιατί αυτό segfault; Τι θα περιμένατε να συμβεί; Αν έκανα printf ("% s \ n", s)? Τι θα περιμένατε να εκτυπωθούν; [Φοιτητής] Χ γειά σου. Ναι >>. Το πρόβλημα είναι ότι όταν δηλώνετε μια σειρά σαν αυτή, s είναι ένας δείκτης που πρόκειται να πάει στη στοίβα, και τι s είναι στραμμένη προς αυτό είναι συμβολοσειρά η οποία περιέχεται σε μνήμη μόνο για ανάγνωση. Έτσι, μόνο από το όνομα, μόνο για ανάγνωση μνήμης, θα πρέπει να πάρετε την ιδέα ότι αν προσπαθήσετε να αλλάξετε ό, τι είναι στην μνήμη μόνο για ανάγνωση, κάνεις κάτι που δεν θα πρέπει να κάνει με τη μνήμη σας και segfault. Αυτό είναι πραγματικά μια μεγάλη διαφορά μεταξύ char * s και char s []. Έτσι, char s [], τώρα αυτή η συμβολοσειρά θα πρέπει να τοποθετούνται στη στοίβα, και η στοίβα δεν είναι μόνο για ανάγνωση, πράγμα που σημαίνει ότι αυτό θα πρέπει να λειτουργεί τέλεια πρόστιμο. Και το κάνει. Να θυμάστε ότι όταν κάνω char * s = "Γειά σου Κόσμε!", S είναι το ίδιο στη στοίβα αλλά επισημαίνει s για κάπου αλλού, και ότι κάπου αλλού συμβαίνει να είναι μόνο για ανάγνωση. Αλλά char s [] είναι απλώς κάτι στη στοίβα. Έτσι, αυτό είναι ένα άλλο παράδειγμα από μια segfault συμβαίνει. Είδαμε ότι ./buggy1 οδήγησε σε segfault. Στη θεωρία, δεν θα πρέπει να εξετάσουμε buggy1.c αμέσως. Αντ 'αυτού, θα εξετάσουμε αυτό με το gdb. Παρατηρήστε ότι όταν παίρνετε σφάλμα Segmentation (πυρήνας ντάμπινγκ), μπορείτε να πάρετε αυτό το αρχείο εδώ ονομάζεται πυρήνας. Αν ls-l, θα δούμε ότι ο πυρήνας είναι συνήθως ένα αρκετά μεγάλο αρχείο. Αυτός είναι ο αριθμός των bytes του αρχείου, έτσι ώστε να φαίνεται σαν να είναι 250-κάτι kilobytes. Ο λόγος για αυτό είναι ότι αυτό η χωματερή πυρήνα είναι πραγματικά είναι όταν κολλάει το πρόγραμμα, η κατάσταση της μνήμης του προγράμματός σας απλά παίρνει αντιγραφεί και επικολληθεί σε αυτό το αρχείο. Παίρνει ντάμπινγκ σε αυτό το αρχείο. Αυτό το πρόγραμμα, ενώ έτρεχε, έτυχε να έχουν μια χρήση της μνήμης της τάξης των 250 kilobytes, και έτσι αυτό είναι που πήρε ντάμπινγκ σε αυτό το αρχείο. Τώρα μπορείτε να δείτε σε αυτό το αρχείο, αν κάνουμε gdb buggy1 πυρήνα. Μπορούμε να κάνουμε μόνο buggy1 gdb, και ότι απλά θα ξεκινήσει gdb τακτικά, buggy1 χρησιμοποιώντας ως αρχείο εισόδου του. Αλλά αν το κάνετε gdb buggy1 πυρήνα, τότε είναι ειδικά πρόκειται να ξεκινήσει το gdb κοιτάζοντας αυτό το αρχείο πυρήνα. Και λέτε buggy1 gdb μέσα ξέρει ότι ο φάκελος πυρήνας προέρχεται από την buggy1 πρόγραμμα. Έτσι gdb buggy1 πυρήνα πρόκειται να μας φέρει άμεσα στο σημείο όπου συνέβη το πρόγραμμα να τερματίσει. Βλέπουμε εδώ Πρόγραμμα τερματίστηκε με το σήμα 11, Τμηματοποίηση σφάλμα. Θα συμβεί για να δείτε μια γραμμή συναρμολόγησης, η οποία κατά πάσα πιθανότητα δεν είναι πολύ χρήσιμη. Αλλά αν πληκτρολογήσετε bt ή backtrace, η οποία πρόκειται να είναι η λειτουργία η οποία μας δίνει τη λίστα των σημερινών πλαισίων στοίβας μας. Έτσι backtrace. Μοιάζει έχουμε μόνο δύο πλαίσια στοίβα. Το πρώτο είναι το κύριο πλαίσιο στοίβας μας, και η δεύτερη είναι η στοίβα πλαισίου για τη λειτουργία αυτή που τυχαίνει να είναι σε, που μοιάζει έχουμε μόνο τον κωδικό συναρμολόγησης. Ας πάμε πίσω σε κύρια λειτουργία μας, και να κάνουμε ότι μπορούμε να κάνουμε πλαίσιο 1, και νομίζω ότι μπορούμε να κάνουμε, επίσης, προς τα κάτω, αλλά σχεδόν ποτέ δεν κάνει τα κάτω - ή πάνω. Ναι. Πάνω και κάτω. Μέχρι σας φέρνει επάνω ένα πλαίσιο στοίβας, σας φέρνει κάτω κάτω ένα πλαίσιο στοίβας. Τείνω να μην το χρησιμοποιήσετε. Θέλω μόνο να πω συγκεκριμένα πλαισίου 1, που είναι να πάτε με το πλαίσιο με την ένδειξη 1. Πλαίσιο 1 θα μας φέρει στο κύριο πλαίσιο στοίβας, και λέει εδώ τη γραμμή του κώδικα που τυχαίνει να είναι σε. Αν θέλαμε ένα ζευγάρι περισσότερες γραμμές κώδικα, μπορούμε να πούμε λίστα, και ότι πρόκειται να μας δώσει όλες τις γραμμές κώδικα γύρω από αυτό. Η γραμμή που ήταν segfaulted σε 6: if (strcmp ("CS50 βράχους", argv [1]) == 0). Εάν δεν είναι εμφανής ακόμα, μπορείτε να το πάρετε κατευθείαν από εδώ μόνο με τη σκέψη γιατί segfaulted. Αλλά μπορούμε να το πάρετε ένα βήμα παραπέρα και να πω, "Γιατί θα argv [1] segfault;" Εκτύπωση Ας argv [1], και μοιάζει με το 0x0, η οποία είναι η μηδενική δείκτη. Είμαστε strcmping CS50 βράχια και άκυρη, έτσι και αυτό πρόκειται να segfault. Και γιατί είναι argv [1] null; [Φοιτητής] Επειδή δεν είχαμε δώσει κάποια ορίσματα της γραμμής εντολών. Ναι. Εμείς δεν της έδωσε κάποια ορίσματα της γραμμής εντολών. Έτσι ./buggy1 θα είναι μόνο για να έχουν argv [0] είναι ./buggy1. Δεν πρόκειται να έχουμε μια argv [1], έτσι ώστε πρόκειται να segfault. Αλλά αν, αντ 'αυτού, να κάνω ακριβώς CS50, πρόκειται να πω Μπορείτε να πάρετε μια D επειδή αυτό είναι αυτό που θα έπρεπε να κάνει. Κοιτάζοντας buggy1.c, είναι υποτίθεται για να εκτυπώσετε "Μπορείτε να πάρετε μια D" - Αν argv [1] δεν είναι "CS50 βράχους», «Μπορείτε να πάρετε μια D", αλλιώς "Μπορείτε να πάρετε ένα Α!" Έτσι, αν θέλουμε ένα Α, θα πρέπει να συγκρίνει αυτό ως αλήθεια, πράγμα που σημαίνει ότι το συγκρίνει με μηδέν. Έτσι, argv [1] πρέπει να είναι "CS50 βράχια». Αν θέλετε να το κάνετε αυτό στη γραμμή εντολών, θα πρέπει να χρησιμοποιήσετε \ για να ξεφύγουν από το χώρο. Έτσι CS50 \ βράχια και μπορείτε να πάρετε μια Α! Αν δεν κάνετε την ανάστροφη κάθετο, γιατί αυτό δεν λειτουργεί; [Φοιτητής] Είναι δύο διαφορετικά επιχειρήματα. Ναι >>. Argv [1] θα είναι CS50, και argv [2] πρόκειται να είναι βράχια. Εντάξει. Τώρα ./buggy2 πρόκειται να segfault πάλι. Αντί να ανοίξετε το αρχείο με πυρήνα της, εμείς απλώς θα ανοίξει buggy2 άμεσα, έτσι gdb buggy2. Τώρα, αν θα τρέξει μόνο το πρόγραμμά μας, τότε πρόκειται να πω Πρόγραμμα λαμβανόμενο σήμα SIGSEGV, η οποία είναι η segfault σήμα, και αυτό είναι που συνέβη να συμβεί. Κοιτάζοντας backtrace μας, θα δούμε ότι ήμασταν στο oh_no λειτουργία, η οποία κλήθηκε από το dinky λειτουργία, η οποία κλήθηκε από το binky λειτουργία, η οποία κλήθηκε από την κύρια. Μπορούμε επίσης να δούμε τα επιχειρήματα για αυτές τις λειτουργίες. Το επιχείρημα για Dinky και binky ήταν 1. Εάν λίστα τη λειτουργία oh_no, βλέπουμε ότι oh_no κάνει ακριβώς char ** s = NULL? * S = "BOOM"? Γιατί δεν θα ήταν αυτό; [Φοιτητής] Δεν μπορείτε να dereference το κενό δείκτη; Ναι >>. Αυτό ακριβώς λέει s είναι NULL, ανεξάρτητα αν αυτό συμβαίνει να είναι ένα char **, το οποίο, ανάλογα με το πώς θα το ερμηνεύσει, θα μπορούσε να είναι ένας δείκτης σε δείκτη σε μια σειρά ή μια σειρά από χορδές. Είναι s είναι NULL, έτσι * s είναι μια εύρεση τιμών null δείκτη, και έτσι αυτό πρόκειται να διακοπεί η λειτουργία του. Αυτός είναι ένας από τους γρηγορότερους τρόπους που μπορείτε ενδεχομένως να segfault. Είναι δηλώνοντας απλώς ένα null δείκτη και αμέσως segfaulting. Αυτό είναι ό, τι oh_no κάνει. Αν πάμε μέχρι ένα καρέ, τότε θα πάμε να μπει σε λειτουργία που ονομάζεται oh_no. Πρέπει να το κάνουμε αυτό κάτω. Εάν δεν εισάγετε μια εντολή και χτυπάτε απλά Enter και πάλι, θα επαναλάβω απλώς την προηγούμενη εντολή που εκτελέσατε. Είμαστε σε πλαίσιο 1. Καταχώρηση αυτό το πλαίσιο, βλέπουμε εδώ είναι η λειτουργία μας. Μπορείτε να χτυπήσει ξανά τη λίστα, ή μπορείτε να κάνετε λίστα των 20 ετών και θα περιλαμβάνει περισσότερους. Η λειτουργία dinky λέει ότι αν i είναι 1, τότε πάμε για τη λειτουργία oh_no, αλλιώς πηγαίνετε στο κρυψίνους λειτουργία. Και ξέρουμε i είναι 1, επειδή τυχαίνει να δείτε εδώ Dinky ότι κλήθηκε με το επιχείρημα 1. Ή μπορείτε απλά να μπορώ να εκτυπώσει και θα πω ότι είναι 1. Αυτή τη στιγμή είμαστε σε dinky, και αν πάμε ένα άλλο πλαίσιο, ξέρουμε ότι θα καταλήξουμε σε binky. Up. Τώρα είμαστε σε binky. Καταχώρηση αυτή τη λειτουργία - η λίστα από πριν από μισή μου κόψει - αυτό που ξεκίνησε ως εάν θ είναι 0, τότε θα πάμε να το ονομάσουμε oh_no, αλλιώς καλέστε dinky. Ξέρουμε ότι ήμουν 1, γι 'αυτό ονομάζεται dinky. Και τώρα είμαστε πίσω στην κύρια και βασικά είναι ακριβώς πρόκειται να είναι int i = rand ()% 3? Αυτό είναι ακριβώς πρόκειται να σας δώσω έναν τυχαίο αριθμό που είναι είτε 0, 1, ή 2. Είναι πρόκειται να καλέσετε binky με αυτόν τον αριθμό, και θα επιστρέψει 0. Κοιτάζοντας αυτό, απλά περπατώντας μέσα από το πρόγραμμα το χέρι, χωρίς να τρέχει αμέσως, θα ορίσετε ένα σημείο καμπής στο κεντρικό, πράγμα που σημαίνει ότι όταν εκτελείτε το πρόγραμμα πρόγραμμα σας τρέχει μέχρι να χτυπήσει το σημείο καμπής. Έτσι λειτουργεί το πρόγραμμα, που θα διαρκέσει και μετά θα χτυπήσει την κύρια λειτουργία και να σταματήσει να λειτουργεί. Τώρα είμαστε στο εσωτερικό των κύριων και βήμα ή την επόμενη θα μας φέρει στην επόμενη γραμμή του κώδικα. Μπορείτε να κάνετε το βήμα ή την επόμενη. Χτύπημα επόμενο, τώρα θ έχει οριστεί για την rand () 3%, έτσι ώστε να μπορεί να εκτυπώσει την τιμή του i, και θα πω ότι είναι 1. Τώρα έχει σημασία αν θα χρησιμοποιήσετε το επόμενο βήμα ή. Υποθέτω ότι πείραξε το προηγούμενο, αλλά θα θέλαμε να χρησιμοποιήσετε στη συνέχεια. Αν χρησιμοποιήσουμε το βήμα, το βήμα σε λειτουργία, πράγμα που σημαίνει ματιά στο πραγματικό πράγμα που συμβαίνει στο εσωτερικό του binky. Αν χρησιμοποιήσουμε επόμενη, τότε αυτό σημαίνει ότι πάει πέρα ​​από τη λειτουργία και πήγαινε στην επόμενη γραμμή του κώδικα στην κύρια λειτουργία μας. Ακριβώς εδώ σε αυτή τη γραμμή, ήμουν πού είπε rand ()% 3? αν έκανα το βήμα, θα πάει στην εφαρμογή του ραντ και να δούμε τι συμβαίνει εκεί, και θα μπορούσα να το βήμα μέσω της λειτουργίας ραντ. Αλλά εγώ δεν ενδιαφέρομαι για τη λειτουργία ραντ. Απλά θέλω να πάω στην επόμενη γραμμή του κώδικα στην κύρια, έτσι μπορώ να χρησιμοποιήσω επόμενη. Τώρα, όμως, να κάνω τη φροντίδα για την binky λειτουργία, γι 'αυτό θέλω να το βήμα σε αυτό. Τώρα είμαι σε binky. Η πρώτη γραμμή του κώδικα πρόκειται να πει εάν (i == 0), παίρνω ένα βήμα, βλέπουμε καταλήγουμε στο Dinky. Αν τα πράγματα λίστα, βλέπουμε ότι ελέγχεται είναι i = 0. i δεν είναι ίσο με μηδέν, οπότε πήγε στο άλλο όρο, η οποία πρόκειται να καλέσει Dinky (i). Μπορεί να μπερδευτείτε. Αν εξετάσουμε μόνο σε αυτές τις γραμμές άμεσα, θα μπορούσε κανείς να σκεφτεί αν (i == 0), εντάξει, τότε πήρα ένα βήμα και τώρα είμαι σε Dinky (i), Μπορείτε να σκεφτείτε ότι πρέπει να σημαίνει i = 0 ή κάτι τέτοιο. Όχι απλά σημαίνει ότι ξέρει ότι μπορεί να κολλήσει απευθείας στο dinky γραμμή (i). Επειδή εγώ δεν είναι 0, το επόμενο βήμα δεν πρόκειται να λήξει στο άλλο. Αλλιώς δεν είναι μια γραμμή πρόκειται να σταματήσει σε. Είναι ακριβώς πρόκειται να πάει στην επόμενη γραμμή μπορεί πραγματικά να εκτελέσει, η οποία είναι Dinky (i). Ενίσχυση σε Dinky (i), βλέπουμε αν (i == 1). Γνωρίζουμε i = 1, έτσι ώστε όταν το βήμα, ξέρουμε ότι πρόκειται να καταλήξει σε oh_no επειδή i = 1 καλεί την oh_no λειτουργία, το οποίο μπορείτε να μπείτε, η οποία πρόκειται να θέσει char ** s = NULL και να αμέσως "BOOM". Και τότε πραγματικά κοιτάζοντας την εφαρμογή του buggy2, αυτό, i παίρνει μόνο έναν τυχαίο αριθμό - 0, 1, ή 2 - καλώντας binky, η οποία αν θ είναι 0 καλεί oh_no, αλλιώς καλεί dinky, η οποία έρχεται εδώ. Αν i είναι 1, oh_no κλήση, καλέστε άλλο κρυψίνους, που έρχονται εδώ, αν i είναι 2, καλέστε oh_no. Δεν χρειάζεται καν να σκέφτονται ότι υπάρχει ένας τρόπος - Υπάρχει κάποιος που να δείτε έναν τρόπο να κάνει αυτό ένα πρόγραμμα που δεν θα segfault; Γιατί αν είμαι λείπει κάτι, αν θ είναι 0, θα segfault αμέσως, αλλιώς θα πάτε σε μια λειτουργία που αν i είναι 1 segfault σας, αλλιώς θα πάτε σε μια λειτουργία όπου αν i είναι 2 segfault σας. Έτσι, δεν έχει σημασία τι θα κάνεις, θα segfault. Υποθέτω ότι ένας τρόπος για να γίνει αυτό θα ήταν αντί να κάνει char ** s = NULL, θα μπορούσατε να malloc χώρος για την εν λόγω σειρά. Θα μπορούσαμε να κάνουμε malloc (sizeof) - sizeof τι; [Φοιτητής] (char) * 5; >> Μήπως αυτό φαίνεται σωστό; Υποθέτω αυτό θα λειτουργήσει, αν έτρεξα πραγματικά, αλλά δεν είναι αυτό που ψάχνω. Κοιτάξτε τον τύπο του s. Ας προσθέσω int *, έτσι int * x. Θα ήθελα να κάνω malloc (sizeof (int)). Ή αν ήθελα μια σειρά 5, θα ήθελα να κάνω (sizeof (int) * 5)? Τι γίνεται αν έχω ένα int **; Τι θα μου malloc; [Φοιτητής] Μέγεθος του δείκτη. Ναι >>. (Sizeof (int *))? Το ίδιο πράγμα εδώ κάτω. Θέλω (sizeof (char *))? Αυτό πρόκειται να διαθέσει χώρο για το δείκτη που δείχνει "BOOM". Δεν χρειάζεται να διαθέσουν χώρο για "BOOM" η ίδια γιατί αυτό είναι ουσιαστικά ισοδύναμο με αυτό που είπα πριν του char * x = "BOOM". "BOOM" υπάρχει ήδη. Συμβαίνει να υπάρχουν στην ανάγνωσης μόνο περιοχή της μνήμης. Αλλά υπάρχει ήδη, πράγμα που σημαίνει αυτή τη γραμμή του κώδικα, αν s είναι ένα char **, τότε * s είναι ένα char * και είστε ρύθμιση αυτή char * στο σημείο να "BOOM". Αν ήθελα να αντιγράψετε "BOOM" σε s, τότε θα πρέπει να διαθέσουν χώρο για s. Θα κάνω * s = malloc (sizeof (char) * 5)? Γιατί 5; Γιατί δεν 4; Μοιάζει με "BOOM" είναι 4 χαρακτήρες. >> [Φοιτητής] Ο χαρακτήρας null. Ναι. Όλα χορδές σας θα πρέπει να το null χαρακτήρα. Τώρα μπορώ να κάνω κάτι σαν strcat - Ποια είναι η λειτουργία για την αντιγραφή ένα string; [Φοιτητής] ορν; >> Strcpy. strcpy άνθρωπος. Έτσι strcpy ή strncpy. strncpy είναι λίγο πιο ασφαλείς αφού μπορείτε να καθορίσετε ακριβώς πόσοι χαρακτήρες, αλλά εδώ δεν έχει σημασία, διότι ξέρουμε. Έτσι strcpy και να δούμε τα επιχειρήματα. Το πρώτο επιχείρημα είναι ο προορισμός μας. Το δεύτερο επιχείρημα είναι η πηγή μας. Εμείς πάμε για να αντιγράψετε σε * προορισμός μας βρέθηκε ο δείκτης "BOOM". Γιατί μπορεί να θέλετε να το κάνετε αυτό με ένα strcpy και όχι μόνο ό, τι είχαμε πριν του * s = "BOOM"; Υπάρχει ένας λόγος ίσως να θέλετε να το κάνετε αυτό, αλλά τι είναι αυτός ο λόγος; [Φοιτητής] Αν θέλετε να αλλάξετε κάτι σε "BOOM". Ναι >>. Τώρα μπορώ να κάνω κάτι σαν s [0] = 'Χ'? επειδή s σημεία στο σωρό και αυτό το χώρο στο σωρό αυτό είναι δείχνει να είναι ένας δείκτης για περισσότερο χώρο στο σωρό, η οποία αποθηκεύει "BOOM". Έτσι, αυτό το αντίγραφο του "BOOM" που αποθηκεύονται στο σωρό. Υπάρχουν τεχνικά δύο αντίγραφα του "BOOM" στο πρόγραμμά μας. Υπάρχει η πρώτη που μόλις δόθηκε από αυτό το "BOOM" σταθερή σειρά, και το δεύτερο αντίγραφο του "BOOM", strcpy δημιουργήθηκε το αντίγραφο του "BOOM". Όμως, το αντίγραφο του "BOOM" που αποθηκεύονται στο σωρό, και ο σωρός είστε ελεύθεροι να αλλάξει. Ο σωρός δεν είναι μόνο για ανάγνωση, έτσι αυτό σημαίνει ότι s [0] πρόκειται να σας αφήσει να αλλάξετε την τιμή του "BOOM". Είναι πρόκειται να σας αφήσει να αλλάξει αυτούς τους χαρακτήρες. Ερωτήσεις; Εντάξει. Προχωρώντας σε buggy3, gdb ας buggy3. Θα τρέξει ακριβώς βλέπουμε και παίρνουμε μια segfault. Αν θέλουμε backtrace, υπάρχουν μόνο δύο λειτουργίες. Αν ανεβαίνουμε σε κύρια λειτουργία μας, θα δούμε ότι segfaulted σε αυτή τη γραμμή. Έτσι απλά κοιτάζοντας αυτή τη γραμμή, για (int γραμμή = 0? Fgets αυτά τα πράγματα δεν είναι ίσο με NULL? γραμμή + +). Προηγούμενο καρέ μας ονομαζόταν _IO_fgets. Θα δείτε ότι πολλοί με ενσωματωμένες λειτουργίες C, ότι, όταν μπορείτε να πάρετε το segfault, θα υπάρξει πραγματικά αινιγματικό ονόματα λειτουργία όπως αυτό _IO_fgets. Αλλά αυτό πρόκειται να σχετίζονται με την παρούσα πρόσκληση fgets. Κάπου εδώ μέσα, είμαστε segfaulting. Αν κοιτάξουμε τα επιχειρήματα για fgets, μπορούμε να τυπώσουμε ρυθμιστικό. Ας εκτυπωθεί ως - Ω, όχι. Εκτύπωση δεν πρόκειται να λειτουργήσει ακριβώς όπως θέλω να. Ας δούμε το πραγματικό πρόγραμμα. Ρυθμιστικό είναι μια σειρά χαρακτήρων. Είναι μια σειρά χαρακτήρων από 128 χαρακτήρες. Έτσι, όταν λέω ρυθμιστικό εκτύπωσης, πρόκειται για την εκτύπωση αυτών των 128 χαρακτήρες, η οποία υποθέτω ότι είναι ό, τι αναμένεται. Αυτό που έψαχνα είναι εκτυπώσετε τη διεύθυνση του buffer, αλλά αυτό δεν πραγματικά να μου πείτε πολλά. Έτσι, όταν τυχαίνει να πω εδώ x ρυθμιστικό, αυτό μου δείχνει 0xbffff090, το οποίο, αν θυμάστε από τις προηγούμενες ή κάποιο σημείο, Oxbffff τείνει να είναι μια στοίβα-ish περιοχή. Η στοίβα τείνει να ξεκινήσει από κάπου λίγο κάτω από 0xC000. Απλά βλέποντας αυτή τη διεύθυνση, ξέρω ότι το ρυθμιστικό συμβαίνει στη στοίβα. Επανέναρξη του προγράμματος μου, τρέχει, μέχρι, buffer είδαμε ήταν αυτή τη σειρά των χαρακτήρων που είναι λίγο πολύ νόημα. Στη συνέχεια, εκτύπωση αρχείων, το αρχείο αυτό δεν μοιάζει; [Φοιτητής] Null. Ναι >>. Το αρχείο είναι ένα αρχείο * του τύπου, έτσι ώστε να είναι ένας δείκτης, και η αξία του εν λόγω δείκτη είναι μηδενική. Έτσι fgets πρόκειται να προσπαθήσει να διαβαστεί από αυτό το δείκτη σε ένα έμμεσο τρόπο, αλλά για να πρόσβαση σε αυτό το δείκτη, πρέπει να dereference αυτό. Ή, για να αποκτήσετε πρόσβαση σε αυτό θα πρέπει να δείχνει προς, αυτό το dereferences. Γι 'αυτό είναι μια εύρεση τιμών null δείκτη και segfaults. Θα μπορούσα να έχω την επανεκκίνηση εκεί. Αν σπάσει σε κεντρικό σημείο μας και να τρέξει, η πρώτη γραμμή του κώδικα είναι char * filename = "nonexistent.txt"? Αυτό θα πρέπει να δώσει μια αρκετά μεγάλη υπόδειξη ως προς το γιατί αυτό το πρόγραμμα αποτυγχάνει. Πληκτρολογώντας δίπλα μου φέρνει στην επόμενη γραμμή, όπου θα ανοίξετε αυτό το αρχείο, και τότε αμέσως να μπει σε γραμμή μας, όπου μια φορά χτύπησα επόμενο, πρόκειται να segfault. Υπάρχει κάποιος που θέλει να ρίξει ένα λόγος για τον οποίο θα μπορούσε να segfaulting; [Φοιτητής] Το αρχείο δεν υπάρχει. Ναι >>. Αυτό υποτίθεται ότι είναι ένας υπαινιγμός ότι κάθε φορά που είστε το άνοιγμα ενός αρχείου που χρειάζεστε για να βεβαιωθείτε ότι το αρχείο υπάρχει στην πραγματικότητα. Έτσι, εδώ, "nonexistent.txt"? Όταν fopen αρχείου για ανάγνωση, τότε θα πρέπει να πούμε αν (αρχείο == NULL) και να πω printf ("Το αρχείο δεν υπάρχει!" ή - ακόμα καλύτερα - αρχείου)? επιστροφή 1? Έτσι τώρα έχουμε ελέγξτε αν είναι NULL πριν από πραγματικά να συνεχίζει και προσπαθεί να διαβαστεί από αυτό το αρχείο. Μπορούμε να το ξανακάνω απλά για να δείτε ότι τα έργα. Είχα σκοπό να συμπεριλάβει μια νέα γραμμή. Έτσι τώρα nonexistent.txt δεν υπάρχει. Θα πρέπει πάντα να ελέγξετε για αυτό το είδος του πράγματος. Θα πρέπει πάντα να ελέγξετε για να δείτε αν fopen επιστρέφει NULL. Θα πρέπει πάντα να ελέγξετε για να βεβαιωθείτε ότι η malloc δεν επιστρέφει NULL, ή αλλιώς segfault. Τώρα buggy4.c. Τρέχοντας. Υποθέτω ότι αυτό είναι σε αναμονή για την είσοδο ή ενδεχομένως άπειρο looping. Ναι, είναι άπειρη looping. Έτσι buggy4. Μοιάζει σαν να είμαστε άπειρη looping. Μπορούμε να σπάσει σε κεντρικό, εκτελέστε το πρόγραμμα μας. Στην gdb, εφ 'όσον η συντομογραφία που χρησιμοποιείτε είναι σαφής ή ειδικές συντομογραφίες που παρέχουν για σας, τότε μπορείτε να χρησιμοποιήσετε για να χρησιμοποιήσετε n επόμενο αντί να χρειάζεται να πληκτρολογήσετε τον επόμενο σε όλη τη διαδρομή. Και τώρα που έχω χτυπήσει n μία φορά, μπορώ να χτυπήσει ακριβώς το Enter για να συνεχίσετε επόμενη αντί να χτυπήσει Εισάγετε n, n Enter, n Enter. Φαίνεται σαν να είμαι σε κάποιο είδος του βρόχου for που είναι για τη διάταξη [i] στο 0. Φαίνεται σαν να είμαι ποτέ να βγουν από αυτό για το βρόχο. Αν θ εκτύπωση, γι 'αυτό είναι 2, τότε θα πάω δίπλα. Θα εκτυπώσετε i, i είναι 3, τότε θα πάω δίπλα. Θα εκτυπώσετε i και i είναι 3. Στη συνέχεια, εκτυπώστε i, i είναι 4. Στην πραγματικότητα, η εκτύπωση sizeof (array), έτσι ώστε το μέγεθος του πίνακα είναι 20. Αλλά φαίνεται σαν να υπάρχει κάποια ειδική εντολή gdb για να πάει έως ότου συμβεί κάτι. Είναι σαν τον καθορισμό ενός προϋπόθεση για την τιμή της μεταβλητής. Αλλά δεν θυμάμαι τι είναι. Έτσι, αν εμείς συνεχίσουμε - Τι έλεγες; Τι σας φέρει μέχρι; [Φοιτητής] Δεν εμφανίζει μπορώ να προσθέσω - >> Ναι. Έτσι εμφανίζεται μπορώ να βοηθήσω. Εάν εμφανίσετε εγώ απλά, θα βάλει εδώ ποια είναι η αξία του i είναι γι 'αυτό δεν πρέπει να το εκτυπώσετε κάθε φορά. Αν απλά συνεχίστε επόμενο, βλέπουμε 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Κάτι πρόκειται τρομερά λάθος, και θ γίνεται επαναφορά στο 0. Κοιτάζοντας buggy4.c, βλέπουμε μόνο που συμβαίνει είναι int array [5]? για (i = 0? i <= sizeof (array)? i + +) array [i] = 0? Τι βλέπουμε ότι είναι λάθος εδώ; Ως έναν υπαινιγμό, όταν έκανα το gdb buggy4 - ας σπάσει κύρια, τρέξιμο - Έκανα εκτύπωσης sizeof (array), μόνο και μόνο για να δούμε τι είναι η κατάσταση όπου θα πρέπει τελικά να ξεσπάσει. Πού είμαι; Μήπως μπορώ να εκτελέσω; Δεν είχα δηλώσει ακόμη. Έτσι εκτύπωση sizeof (array) και αυτό είναι 20, η οποία αναμένεται από σειρά μου έχει μέγεθος 5 και είναι ακέραιοι από 5, έτσι το όλο πράγμα θα πρέπει να είναι 5 * sizeof (int) bytes, όπου sizeof (int) τείνει να είναι 4. Έτσι, sizeof (array) είναι 20. Τι θα πρέπει να είναι αυτό; [Φοιτητής] Χωρίζεται από sizeof (int). >> Ναι, / sizeof (int). Φαίνεται σαν να υπάρχει ακόμα ένα πρόβλημα εδώ. Νομίζω ότι αυτό θα πρέπει απλώς να είναι < δεδομένου ότι είναι λίγο πολύ πάντα <και ποτέ <=. Τώρα, ας σκεφτούμε γιατί αυτό στην πραγματικότητα σπάσει. Υπάρχει κάποιος που να μαντεύει γιατί ήταν i επαναφορά στο 0 με κάθε επανάληψη του βρόχου; Το μόνο πράγμα εδώ μέσα που συμβαίνει είναι ότι η διάταξη [i], η οποία προβλέπεται στο 0. Έτσι, κατά κάποιο τρόπο, αυτή η γραμμή του κώδικα προκαλεί int i μας πρέπει να οριστεί σε 0. [Φοιτητής] Θα μπορούσε να είναι επειδή είναι επιτακτικό τη μνήμη του αυτό το μέρος του i όταν νομίζει ότι είναι το επόμενο στοιχείο του πίνακα; >> [Bowden] Ναι. Όταν θα πάμε πέρα ​​από το τέλος του πίνακα μας, κατά κάποιο τρόπο ότι το διάστημα που είμαστε επιτακτικούς είναι επιτακτικό η τιμή του i. Και έτσι, αν κοιτάξουμε σε buggy4, να σπάσει κύρια, τρέξιμο, ας εκτυπώσετε τη διεύθυνση του i. Φαίνεται σαν να είναι bffff124. Τώρα ας εκτυπώσετε τη διεύθυνση του array [0]. 110. Τι γίνεται με το [1]; 114. [2], 118. 11c, 120. array [5] είναι bfff124. Έτσι, array [5] έχει την ίδια διεύθυνση, όπως εγώ, που σημαίνει ότι array [5] i. Εάν έχουν την ίδια διεύθυνση, που είναι το ίδιο πράγμα. Έτσι, όταν θέτουμε array [5] στο 0, εμείς θ ρύθμιση στο 0. Και αν νομίζετε γι 'αυτό από την άποψη της στοίβας, int i έχει δηλωθεί κατ 'αρχάς, το οποίο σημαίνει ότι θ παίρνει λίγο χώρο στη στοίβα. Στη συνέχεια, array [5] κατανέμεται, ώστε στη συνέχεια 20 bytes που διατίθενται στη στοίβα. Γι 'αυτό και παίρνει κατανέμεται πρώτα, στη συνέχεια, να πάρει κατανέμονται αυτά τα 20 bytes. Γι 'αυτό θα συμβεί ακριβώς πριν από τη σειρά, και λόγω του τρόπου που, όπως είπα και την περασμένη εβδομάδα, όπου αυτό είναι τεχνικά η στοίβα μεγαλώνει προς τα κάτω, όταν δείκτη σε ένα πίνακα, θα είναι εγγυημένο ότι η 0th θέση στη συστοιχία συμβαίνει πάντα πριν από την πρώτη θέση στη συστοιχία. Αυτό είναι το είδος του πώς επέστησε την περασμένη εβδομάδα. Παρατηρήστε ότι στο κάτω μέρος έχουμε τη διεύθυνση 0 και στην κορυφή έχουμε Max διεύθυνση. Η στοίβα είναι πάντα αυξάνεται προς τα κάτω. Ας πούμε ότι εγώ κατανέμουν. Διαθέτουμε ακέραιο i, το οποίο σημαίνει ας πούμε εδώ ακέραιο i παίρνει διατεθεί. Στη συνέχεια, διαθέτουμε μας σειρά από 5 ακέραιους αριθμούς, που σημαίνει ότι κάτω από αυτό, δεδομένου ότι η στοίβα μεγαλώνει προς τα κάτω, να πάρει εκείνα τα 5 κατανέμονται ακέραιοι. Όμως, λόγω του τρόπου με συστοιχίες δουλειά, είμαστε εγγυάται ότι η πρώτη θέση του πίνακα έχει πάντα μια διεύθυνση μικρότερη από τη δεύτερη πράγμα στη συστοιχία. Έτσι σειρά θέση 0 έχει πάντα να συμβεί πρώτα στην μνήμη, λαμβάνοντας υπόψη ότι η θέση του πίνακα 1 πρέπει να συμβεί μετά από αυτό και θέση του πίνακα 2 έχει να συμβεί μετά από αυτό, πράγμα που σημαίνει ότι η θέση 0 array θα συμβεί κάπου εδώ, θέση σειρά 1 θα συμβεί παραπάνω ότι γιατί κινείται επάνω σημαίνει υψηλότερες διευθύνσεις από το μέγιστο διεύθυνση είναι εδώ. Έτσι, array [0] εδώ κάτω, array [1] μέχρι εδώ, array [2] μέχρι εδώ, array [3] μέχρι εδώ. Παρατηρήστε πως πριν χορηγηθεί ακέραιος i όλη τη διαδρομή μέχρι εδώ, καθώς προχωράμε όλο και περισσότερο σε σειρά μας, είμαστε όλο και πιο κοντά στο ακέραιο i μας. Συμβαίνει ότι array [5], η οποία είναι μια θέση πέρα ​​από σειρά μας, είναι ακριβώς όπου ακέραιος i έτυχε να διατεθεί. Έτσι, αυτό είναι το σημείο όπου τυχαίνει να χτυπήσει το χώρο στη στοίβα που κατανεμήθηκε για ακέραιο αριθμό i, και είμαστε ρύθμιση ότι στο 0. Αυτός είναι ο τρόπος που λειτουργεί. Ερωτήσεις; Ναι. [Φοιτητής] Δεν πειράζει. Εντάξει. [Φοιτητής] Πώς μπορείτε να αποφύγετε αυτά τα λάθη είδος; Τέτοιου είδους λάθη; Μην χρησιμοποιείτε C ως γλώσσα προγραμματισμού σας. Χρησιμοποιήστε μια γλώσσα που έχει σειρά όρια ελέγχου. Εφ 'όσον είστε προσεκτικοί, το μόνο που χρειάζεται για να αποφύγω να πάω πέρα ​​από τα όρια του πίνακα σας. [Φοιτητής] Έτσι, εδώ όταν πήγαμε παρελθόν τα όρια του πίνακα σας - [Bowden] Αυτό είναι όπου τα πράγματα αρχίζουν πηγαίνει στραβά. >> [Φοιτητής] Ω, εντάξει. Όσο μένετε στο πλαίσιο της μνήμης που εκχωρείται για την σειρά σας, είστε μια χαρά. Αλλά C δεν κάνει έλεγχο σφαλμάτων. Αν κάνω array [1000], ευχαρίστως θα τροποποιήσει μόνο ό, τι συμβαίνει - Πηγαίνει στην αρχή της συστοιχίας, τότε πηγαίνει 1000 θέσεις μετά και το θέτει σε 0. Δεν κάνει κανένα έλεγχο ότι ω, αυτό δεν έχει στην πραγματικότητα 1000 τα πράγματα σε αυτό. 1000 είναι πολύ πέρα ​​από αυτό που θα πρέπει να αλλάζει, λαμβάνοντας υπόψη ότι η Java ή κάτι που θα πάρει σειρά από τα όρια του δείκτη ή δείκτης εκτός ορίων εξαίρεση. Αυτός είναι ο λόγος που πολλοί από υψηλότερο επίπεδο γλώσσες έχουν αυτά τα πράγματα όπου κι αν πάτε πέρα ​​από τα όρια του πίνακα, μπορείτε να αποτύχει έτσι ώστε δεν μπορείτε να αλλάξετε τα πράγματα από κάτω σας και τότε τα πράγματα πάνε πολύ χειρότερα από ό, τι ακριβώς να πάρει μια εξαίρεση λέγοντας ότι πήγατε πέρα ​​από το τέλος του πίνακα. [Φοιτητής] Και έτσι έπρεπε να είχαμε μόλις αλλάξει το <= σε μόλις > [Bowden] Ναι. Θα πρέπει να είναι > [Φοιτητής] Δεξιά. Περισσότερες ερωτήσεις; Εντάξει. [Φοιτητής] Έχω μια ερώτηση. Ναι >>. [Φοιτητής] Ποια είναι η πραγματική μεταβλητή πίνακα; [Bowden] Όπως Τι είναι η σειρά; Array είναι ένα σύμβολο. Είναι ακριβώς η διεύθυνση της έναρξης των 20 bytes που μας αναφορά. Μπορείτε να σκεφτείτε από το ως ένα δείκτη, αλλά είναι μια συνεχής δείκτη. Από τη στιγμή που τα πράγματα παίρνουν καταρτίζονται, η μεταβλητή πίνακα δεν υπάρχει πια. [Φοιτητής] Έτσι, πώς να βρείτε το μέγεθος του πίνακα; Μέγεθος του πίνακα αναφέρεται στο μέγεθος των εν λόγω μπλοκ ότι σύμβολο αναφέρεται σε. Όταν κάνω κάτι σαν printf ("% p \ n", σειρά)? ας το τρέξει. Τι έκανα ακριβώς κάνω λάθος; Οι «συστοιχίες» δήλωσε Array εδώ. Ω, εδώ. Clang είναι έξυπνος, και συμβαίνει να παρατηρήσετε ότι έχω δηλώσει τον πίνακα ως 5 στοιχεία αλλά είμαι σε θέση ευρετηρίαση 1000. Μπορεί να το κάνει αυτό, διότι αυτά είναι μόνο σταθερές. Το μόνο που μπορεί να φθάσει μέχρι το παρατηρήσει ότι θα πάω πέρα ​​από τα όρια του πίνακα. Αλλά παρατηρήσετε πριν όταν είχαμε i να είναι εσφαλμένη, δεν είναι δυνατόν να προσδιορίσουν πόσες τιμές θα μπορούσα να αναλάβει, έτσι ώστε να μπορεί να προσδιορίσει ότι πήγαινα πέρα ​​από το τέλος του πίνακα. Αυτό είναι ακριβώς Clang είναι έξυπνος. Αλλά να τώρα buggy4. Λοιπόν, τι άλλο κάνω λάθος; Εμμέσως δηλώνοντας λειτουργία βιβλιοθήκης printf ». Πάω να θέλετε να συμπεριλάβετε # . Εντάξει. Τώρα τρέχει buggy4. Εκτύπωση της αξίας του πίνακα, όπως έκανα εδώ, την εκτύπωση ως δείκτης εκτυπώσεις κάτι που μοιάζει με αυτό - bfb8805c - η οποία είναι κάποια διεύθυνση αυτό είναι στη στοίβα-ish περιοχή. Array μόνη της είναι σαν ένα δείκτη, αλλά δεν είναι ένας πραγματικός δείκτης, από ένα κανονικό δείκτη μπορούμε να αλλάξουμε. Array είναι μερικά μόνο σταθερή. Τα 20 μπλοκ μνήμης ξεκινούν από 0xbfb8805c διεύθυνση. Έτσι, μέσα από αυτό το bfb8805c διεύθυνση ή +20--υποθέτω -20 - είναι το σύνολο της μνήμης που διατίθενται για αυτή τη διάταξη. Array, η μεταβλητή είναι η ίδια δεν αποθηκεύονται πουθενά. Όταν την κατάρτιση, ο compiler - κύμα χέρι σε αυτό - αλλά ο compiler θα χρησιμοποιήσει μόνο όταν γνωρίζει σειρά να είναι. Δεν ξέρει πού αρχίζει η σειρά, και γι 'αυτό μπορεί πάντα να κάνει ακριβώς τα πράγματα όσον αφορά τις αποζημιώσεις από την αρχή. Δεν χρειάζεται μια μεταβλητή η ίδια να εκπροσωπεί συστοιχία. Αλλά όταν κάνω κάτι σαν int * p = array? Τώρα p είναι ένας δείκτης που δείχνει την σειρά, και τώρα σ. πραγματικότητα δεν υπάρχει στη στοίβα. Είμαι ελεύθερος να αλλάξει p. Μπορώ να κάνω p = malloc. Έτσι, αρχικά επεσήμανε σειρά? Τώρα δείχνει σε κάποιο χώρο στο σωρό. Δεν μπορώ να κάνω σειρά = malloc. Αν Clang είναι έξυπνος, θα μου φωνάζεις δεξιά από το ρόπαλο. Στην πραγματικότητα, είμαι σίγουρος ότι gcc θα το κάνετε αυτό πάρα πολύ. Έτσι τύπου σειρά «int [5]» δεν μπορεί να αντιστοιχηθεί. Δεν μπορείτε να εκχωρήσετε κάτι σε έναν τύπο πίνακα επειδή πίνακας είναι απλά μια σταθερά. Είναι ένα σύμβολο το οποίο οι αναφορές αυτές 20 bytes. Δεν μπορώ να το αλλάξετε. [Φοιτητής] Και πού είναι το μέγεθος του πίνακα αποθηκεύονται; [Bowden] Δεν είναι αποθηκευμένο πουθενά. Είναι όταν είναι κατάρτιση. Έτσι, όταν είναι το μέγεθος του πίνακα αποθηκεύονται; Μπορείτε να χρησιμοποιήσετε μόνο sizeof (array), μέσα από τη λειτουργία ότι η σειρά είναι η ίδια δήλωσε. Έτσι, αν κάνω κάποια λειτουργία, foo, και να κάνω (int array []) printf ("% d \ n", sizeof (array))? και στη συνέχεια κάτω από εδώ καλώ foo (array)? μέσα από αυτή τη λειτουργία - ας το τρέξει. Αυτό είναι Clang είναι έξυπνος και πάλι. Είναι μου λέει ότι sizeof παράμετρος για τη λειτουργία πίνακα θα επιστρέψει το μέγεθος του «int *». Αυτό θα ήταν ένα λάθος, αν δεν είναι αυτό που ήθελα να συμβεί. Ας πραγματικά να απενεργοποιήσετε Werror. Προειδοποίηση. Προειδοποιήσεις είναι μια χαρά. Θα εξακολουθούν να συγκεντρώνουν όσο έχει μια προειδοποίηση. . / A.out πρόκειται να εκτυπώσετε 4. Η προειδοποίηση που δημιουργήθηκε αποτελεί σαφή ένδειξη για το τι πήγε στραβά. Αυτό int array είναι ακριβώς πρόκειται να εκτυπώσετε sizeof (int *). Ακόμα κι αν έβαλα array [5] εδώ, είναι ακόμα ακριβώς πρόκειται να εκτυπώσετε sizeof (int *). Έτσι, το συντομότερο μπορείτε να το δώσετε σε μια λειτουργία, η διάκριση μεταξύ πινάκων και δεικτών είναι ανύπαρκτη. Αυτό συμβαίνει να είναι ένας πίνακας που δηλώθηκε στη στοίβα, αλλά μόλις περάσουμε αυτή την τιμή, ότι 0xbf μπλα, μπλα, μπλα σε αυτή τη λειτουργία, τότε αυτό δείχνει δείκτη στην εν λόγω συστοιχία επί της στοίβας. Έτσι, αυτό σημαίνει ότι sizeof ισχύει μόνο για τη λειτουργία που η σειρά είχε δηλώσει, πράγμα που σημαίνει ότι όταν είστε κατάρτιση αυτή τη λειτουργία, Clang όταν περνά μέσα από αυτή τη λειτουργία, βλέπει ένα πίνακα είναι int πίνακα μεγέθους 5. Άρα, λοιπόν, βλέπει sizeof (array). Λοιπόν, αυτό είναι 20. Αυτό είναι πραγματικά πώς λειτουργεί sizeof βασικά για σχεδόν όλες τις περιπτώσεις. Sizeof δεν είναι μια λειτουργία? Είναι ένας επιχειρηματίας. Δεν καλέστε το sizeof λειτουργία. Sizeof (int), ο compiler θα μεταφράσει απλώς ότι έως 4. Κατάλαβες; Εντάξει. [Φοιτητής] Έτσι ποια είναι η διαφορά μεταξύ sizeof (array) στην κύρια και στην foo; Αυτό συμβαίνει επειδή λέμε sizeof (array), η οποία είναι τύπου int *, ενώ ο πίνακας εδώ κάτω δεν είναι του τύπου int *, είναι μια int array. [Φοιτητής] Έτσι, αν είχατε την παράμετρο σε array [] αντί int * array, ότι θα σημαίνει ότι θα μπορούσε να αλλάξει παράταξη γιατί τώρα είναι ένας δείκτης; [Bowden] Σας αρέσει αυτό; >> [Φοιτητής] Ναι. Μπορείτε να αλλάξετε σειρά στο πλαίσιο της λειτουργίας τώρα; [Bowden] Θα μπορούσε να αλλάξει σειρά και στις δύο περιπτώσεις. Και στις δύο αυτές περιπτώσεις, είστε ελεύθεροι να πείτε array [4] = 0. [Μαθητής] Αλλά μπορεί να σας κάνει το σημείο σειρά για κάτι άλλο; [Bowden] Αχ. Ναι. Σε κάθε περίπτωση - >> [φοιτητής] Ναι. [Bowden] Η διάκριση μεταξύ array [] και int * array, δεν υπάρχει κανένας. Μπορείτε επίσης να πάρετε κάποια πολυδιάστατη array εδώ για κάποιο βολικό σύνταξη, αλλά είναι ακόμα μόνο ένα δείκτη. Αυτό σημαίνει ότι είμαι ελεύθερος να κάνω array = malloc (sizeof (int))? Και τώρα το σημείο κάπου αλλού. Αλλά ακριβώς όπως το πώς αυτό λειτουργεί για πάντα και πάντα, αλλαγή αυτή σειρά, καθιστώντας το σημείο σε κάτι άλλο δεν αλλάξει αυτή η σειρά εδώ κάτω επειδή είναι ένα αντίγραφο του επιχειρήματος, δεν είναι ένας δείκτης σε αυτό το επιχείρημα. Και στην πραγματικότητα, ακριβώς όπως ακόμη ένδειξη ότι είναι ακριβώς το ίδιο - είδαμε ήδη τι εκτυπώσεις σειρά εκτύπωσης - τι θα γίνει αν εμείς εκτυπώσετε τη διεύθυνση του πίνακα ή τη διεύθυνση της διεύθυνσης του πίνακα είτε από αυτούς; Ας αγνοήσουμε αυτό. Εντάξει. Αυτό είναι μια χαρά. Είναι τρέχει τώρα. / A.out. Σειρά εκτύπωσης, στη συνέχεια να εκτυπώσετε τη διεύθυνση του πίνακα, είναι το ίδιο πράγμα. Array, απλά δεν υπάρχει. Ξέρει πότε είστε εκτύπωση σειρά, τυπώνετε το σύμβολο που αναφέρεται σε αυτά τα 20 bytes. Εκτύπωση τη διεύθυνση του πίνακα, επίσης, σειρά δεν υπάρχει. Δεν έχει μια διεύθυνση, έτσι ώστε να εκτυπώνει μόνο την διεύθυνση των 20 bytes. Μόλις μεταγλωττίσετε τα κάτω, όπως στην καταρτίζονται buggy4 σας. / A.out, πίνακα είναι ανύπαρκτη. Δείκτες υπάρχουν. Οι πίνακες δεν το κάνουν. Τα μπλοκ της μνήμης που αντιπροσωπεύει τη συστοιχία εξακολουθούν να υπάρχουν, αλλά η μεταβλητή πίνακα και μεταβλητές αυτού του τύπου δεν υπάρχουν. Αυτά είναι σαν τις κύριες διαφορές μεταξύ πινάκων και δεικτών Οι συντομότερο μπορείτε να πραγματοποιήσετε κλήσεις λειτουργία, δεν υπάρχει καμία διαφορά. Αλλά μέσα από τη λειτουργία που η ίδια η σειρά έχει δηλωθεί, sizeof λειτουργεί με διαφορετικό τρόπο δεδομένου τυπώνετε το μέγεθος των μπλοκ αντί του μεγέθους του τύπου, και δεν μπορείτε να το αλλάξετε γιατί είναι ένα σύμβολο. Εκτύπωση του το πράγμα και τη διεύθυνση του πράγματος εκτυπώνει το ίδιο πράγμα. Και αυτό είναι λίγο πολύ αυτό. [Φοιτητής] Θα μπορούσατε να πείτε ότι για άλλη μια φορά; Θα μπορούσα να έχω χάσει κάτι. Σειρά εκτύπωσης και τη διεύθυνση του πίνακα εκτυπώνει το ίδιο πράγμα, ενώ αν εκτυπώσετε ένα δείκτη σε σχέση με τη διεύθυνση του δείκτη, το μόνο πράγμα που τυπώνει τη διεύθυνση του τι είστε επισημαίνοντας, το άλλο εκτυπώνει τη διεύθυνση του δείκτη στη στοίβα. Μπορείτε να αλλάξετε το δείκτη? Δεν μπορείτε να αλλάξετε μια σειρά σύμβολο. Και sizeof δείκτης πρόκειται να εκτυπώσετε το μέγεθος αυτού του τύπου δείκτη. Έτσι, int * p sizeof (ιστ) πρόκειται να εκτυπώσετε 4, αλλά int array [5] εκτύπωσης sizeof (array) πρόκειται να εκτυπώσετε 20. [Φοιτητής] Έτσι int array [5] θα εκτυπώσει 20; >> Ναι. Αυτός είναι ο λόγος που μέσα από buggy4 όταν χρησιμοποιείται για να είναι sizeof (array) αυτό έκανε i <20, το οποίο δεν είναι αυτό που θέλαμε. Θέλουμε i <5. >> [Φοιτητής] Εντάξει. [Bowden] Και στη συνέχεια, μόλις αρχίσει περνώντας στις λειτουργίες, αν κάναμε int * p = array? μέσα από αυτή τη λειτουργία, μπορούμε να χρησιμοποιήσουμε βασικά σ. σειρά και με τον ίδιο ακριβώς τρόπο, εκτός από το πρόβλημα sizeof και το μεταβαλλόμενο πρόβλημα. Αλλά ρ [0] = 1? Είναι η ίδια όπως λέγοντας array [0] = 1? Και το συντομότερο λέμε foo (array)? Ή foo (ιστ)? εσωτερικό του συνάρτηση foo, αυτή είναι η ίδια κλήση δύο φορές. Δεν υπάρχει καμία διαφορά μεταξύ αυτών των δύο κλήσεων. Όλοι καλό σε αυτό; Εντάξει. Έχουμε 10 λεπτά. Θα προσπαθήσουμε να ξεπεράσουμε αυτό το πρόγραμμα Typer Hacker, αυτή η ιστοσελίδα, η οποία βγήκε πέρυσι ή κάτι τέτοιο. Είναι ακριβώς υποτίθεται ότι είναι σαν να πληκτρολογήσετε τυχαία και εκτυπώνει - Όποια και αν είναι το αρχείο που συμβαίνει να έχουν φορτωθεί είναι αυτό που μοιάζει με πληκτρολογείτε. Μοιάζει με κάποιο είδος λειτουργικού συστήματος κώδικα. Αυτό είναι που θέλουμε να υλοποιήσουμε. Πρέπει να έχετε ένα εκτελέσιμο ονομάζεται hacker_typer που παίρνει σε ένα επιχείρημα, το αρχείο "τύπος χάκερ." Τρέχοντας το εκτελέσιμο θα πρέπει να καθαρίσετε την οθόνη και στη συνέχεια να εκτυπώσετε ένα χαρακτήρα από το πέρασαν-σε αρχείο κάθε φορά που ο χρήστης πατήσει ένα πλήκτρο. Έτσι, ό, τι πλήκτρο που θα πατήσετε, θα πρέπει να ρίξει μακριά και αντί να εκτυπώσετε ένα χαρακτήρα από το αρχείο αυτό είναι το επιχείρημα. Θα λίγο πολύ να σας πω ποια είναι τα πράγματα που θα πάμε να πρέπει να ξέρετε. Αλλά θέλουμε να ελέγξετε έξω τη βιβλιοθήκη termios. Δεν έχω χρησιμοποιήσει ποτέ αυτή τη βιβλιοθήκη σε ολόκληρη τη ζωή μου, γι 'αυτό έχει πολύ ελάχιστη σκοπούς. Αλλά αυτό πρόκειται να είναι η βιβλιοθήκη που μπορούμε να χρησιμοποιήσουμε για να ρίξει μακριά το χαρακτήρα που έπληξε όταν πληκτρολογείτε σε πρότυπο μέσα Έτσι hacker_typer.c, και θα πάμε να θέλετε να συμπεριλάβετε # . Κοιτάζοντας την σελίδα man για termios - I'm μαντέψουν το τερματικό του λειτουργικού συστήματος ή κάτι τέτοιο - Δεν ξέρω πώς να το διαβάσετε. Κοιτάζοντας αυτό, το λέει για να συμπεριλάβει αυτά τα 2 αρχεία, οπότε θα το κάνουμε αυτό. Το πρώτο πράγμα πρώτα, θέλουμε να λάβει ένα επιχείρημα, το οποίο είναι το αρχείο θα πρέπει να ανοίξει. Έτσι, αυτό που θέλω να κάνω; Πώς μπορώ να ελέγξω να δω δεν έχω ούτε ένα επιχείρημα; [Φοιτητής] Αν argc είναι ίσοι. >> [Bowden] Ναι. Έτσι, αν (argc = 2!) Printf ("Χρήση:% s [για να ανοίξετε το αρχείο]"). Έτσι τώρα, αν μπορώ να εκτελέσω αυτό, χωρίς να παρέχει ένα δεύτερο επιχείρημα - OH, χρειάζομαι τη νέα γραμμή - θα δείτε ότι λέει χρήσης:. / hacker_typer, και στη συνέχεια το δεύτερο επιχείρημα πρέπει να είναι το αρχείο που θέλετε να ανοίξετε. Τώρα τι μπορώ να κάνω; Θέλω να διαβάσετε από αυτό το αρχείο. Πώς μπορώ να διαβάσω από ένα αρχείο; [Φοιτητής] Μπορείτε να το ανοίξετε πρώτα. Ναι >>. Έτσι fopen. Τι σημαίνει fopen μοιάζει; [Φοιτητής] Όνομα. >> [Bowden] Όνομα πρόκειται να είναι argv [1]. [Φοιτητής] Και τότε τι θέλετε να κάνετε με αυτό, έτσι ώστε το - >> [Bowden] Ναι. Έτσι, αν δεν θυμάστε, μπορείτε να το κάνετε μόνο fopen άνθρωπος, όπου πρόκειται να είναι μια const char * path όπου διαδρομή είναι όνομα αρχείου, const char * mode. Αν συμβεί να μην θυμάται τι κατάσταση είναι, τότε μπορείτε να ψάξετε για λειτουργία. Μέσα από τις σελίδες του ανθρώπου, ο χαρακτήρας της καθέτου είναι αυτό που μπορείτε να χρησιμοποιήσετε για να αναζητήσετε τα πράγματα. Γι 'αυτό τον τύπο / τρόπο για να αναζητήσετε λειτουργία. n και Ν είναι αυτό που μπορείτε να χρησιμοποιήσετε για να μετακινηθείτε μέσα από τους αγώνες αναζήτησης. Εδώ λέει τα σημεία λειτουργίας επιχείρημα σε μια σειρά αρχίζοντας με μία από τις ακόλουθες αλληλουχίες. Έτσι, r, Άνοιγμα αρχείου κειμένου για ανάγνωση. Αυτό είναι ό, τι θέλουμε να κάνουμε. Για την ανάγνωση, και θέλω να αποθηκεύσετε αυτό. Το θέμα πρόκειται να είναι ένα αρχείο *. Τώρα τι θέλω να κάνω; Δώσε μου μια δεύτερη. Εντάξει. Τώρα τι θέλω να κάνω; [Φοιτητής] Ελέγξτε αν είναι NULL. >> [Bowden] Ναι. Κάθε φορά που ανοίγετε ένα αρχείο, βεβαιωθείτε ότι είστε σε θέση να με επιτυχία να το ανοίξετε. Τώρα θέλω να κάνω ότι termios πράγματα που θέλω να διαβάσετε πρώτα τις τρέχουσες ρυθμίσεις μου και να σώσει αυτούς σε κάτι, τότε θέλω να αλλάξω τις ρυθμίσεις μου να πετάει κάθε χαρακτήρα που πληκτρολογείτε, και στη συνέχεια θέλω να ενημερώσετε αυτές τις ρυθμίσεις. Και στη συνέχεια, στο τέλος του προγράμματος, θέλω να αλλάξω πίσω στις αρχικές ρυθμίσεις μου. Έτσι, το struct θα είναι του τύπου termios, και είμαι πρόκειται να θέλουν δύο από αυτά. Η πρώτη θα είναι current_settings μου, και στη συνέχεια, από όπου και αν πρόκειται να είναι hacker_settings μου. Κατ 'αρχάς, θα πάω να θέλετε να αποθηκεύσετε τις τρέχουσες ρυθμίσεις μου, τότε Πάω να θέλετε να ενημερώσετε hacker_settings, και στη συνέχεια το δρόμο στο τέλος του προγράμματός μου, θέλω να επαναφέρετε τις τρέχουσες ρυθμίσεις. Έτσι, εξοικονομώντας τις τρέχουσες ρυθμίσεις, ο τρόπος που λειτουργεί, εμείς termios άνθρωπος. Βλέπουμε ότι έχουμε αυτό το int tcsetattr, int tcgetattr. Περνώ σε ένα struct termios με το δείκτη του. Ο τρόπος με τον οποίο θα δούμε είναι - έχω ήδη ξεχάσει τι η συνάρτηση κλήθηκε. Αντιγράψτε και επικολλήστε. Έτσι tcgetattr, τότε θέλω να περάσει στο struct ότι είμαι αποθήκευση των πληροφοριών σε, η οποία πρόκειται να είναι current_settings, και το πρώτο επιχείρημα είναι ο file descriptor για το πράγμα που θέλετε να αποθηκεύσετε τα χαρακτηριστικά του. Τι το Περιγραφέας αρχείο είναι είναι όπως κάθε φορά που ανοίγετε ένα αρχείο, παίρνει ένα περιγραφέα αρχείου. Όταν fopen argv [1], παίρνει ένα περιγραφέα αρχείου που έχετε αναφορά κάθε φορά που θέλετε να διαβάσει ή να γράψει σε αυτό. Αυτό δεν είναι το Περιγραφέας αρχείο που θέλετε να χρησιμοποιήσετε εδώ. Υπάρχουν τρεις περιγραφείς αρχείων που έχετε από προεπιλογή, που είναι στάνταρ σε, τυποποιημένα, και τυπικό σφάλμα. Από προεπιλογή, νομίζω ότι είναι πρότυπο είναι 0, από πρότυπο είναι 1, και τυπικό σφάλμα είναι 2. Έτσι, αυτό που μπορώ να θέλετε να αλλάξετε τις ρυθμίσεις του; Θέλω να αλλάξω τις ρυθμίσεις της κάθε φορά που χτύπησα ένα χαρακτήρα, Θέλω να ρίξει αυτό το χαρακτήρα μακριά αντί να τα εκτυπώνει στην οθόνη. Τι ρεύμα - πρότυπο, πρότυπο έξω, ή τυπικό σφάλμα - ανταποκρίνεται στα πράγματα, όταν πληκτρολογείτε στο πληκτρολόγιο; >> [Φοιτητής] Πρότυπο μέσα >> Ναι. Έτσι, μπορώ να κάνω είτε 0 ή μπορώ να κάνω stdin. Παίρνω το current_settings του προτύπου μέσα Τώρα θέλω να ενημερώσετε αυτές τις ρυθμίσεις, Έτσι, πρώτα θα αντιγράψουμε σε hacker_settings τι είναι current_settings μου. Και πώς structs έργο θα είναι απλά να αντιγράψετε. Αυτό αντιγράφει όλα τα πεδία, όπως θα περίμενε κανείς. Τώρα θέλω να ενημερώσετε μερικά από τα πεδία. Κοιτάζοντας termios, θα πρέπει να διαβάσετε πολλά από αυτό ακριβώς για να δούμε τι θα θέλετε να αναζητήσετε, αλλά οι σημαίες θα πάμε να θέλετε να αναζητήσετε είναι ηχώ, έτσι ECHO χαρακτήρες εισόδου Echo. Πρώτα θέλω να - έχω ήδη ξεχάσει τι είναι τα πεδία. Αυτό είναι ό, τι το struct μοιάζει. Έτσι τρόπους εισόδου νομίζω θέλουμε να αλλάξουμε. Θα εξετάσουμε τη λύση για να βεβαιωθείτε ότι είναι αυτό που θέλουμε να αλλάξουμε. Θέλουμε να αλλάξουμε lflag προκειμένου να αποτραπεί χρειάζεται να κοιτάξετε μέσα από όλα αυτά. Θέλουμε να αλλάξουμε τους τρόπους τοπικό. Θα πρέπει να διαβάσετε όλο αυτό το πράγμα που πρέπει να κατανοήσουμε, όπου τα πάντα ανήκει ότι θέλουμε να αλλάξουμε. Αλλά είναι μέσα από τις τοπικές τρόπους μεταφοράς, όπου θα πάμε να θέλουν να το αλλάξουμε αυτό. Έτσι hacker_settings.cc_lmode είναι ό, τι λέγεται. c_lflag. Αυτό είναι όπου θα μπει σε bitwise φορείς. Είμαστε είδος του από του χρόνου, αλλά θα περάσει μέσα από αυτό πραγματικά γρήγορα. Αυτό είναι όπου θα μπει σε bitwise φορείς, όπου Νομίζω ότι είπε μια φορά εδώ και πολύ καιρό ότι κάθε φορά που ξεκινάτε ασχολούνται με σημαίες, θα πάμε να χρησιμοποιούν bitwise χειριστή πολλά. Κάθε bit της σημαίας αντιστοιχεί σε κάποιο είδος της συμπεριφοράς. Έτσι, εδώ, αυτή η σημαία έχει ένα σωρό διαφορετικά πράγματα, όπου όλα αυτά σημαίνουν κάτι διαφορετικό. Αλλά αυτό που θέλω να κάνω είναι απλά να απενεργοποιήσετε το κομμάτι που αντιστοιχεί στην ECHO. Έτσι για να τη σειρά του ότι εκτός κάνω & = ¬ ECHO. Στην πραγματικότητα, νομίζω ότι είναι σαν Techo ή κάτι τέτοιο. Είμαι ακριβώς πρόκειται να ελέγξει και πάλι. Μπορώ να termios. Είναι ακριβώς ECHO. ECHO πρόκειται να είναι ένα ενιαίο κομμάτι. ¬ ECHO πρόκειται να σημαίνει όλα τα bits οριστεί σε 1, το οποίο σημαίνει ότι όλες οι σημαίες που να ισχύει εκτός από το κομμάτι της ECHO. Με το που έληξε στις τοπικές σημαίες μου με αυτό, αυτό σημαίνει ότι όλα τα σημαίες που χρησιμοποιούνται σήμερα για την αλήθεια που θα εξακολουθεί να ρυθμιστεί σε αλήθεια. Εάν η σημαία της ECHO μου έχει οριστεί σε true, τότε αυτό είναι κατ 'ανάγκη οριστεί σε FALSE στη σημαία της ECHO. Έτσι, αυτή η γραμμή του κώδικα γίνεται ακριβώς έξω από τη σημαία της ECHO. Οι υπόλοιπες γραμμές κώδικα, θα αντιγράψετε μόνο τους προς το συμφέρον του χρόνου και στη συνέχεια να εξηγήσει τους. Στο διάλυμα, είπε 0. Είναι ίσως καλύτερα να πω ρητά stdin. Παρατηρήστε ότι κάνω επίσης ECHO | ICANON εδώ. ICANON αναφέρεται σε κάτι ξεχωριστό, κάτι που σημαίνει κανονική λειτουργία. Τι σημαίνει κανονική λειτουργία είναι συνήθως όταν πληκτρολογείτε τη γραμμή εντολών, πρότυπο δεν επεξεργάζεται τίποτα μέχρι να χτυπήσει νέα γραμμή. Έτσι, όταν κάνετε GetString, πληκτρολογείτε ένα σωρό πράγματα, τότε θα χτυπήσει νέα γραμμή. Αυτό είναι όταν έχει σταλεί με το πρότυπο μέσα Αυτή είναι η προεπιλεγμένη. Όταν απενεργοποιήσετε την κανονική λειτουργία, τώρα κάθε μεμονωμένο χαρακτήρα πατήσετε είναι αυτό που παίρνει επεξεργασία, η οποία είναι συνήθως το είδος των κακών γιατί είναι αργή για να επεξεργαστεί αυτά τα πράγματα, και γι 'αυτό είναι καλό να το buffer σε ολόκληρες σειρές. Αλλά θέλω κάθε χαρακτήρα για να υποβληθούν σε επεξεργασία δεδομένου ότι δεν θέλω να περιμένω για μένα να χτυπήσει newline πριν επεξεργάζεται όλους τους χαρακτήρες που έχω την πληκτρολόγηση. Αυτό απενεργοποιεί κανονική λειτουργία. Αυτό σημαίνει ότι τα πράγματα μόνο όταν πραγματικά επεξεργάζεται χαρακτήρες. Αυτό σημαίνει ότι η επεξεργασία τους αμέσως? Το συντομότερο είμαι πληκτρολογώντας τους, την επεξεργασία τους. Και αυτή είναι η λειτουργία, η οποία είναι η ενημέρωση για τις ρυθμίσεις μου σε πρότυπο, TCSA μέσα και να το κάνουμε τώρα. Οι άλλες επιλογές είναι να περιμένετε μέχρι ό, τι είναι σήμερα για το ρεύμα σε επεξεργασία. Αυτό δεν πειράζει πραγματικά. Ακριβώς αυτή τη στιγμή να αλλάξετε τις ρυθμίσεις μου να είναι ό, τι είναι σήμερα hacker_typer_settings. Υποθέτω ότι αυτό που ονομάζεται hacker_settings, οπότε ας το αλλάξουμε αυτό. Αλλάξτε τα πάντα για να hacker_settings. Τώρα, στο τέλος του προγράμματος μας, θα πάμε να θέλουν να επιστρέψουν σε ό, τι είναι σήμερα μέσα από normal_settings, η οποία πρόκειται να δούμε ακριβώς όπως και normal_settings. Παρατηρήστε Δεν έχω αλλάξει καμία από normal_settings μου αφού αρχικά το πάρει. Στη συνέχεια, για να αλλάξετε τους ακριβώς πίσω, θα τους περάσει πίσω στο τέλος. Αυτή ήταν η ενημέρωση. Εντάξει. Τώρα, μέσα από εδώ θα εξηγήσω ακριβώς τον κώδικα προς το συμφέρον του χρόνου. Δεν είναι ότι μεγάλο μέρος του κώδικα. Βλέπουμε διαβάζουμε ένα χαρακτήρα από το αρχείο. Ζητήσαμε την f. Τώρα μπορείτε να fgetc άνθρωπος, αλλά πώς fgetc πρόκειται να λειτουργήσει είναι ακριβώς πρόκειται να επιστρέψει το χαρακτήρα που μόλις διαβάσατε ή ΕΟΦ, η οποία αντιστοιχεί στο τέλος του αρχείου ή κάποιο happening σφάλματος. Είμαστε looping, συνεχίζοντας να διαβάσει έναν χαρακτήρα από το αρχείο, έως ότου έχουμε ξεμείνει από χαρακτήρες να διαβάσετε. Και ενώ το κάνουμε αυτό, περιμένουμε σε ένα μόνο χαρακτήρα από το πρότυπο μέσα Κάθε φορά που πληκτρολογείτε κάτι στη γραμμή εντολών, που διαβάζει ένα χαρακτήρα από το πρότυπο μέσα Στη συνέχεια, putchar είναι ακριβώς πρόκειται να θέσει το char διαβάζουμε εδώ από το αρχείο έξω πρότυπο. Μπορείτε να putchar άνθρωπος, αλλά αυτό είναι απλά βάζοντας το πρότυπο έξω, είναι ότι ο χαρακτήρας εκτύπωση. Θα μπορούσατε επίσης να κάνετε απλά printf ("% c", γ)? Ίδια ιδέα. Αυτό πρόκειται να κάνει το μεγαλύτερο μέρος της δουλειάς μας. Το τελευταίο πράγμα που θα πάμε να θέλετε να κάνετε είναι απλά fclose αρχείο μας. Αν δεν fclose, αυτό είναι μια διαρροή μνήμης. Θέλουμε να fclose το αρχείο που άνοιξε αρχικά, και νομίζω ότι αυτό είναι αυτό. Αν κάνουμε αυτό, πήρα ήδη προβλήματα. Ας δούμε. Τι να διαμαρτύρονται για? Αναμενόμενη «int», αλλά είναι επιχείρημα του τύπου «struct _IO_FILE *». Θα δούμε αν αυτό λειτουργεί. Επιτρέπεται μόνο σε C99. Augh. Εντάξει, να hacker_typer. Τώρα έχουμε πιο χρήσιμες περιγραφές. Έτσι, η χρήση της αδήλωτης αναγνωριστικό »normal_settings». Εγώ δεν το ονομάσουμε normal_settings. Κάλεσα το current_settings. Οπότε ας αλλάξει όλα αυτά. Τώρα περνάει το επιχείρημα. Θα κάνω αυτό 0 για τώρα. Εντάξει. . / Hacker_typer cp.c. Επίσης, δεν καθαρίσετε την οθόνη από την αρχή. Αλλά μπορείτε να κοιτάξετε πίσω στο τελευταίο σετ πρόβλημα για να δείτε πώς μπορείτε να καθαρίσετε την οθόνη. Είναι εκτύπωση μόνο μερικά χαρακτήρες ενώ αυτή κάνει ό, τι θέλω να κάνω. Εντάξει. Και να σκεφτόμαστε γιατί αυτό έπρεπε να είναι 0 αντί του stdin, η οποία θα πρέπει να καθορίσει # 0, αυτό διαμαρτύρονται ότι - Πριν, όταν είπα ότι δεν υπάρχει περιγραφείς αρχείων, αλλά τότε θα πρέπει επίσης * Το αρχείο σας, Περιγραφέας ένα αρχείο είναι μόνο ένα ακέραιο, λαμβάνοντας υπόψη ότι ένα αρχείο * έχει ένα σωρό πράγματα που συνδέονται με αυτό. Ο λόγος που πρέπει να πούμε 0 αντί του stdin stdin είναι ότι είναι ένα αρχείο * που δείχνει το πράγμα που έχει αναφορά περιγραφέας αρχείου 0. Έτσι, ακόμη και εδώ, όταν κάνω fopen (argv [1], παίρνω ένα αρχείο * πίσω. Αλλά κάπου σε αυτό το αρχείο * είναι ένα πράγμα που αντιστοιχεί στο περιγραφέα αρχείου για το αρχείο. Αν κοιτάξετε την σελίδα man για ανοικτή, έτσι νομίζω ότι θα πρέπει να κάνουμε Man 3 ανοικτό - nope - 2 ανοικτές άνθρωπος - ναι. Αν κοιτάξετε τη σελίδα για ανοικτή, ανοικτό είναι σαν ένα χαμηλότερο επίπεδο fopen, και αυτό είναι το πραγματικό επιστροφή περιγραφέα αρχείου. fopen κάνει ένα σωρό πράγματα στην κορυφή των ανοικτών, που αντί να επιστρέψει μόνο ότι περιγραφέα αρχείου επιστρέφει ένα σύνολο FILE * δείκτης μέσα από τα οποία είναι λίγο Περιγραφέας αρχείο μας. Έτσι, στο πρότυπο αναφέρεται στο αρχείο * πράγμα, ενώ 0 αναφέρεται στο ακριβώς το πρότυπο περιγραφέα αρχείου από μόνη της. Ερωτήσεις; [Γέλια] ανατίναξε μέσα από αυτό. Εντάξει. Εμείς τελειώσαμε. [Γέλια] [CS50.TV]