[Powered by Google Translate] [Раздел 5 - по-комфортно] [Роб Боудън - Харвардския университет] [Това е CS50. - CS50.TV] Както казах в моя имейл, че има много неща, които можете да използвате различна от уреда, за да се прави на проблемните комплекти. Препоръчваме ви да го направите в уреда само защото тогава ние можем да ви помогне по-лесно и ние знаем как всичко ще работи. Но както един пример от където можете да направите неща, ако, да речем, не да имат достъп на уреда или искате да работите в мазето Science Center - което всъщност те имат уреда - ако искате да работите навсякъде. Един такъв пример е виждали ли сте / чували за SSH? SSH е просто като се свърже с нещо. Всъщност, точно сега съм SSHed в уреда. Аз никога не работят директно в уреда. Тук е уред, и ако погледнете тук виждате този IP адрес. Аз никога не работят в самия уред; Аз винаги идват на iTerm2 прозорец / терминален прозорец. Можете SSH, че IP адрес, SSH jharvard@192.168.129.128. Спомням си, че номер много лесно, защото това е толкова хубав модел. Но това ще ме питате за моята парола, а сега съм в уреда. По принцип, в този момент, ако сте отворили терминал вътре в самия уред, този интерфейс, но можете да го използвате, е точно същото като интерфейс съм тук, но сега сте SSHed. Вие не трябва да SSH на уреда. Един пример на друго място, където бихте могли да SSH е, че съм сигурен, че имате по подразбиране - О. По-голяма. Всеки от вас трябва да са по подразбиране FAS на сървърите на FAS. За мен, аз ще SSH на rbowden@nice.fas.harvard.edu. Това ще да ви попитам, че за първи път, а вие казвате "да". Моята парола е просто ще бъде паролата си FAS. И така, сега, аз съм SSHed хубави сървъри, и мога да правя каквото си искам тук. Много от класове, може да предприемете, като 124, ще трябва да качите неща тук действително да представят проблемните комплекти. Но казват, че не разполагат с достъп до вашия уред. След това можете да правите неща, като тук ще кажа - Това е само нашата секция от въпроси. Тя ще ви помоля да направите това в уреда. Вместо това просто ще го направя на сървъра. Отивам да разархивирате. Проблемът ще бъде, че сте свикнали да използвате нещо като Gedit или каквото и вътрешността на уреда. Нали няма да има, че на FAS сървър. Всичко това е просто ще бъде този текстова интерфейс. Така бихте могли да, опитайте се да научите текстов редактор, който те имат. Те имат Nano. Нано обикновено е доста лесен за използване. Можете да използвате стрелките и въведете нормално. Така че не е трудно. Ако искате да получите наистина фантазия можете да използвате Emacs, което аз вероятно не трябва да се отвори, защото аз дори не знам как да затворите Emacs. Control X, Control C? Да. Или можете да използвате Vim, който е това, което аз използвам. И така, това са вашите възможности. Ако не искате да направите това, можете също така, ако се вгледате в manual.cs50.net - О. На компютър, можете да SSH с PuTTY, които вие ще трябва да изтеглите отделно. На Mac, може просто от Терминал употреба или по подразбиране можете да изтеглите iTerm2, което е като хубава, специален терминал. Ако отидете в manual.cs50.net ще видите линк към Notepad + +, което е това, което можете да използвате на компютъра. Тя ви позволява да SFTP от Notepad + +, която е в основата SSH. Това ще ви позволи да направите, е да редактирате вашите файлове локално, и след това всеки път, когато искате да ги спаси, ще спаси на nice.fas, , където ще можете да ги изпълните. И еквивалент на Mac ще да TextWrangler. Така че ви позволява да правите едно и също нещо. Тя ви позволява да редактирате файлове на местно ниво и да ги запишете на nice.fas, , където ще можете да ги изпълните. Така че, ако някога сте остана без един уред, имате тези опции все още го правя проблемните комплекти. Един проблем ще бъде, че няма да има CS50 библиотека защото nice.fas не по подразбиране това. Можете да изтеглите CS50 библиотека - Не мисля, че имам нужда, че в този момент. Можете да изтеглите CS50 библиотека и го копирайте в nice.fas или аз мисля, че в този момент ние не го използвате повече така или иначе. Или, ако го направим, можете да за момента да го замени с реализации на функциите в библиотеката CS50 така или иначе. Така че не трябва да бъде, че голяма част ограничение. И това е всичко. Аз ще се върна на уреда, ние ще направим всичко, което е по уреда. Поглед към нашата секция от въпроси, в началото, както казах в моя имейл, ние трябва да говорим за един кратък, който трябваше да се гледа. Ние имаме пренасочване на тръби и тези три въпроса. На което поток функции като ФОРМАТ напиши по подразбиране? Така поток. Какво е поток? Поток е в общи линии, като това е само част - Тя дори не е източник на 1s и 0s. Потокът, че искам тук е стандартна. И така стандарт навън е поток, който, когато ви пиша до него, се появява на екрана. Стандартен изход, като поток, това означава, че просто напишете 1s и 0s да го и на другия край на стандартния изход просто чете от този поток. Това е просто низ от 1s и 0s. Можете да пишете на потоци или можете да прочетете от потоци в зависимост от това какво всъщност е потока. Другите две по подразбиране потоци са стандартни и стандартната грешка. Standard е, когато се GetString, ви чака да въведете неща. Така че ви очаква, всъщност чака на стандарт в , което е наистина това, което ще получите, когато пишете на клавиатурата. Вие пишете в стандарт инча Стандартна грешка по същество е еквивалент на стандартния изход, но тя е специализирана в тази при печат на стандартна грешка, което се очаква да отпечатвате само съобщения за грешки на това така че може да прави разлика между редовните съобщения, отпечатани на екрана срещу съобщения за грешки, в зависимост от това дали те отидоха в стандарт или за стандартната грешка. Файлове. Стандарт, като стандарт, и стандартната грешка са само специални потоци, но наистина всеки файл, когато отворите файл, тя се превръща в поток от байтове където можете просто да прочетете от този поток. Вие, в по-голямата си част, може просто да мисля, че на файл като поток от байтове. Така че това, което потоци да те пишат по подразбиране? Стандартна навън. Каква е разликата между> и >>? Ли гледате видео преди това? Добре. > Ще бъде как да пренасочат в файлове, и >> също ще пренасочване на изхода във файл, но това е, вместо да приложи към файла. Например, нека кажем, че се случи да има волята си тук, и само неща вътрешността на волята си е котка, котка, куче, риба, куче. Едно, че имате команда в командния ред е котка, която е просто да отпечатате това, което е във файл. Така че, когато казвам волята си котка, тя ще да отпечатате котка, котка, куче, риба, куче. Това е, всички котка. Това означава, че отпечатва стандарт, котка, котка, куче, риба, куче. Ако аз вместо искате да пренасочите, че във файл, да използвате> и го пренасочи към каквото и да е файл. Ще се обадя на файла. Така че сега, ако ли, ще видиш, че има нов файл, наречен файл. И ако го отворя, ще има точно това, което и котки, пригодени в командния ред. Така че сега, ако аз го направя отново, то тогава ще да пренасочи изхода в досието, Отивам да имат една и съща точно това. Така че технически, то напълно се припокрива, което имахме. И ние ще видя дали мога да променя волята си, взех куче. Сега, ако ние котка Dict отново в досието, ще има нова версия с куче отстранени. Така че напълно го отменя. Вместо това, ако ние използваме >>, е да добавите файл. Сега, отваряне на файл, ние имаме само отпечатва два пъти едно и също нещо защото тя беше там веднъж, после ние добавят към оригиналната. Така че това> и >>. Ли следващата попитам - Той не пита за това. Другото, което имаме е <, която, ако пренасочва стандарт, <Ще се пренасочи стандартния. Нека видим дали имаме пример. Мога да напиша една истинска бързо. Нека да вземем всеки файл, hello.c. Относително проста файл. Аз съм просто низ и след това печат "Hello" независимо от низ, аз току-що влезе. Така че направи здрасти и след това. / Здрасти. Сега той ме подкани да въведете нещо, което означава, че те чака на неща, които трябва да се вписват в стандартната инча Така че въведете каквото си искам в стандарт инча Ние просто ще да кажа здрасти, Роб! След това печат за стандарта Здравейте, Роб! Ако го направя. / Здрасти и след това пренасочване, за сега можете да пренасочват само от файл. Така че, ако в някой файл, TXT, и аз си Роб, ако аз тичам здрасти и след това пренасочване на текстовия файл в / Здравейте, това е ще да кажа здрасти, Роб! веднага. Когато за първи път получава GetString и чака стандарт в стандарт е вече не чака на клавиатурата за да се вписани данни. Вместо това, ние сме пренасочени стандарт, за да се четат от текстовия файл. И така ще четат от текстовия файл, който е само линията Роб, и след това да отпечатате Здравейте, Роб! И ако исках, аз също може да свърши работа. / Здравей правиш 2> пренасочване на стандартната грешка. Така че, ако нещо се стандартната грешка, тя не би се сложи в txt2. Но ако го направя 2>, то тогава все още отпечатване Здравейте, Роб! командния ред защото аз съм само пренасочване на стандартната грешка, аз не съм пренасочване стандарт. Стандартна грешка и извън стандарта са различни. Ако искате да пишете на стандартната грешка, след това може да се промени това, за да бъде fprintf да STDERR. Така че, по подразбиране, ФОРМАТ отпечатва на стандартния изход. Ако искам да печатате ръчно стандартна грешка, тогава трябва да използвате fprintf и да определи това, което искате да печатате. Ако вместо това е fprintf стандартния изход, това е в общи линии еквивалентни на ФОРМАТ. Но fprintf стандартна грешка. Така че сега, ако пренасочи в txt2 Здравейте, Роб! все още се отпечатва в командния ред , тъй като тя ще се отпечатват на стандартна грешка и аз съм само пренасочване стандарт. Ако сега пренасочване на стандартната грешка, сега не се отпечатва и txt2 ще бъде Здравейте, Роб! Така че сега, можете да отпечатате вашите действителни грешки стандартната грешка и да отпечатате вашите редовни съобщения на стандартния изход. И така, когато изпълнявате вашата програма, можете да го стартирате като / Здравей, този тип с 2> така, че вашата програма ще протичат нормално, но всички съобщения за грешки, които ви да проверите по-късно в грешка дневник толкова грешки, и след това погледнете по-късно и грешки файл ще има каквито и да било грешки, които са се случили. Въпроси? Последното от тях е тръба, която можеш да се сетиш, като стандарт от една команда и стандарта на следващата команда. Един пример тук е ехо е нещо командния ред че е просто ще ехо, каквото и да постави като аргумент. Аз няма да сложи кавички. Echo дрън, дрън, дрън е просто да отпечатате дрън, дрън, дрън. Преди, когато казах, че трябваше да се примири Роб в текстовия файл защото мога да пренасочват само текстови файлове, вместо това / ако Повтарям Роб и след това тръба в него. / здравей, това ще направи същото вид на нещо. Това е изхода от тази команда, ехо Роб, и да го използвате като вход за / здрасти. Можете да мислите за него като за първото пренасочване ехо Роб във файл и след въвеждане в / Здравей, този файл, който бе изведен. Но това отнема временен файл на картината. Въпроси за това? Следващият въпрос е ще включва това. Какво тръбопровода може да използвате, за да намерите броя на уникални имена във файл, наречен names.txt? Командите, които ще искате да използвате тук са уникални, така че Uniq, а след това и тоалетна. Можете да направите Uniq човек да погледнете какво прави, и това е просто ще филтрира съседни съвпадение на линии от входа. И човекът тоалетна ще да отпечатате нов ред, дума и броя байтове за всеки файл. И последно, че ще искате да използвате, е нещо, , което се случва просто да сортирате линии на текстовия файл. Ако направя някой файл TXT, names.txt, и това е Роб, Томи, Йосиф, Томи, Йосиф, RJ, Роб, това, което искам да направя тук, е да се намери броят на уникални имена в този файл. И така, какво трябва да бъде отговорът? >> [Ученик] 4. >> Да. Тя трябва да бъде 4, тъй като Роб, Томи, Йосиф, RJ са само уникални имена в този файл. Първата стъпка, ако аз просто правя брой думи на names.txt, това всъщност ми казва всичко. Това всъщност е печат - нека да видим, човек WC - нови редове, думи и брой байтове. Ако само се грижи за линии, тогава може просто да направи тоалетна-л names.txt. Така че това е стъпка 1. Но аз не искам да names.txt WC-л, защото names.txt съдържа само имената на всички, и аз искам да филтрирате, които не са уникални. Така че, ако правя Uniq names.txt, че не ме даде това, което искам защото дублирани имена са все още там. Защо е така? Защо не е Uniq прави това, което искам? [Ученик] дубликати не се чува] >> Да. Запомни страницата човек за Uniq казва филтър съседни линии съвпадение. Те не са в непосредствена близост, така че няма да ги филтрирате. Ако аз сортирате тях на първо място, нещо names.txt ще постави всички дублиращи се линии заедно. Така че сега вид names.txt е това. Отивам да искате да използвате това като принос към Uniq, което е | Uniq. Това дава ми Йосиф, RJ, Роб, Томи, и аз искам да се използват като вход-л тоалетна, който ще ми даде 4. Както се казва тук, какво тръбопровода може да използвате? Можете да направите много неща, като с помощта на поредица от команди , когато използвате изхода от една команда като вход към следващата команда. Можете да направите много неща, много умни неща. Въпроси? Добре. Това е за тръби и пренасочване. Сега отиваме на действителната неща, кодиране неща. Вътре в този PDF, ще видите тази команда, и вие ще искате да стартирате тази команда във вашия уред. Wget е командата за получаване на нещо от интернет, общо взето, Wget и този URL. Ако отидеш на този URL адрес в браузъра ви, че ще свалите този файл. Аз просто кликне върху него, така че го изтеглили файла за мен. Но писмено Wget на това нещо вътре в терминала е просто да го изтеглите във вашия терминал. Имам section5.zip, и вие ще искате да разархивирате section5.zip , която ще ви даде една папка, наречена раздел 5, който ще има всички файлове, ние ще трябва да се използват днес в нея. Файлови имена на тези програми показват, те са малко бъги, така че вашата мисия е да разбера защо използването на GDB. Всички ли са ги изтеглили / знаят как да ги свален в уреда си? Добре. Работещи ./buggy1, той ще каже Сегментация грешка (ядро дъмпингови), които всеки път, когато получите segfault, това е нещо лошо. При какви обстоятелства да получите segfault? [Ученик] Dereferencing с нулев указател. >> Да. Така че това е един пример. , Dereferencing нулев указател, ти започваш да се получи segfault. Какво segfault средства е, че докосвате памет не трябва да се докосват. Така dereferencing нулев указател докосва адрес 0, и в общи линии, всички компютри в днешно време казват, че адрес 0 памет не трябва да се докосват. Така че това е защо dereferencing нулеви резултати показалеца в segfault. Когато се случи да не се инициализира указател, то тя има стойността на боклук, и така, когато се опитате да сочен, по всяка вероятност сте докосва памет , която е в средата на нищото. Ако се случи да получите късмет и стойността на боклука се случи да се отбележи някъде на стека или нещо такова, тогава, когато и сочен, че показалеца, които не са инициализирани нищо няма да се обърка. Но ако тя е насочена, да речем, някъде между стека и на куп, или е насочена само до някъде, че не е била използвана от вашата програма, все още, тогава сте докосва памет не трябва да се докосват и ви segfault. Когато се напише рекурсивна функция и тя recurses твърде много пъти и стака си расте твърде голям и стека се сблъсква в нещата , че не трябва да се сблъскат с това, което се докосва памет не трябва да се докосва, така че segfault. Ето какво segfault. Това е същата причина, че ако имате низ като нека се върнем към предишната програма. В hello.c-Аз съм просто ще направи нещо друго. Чар * = "Здравей, свят!"; Ако използвам * S = нещо или и [0] = 'X'; така че се здравей, / здравей, защо че segfault? Защо това segfault? Какво би очаквате да се случи? Ако го бях направил ФОРМАТ ("% S \",), какво бихте се очаква да бъдат отпечатани? [Ученик] X здрасти. >> Да. Проблемът е, че когато вие декларирате, низ като това, и е указател, че ще отиде в стека, и какво и е насочена към това е низ, който се съдържа в памет само за четене. Така че просто от името, памет само за четене, можете да получите представа че ако се опитате да промените това, което е в памет само за четене, правиш нещо, което не трябва да се прави с памет и ви segfault. Това всъщност е голяма разликата между Чар * и Чар []. Така Чар [], сега този низ ще бъде поставен върху стека, и стека не е само за четене, което означава, че това трябва да работи перфектно глоба. И го прави. Не забравяйте, че когато правя Чар * = "Здравей, свят!", А себе си е в стека но посочва някъде другаде, и че някъде другаде се случва да бъде само за четене. Но Чар [] е просто нещо, на стека. Така че това е още един пример на segfault случва. Видяхме че ./buggy1 в segfault. На теория не би трябвало да изглежда в buggy1.c веднага. Вместо това, ние ще разгледаме през GDB. Забележете, че когато се Сегментация грешка (ядро дъмпингови), можете да получите този файл тук, наречена ядро. Ако LS-л, ще видим, че основната обикновено е доста голям файл. Това е броят на байтовете на файла, така че да изглежда като това е 250-тина килобайта. Причината за това е, че това, което в основата сметището всъщност е е, когато си програма забива, състоянието на паметта на програмата си просто получава копират и поставят в този файл. То се изхвърлят в този файл. Тази програма, докато тя е била в ход, се случи да има използването на паметта от около 250 килобайта, и така, че това, което се изхвърлят в този файл. Сега можете да погледнете този файл, ако ние не направим GDB buggy1 ядро. Ние можем само да направим GDB buggy1, и че просто ще започнат GDB редовно, използване на buggy1 като входен файл. Но ако вие направите GDB buggy1 ядро, то тогава е специално ще се включи GDB като гледа, че основната файла. И искаш да кажеш buggy1 средства GDB знае, че, че основният файл идва от buggy1 програма. Така GDB buggy1 ядрото е незабавно ще ни донесе до мястото, където се случи да прекрати програмата. Тук виждаме програма прекратен със сигнал 11, сегментация вина. Се случи да видите една линия, на събранията, което вероятно не е много полезно. Но ако въведете BT или обратно проследяване, че ще бъде функцията , който ни дава списък на нашите настоящи рамки стека. Така че, обратно проследяване. Изглежда, че имаме само два стека рамки. Първата е нашата основна рамка стека, , а втората е стека рамка за тази функция, че да се озовем в който изглежда като имаме само код събрание за. Така че нека да се върнем в нашата основна функция, и да се направи, че не можем да направим рамка 1, и аз мисля, че ние можем да направим надолу, но почти никога не правя - или нагоре. Да. Нагоре и надолу. Up ви носи една купчина рамка, надолу ви носи купчина рамка. Склонен съм никога да не се използва това. Аз просто казва конкретно рамка 1, която е да отидете на рамката, означен с 1. Frame 1 ще ни въвеждат в основната стека рамка, и то се казва тук линията на кода, който се случи да бъде. Ако искахме още няколко реда код, можем да кажем списък и това ще ни даде на всички линии на код около него. Линията segfaulted 6: ако (strcmp ("CS50 скали", argv [1]) == 0). Ако това все още не е ясно, можете да го получите направо от тук само с мисъл защо segfaulted. Но можем да го отнеме една стъпка по-нататък и да каже: "Защо argv [1] segfault? Печат Да argv [1], и тя изглежда като това е 0x0, което е нулев указател. Ние сме strcmping CS50 скали и нулеви, и така че ще segfault. И защо е argv [1] нула? [Студент, защото не сме го даде никакви аргументи от командния ред. Да. Ние не даде никакви аргументи от командния ред. Така ./buggy1 само ще трябва argv [0] да ./buggy1. Няма да има argv [1], така че ще segfault. Но ако вместо това, аз правя само CS50, той ще каже Получавате D защото това е това, което трябваше да направя. Поглед към buggy1.c, тя е трябвало да отпечатате "Получавате D" - Ако argv [1] не е "CS50 скали", "Можете да получите D", в противен "Можете да получите А!" Така че, ако искаме А, ние трябва да се сравни като истински, което означава, че го сравнява с 0. Така argv [1] трябва да бъде "CS50 скали". Ако искате да направите това в командния ред, трябва да използвате \ да избяга пространството. Така CS50 \ скали и ще получите A! Ако не направите наклонена черта, защо не работи? [Ученик] Това са две различни аргументи. >> Да. Argv [1] ще бъде CS50 и argv [2] ще бъде скали. Добре. Сега ./buggy2 ще segfault отново. Вместо да го отворите с основната си файл, ние просто ще отвори buggy2 директно, така GDB buggy2. Сега, ако ние просто нашата програма, то тогава ще да кажа, програма приемания сигнал SIGSEGV което е segfault сигнал и това е мястото, където това се е случило да се случи. Поглед към нашата обратно проследяване, ние виждаме че бяхме във функцията oh_no, който беше наречен от функцията готина, който беше наречен от функцията Бинки който беше наречен от главния. Можем също така да види аргументите на тези функции. Аргументът за дребен и Бинки е един. Ако ние списък функцията oh_no, ние виждаме, че oh_no е просто Чар ** = NULL; * = "Бум"; Защо се провали? [Ученик] не може да сочен нулев указател? >> Да. Това е просто казвам, и е NULL, независимо дали това се случва да бъде знак **, които, в зависимост от това как го тълкува, тя може да бъде указател към указател към низ или масив от низове. Това е и е NULL, така че * и се dereferencing нулев указател, и така това ще се срине. Това е един от най-бързите начини, по които би могло да segfault. Това е просто обявяване на нулев указател и веднага segfaulting. Ето какво прави oh_no. Ако се качим един кадър, след това отиваме да влязат в функцията, която нарича oh_no. Аз трябва да направя това. Ако не въведете команда и можете просто да натиснете Enter отново тя просто ще повторете предишната команда, която ще ви се притече. Ние сме в кадър 1. Изброяването на тази рамка, което виждаме тук е нашата функция. Вие може да удари отново, или можете да направите списък 20 и той ще съдържа повече. Функцията готина казва, че ако аз е 1, тогава отидете на функцията oh_no, друг да отиде на дебнещ функция. И знам, че е една, защото ние се случи да видите тук че готина е наречен с аргумента 1. Или пък може просто да мога да отпечата и ще кажа, че е една. В момента сме в готина и ако отидем до друга рамка, ние знаем, че ще свърши в Бинки. Up. Сега сме в Бинки. Изброяването на тази функция - списък от преди почивката ме отреже - тя започна, като че ли е 0, тогава ние ще да го наричат ​​oh_no, друг разговор дребен. Знаем, че аз бях един, така го наричат ​​дребен. И сега сме в основната и основната е просто ще бъде Int = RAND () от 3%; Това е просто ще ви дам един случаен номер, който е или 0, 1 или 2. Тя ще се обади Бинки с този номер, и тя ще върне 0. Поглед към това, просто ходене чрез програмата ръчно, без да го изпълняват веднага, трябва да зададете точка за пробив в основния, което означава, че когато стартирате програмата програмата работи, докато не достигне критичната точка. Така че изпълнението на програмата, тя ще върви и след това ще се появи на основната функция и да спре да работи. Сега ние сме вътре в основната и стъпка или следващата ще ни доведе до следващия ред код. Можете да направите стъпка или следващата. Отиват на следващата, сега аз е настроен да ранд () от 3%, така че ние можем да отпечатаме стойността на I, и то ще кажа, че е едно. Сега го прави, независимо дали ние използваме следващата или стъпка. Предполагам, че това има значение в предишния, но би искал да използва следващия. Ако използваме стъпка, стъпка в тази функция, което означава поглед към действително нещо , което се случва вътре в Бинки. Ако ние използваме следващия, то това означава над функцията и просто отидете на следващия ред на кода в нашата основна функция. Точно тук по тази линия, аз бях къде го каза ранд () от 3%; ако го направя стъпка, той ще отиде в изпълнението на ранд и погледнете какво се случва там, и аз може да се стъпи чрез функцията ранд. Но не ми пука за функцията на Ранд. Аз просто искам да отида на следващия ред код в основната, така че да използвам следващия. Но сега ми пука за Бинки функция, така че аз искам да се оттегли в това. Сега съм в Бинки. Първия ред на кода ще каже, ако (I == 0), направи крачка, виждаме, че в крайна сметка най-дребен. Ако списък на нещата, виждаме, че тя проверява е = 0. аз не е равна на 0, така че тя отиде в друго състояние, който ще се обадя дребен (I). Може да се бърка. Ако просто погледнете тези линии, ще си помислите, ако (I == 0), Добре, тогава взех една стъпка и сега съм в спретнат (I), може да мисля, че това трябва да означава, I = 0 или нещо такова. Това просто означава, че той знае, че може да се придържаме директно на линията готина (I). , Защото не е 0, следващата стъпка няма да завърши в друго. Друго не е на линия, че ще поканим в. Това е просто да отида на следващия ред, тя действително може да изпълни, което е дребен (I). Стъпвайки в спретнат (I), виждаме, ако (I == 1). Мога да разбера = 1, така че когато излезем, ние знаем, че започваш да се озове в oh_no защото аз = 1 се нарича функция oh_no, който можете да влезете, които ще определят Чар ** = NULL и веднага "Boom". И тогава всъщност търсят в изпълнението на buggy2, това, аз просто случаен номер - 0, 1 или 2 - призвание Бинки които, ако е 0 го нарича oh_no, иначе го нарича дребен, който идва тук. Ако аз е едно, обадете се oh_no, друг разговор дебнещ, който идва тук, ако аз е 2, oh_no. Аз дори не мисля, че има начин - Ли някой да види начин за извършването на тази програма, която не ще segfault? Защото, освен ако аз не съм липсва нещо, ако аз е 0, веднага ще segfault, друго да отидете на функция, която, ако аз 1 ви segfault, друго да отидете на функция, където, ако аз два segfault. Така че без значение какво правиш, segfault. Предполагам, че един от начините за определяне би било, вместо да правиш Чар ** = NULL, изчистване на пространство за тази струна. Можем да го направим изчистване (sizeof) - sizeof какво? [Ученик] (Чар) * 5? >> Ли това, изглежда, нали? Предполагам, че това ще работи, ако аз действително се завтече, но не е това, което търся. Виж вида на S. Нека добавим INT *, така Int * х. Аз ще направя изчистване (sizeof (INT)). Или, ако исках масив от 5, аз ще направя (sizeof (INT) * 5); Какво ще стане, ако имам INT **? Това, което бих изчистване? [Ученик] Размер на показалеца. >> Да. (Sizeof (INT *)); Същото е и тук. Искам (sizeof (Чар *)); Това се случва за разпределяне на пространството за показалеца, който сочи към "Boom". Не трябва да отделят място за "бум" защото това по същество е еквивалентно на това, което казах преди Чар * х = "Boom". "Boom" вече съществува. Това се случва да съществува само за четене района на паметта. Но тя вече съществува, което означава, че тази линия на код, ако и е знак **, * S е знак * и сте създаването на този знак *, за да сочат към "Boom". Ако исках да копирате "бум" в S, тогава аз ще трябва да отделят място за S. Ще направя * = изчистване (sizeof (Чар) * 5); Защо пет? Защо не 4? Тя изглежда като "бум" е 4 символа. >> Студент Нулевата характер. Да. Всички на вашите струни ще се нуждаят от нулевата характер. Сега мога да направя нещо като strcat - Каква е функцията за копиране на низ? [Ученик] cpy? >> Strcpy. мъж strcpy. Така strcpy или strncpy. strncpy е по-сигурен, тъй като можете да определите точно колко знака, но тук няма значение, защото ние знаем. Така strcpy и погледнете в аргументацията. Първият аргумент е нашата дестинация. Вторият аргумент е нашият източник. Отиваме да копирате в нашата дестинация * S показалеца "бум". Защо може да искате да направите това с strcpy вместо само това, което имахме преди * S = "бум"? Има причина, може да искате да направите това, но каква е тази причина? [Ученик] Ако искате да промените нещо в "бум". >> Да. Сега мога да направя нещо подобно и [0] = 'X'; защото точки, за да грамада и, че пространството на куп, че е насочена към е указател за повече пространство на куп, който се съхранение на "Boom". Така че това копие на "бум" се съхранява в купчина. Има технически две копия на "бум" в нашата програма. Налице е първият, който току-що от този "бум" низ константа, и второ копие на "Boom", strcpy създаде копие на "Boom". Но копието на "бум" се съхраняват на куп, и куп вие сте свободни да се промени. Купчина не е само за четене, така че това означава, че [0] ще ви позволи да промените стойността на "Boom". Това ще ви позволи да промените тези знаци. Въпроси? Добре. Преминавайки към buggy3, нека GDB buggy3. Ние просто го изпълним и виждаме ние се segfault. Ако ние обратно проследяване, има само две функции. Ако се качим в нашата основна функция, ние виждаме, че ние segfaulted в този ред. Така че, просто гледам на тази линия, за (INT ред = 0; fgets тези неща не е равно NULL; линия + +). Нашата предишния кадър се нарича _IO_fgets. Ще видите, че много с вградени функции C, че когато получите на segfault, ще има наистина загадъчни имена на функции Харесвам това _IO_fgets. Но това ще се отнасят до този fgets повикване. Някъде вътре, ние сме segfaulting. Ако се вгледаме в аргументите на fgets, ние можем да отпечатаме буфер. Нека да отпечатате като - О, не. Печат не ще да работи точно така, както аз искам тя да. Нека да погледнем в действителната му програма. Buffer е знак масив. Той е герой масив от 128 символа. Така че, когато казвам, печат буфер, ще отпечатате тези 128 символа, които предполагам е това, което се очаква. Това, което търсех, е да изписва адреса на буфера, но това не наистина да ми каже много. Така че, когато се случи да се каже тук х буфер, той ми показва 0xbffff090, който, ако си спомняте от по-рано или някакъв момент, Oxbffff тенденция да бъде стак-Иш регион. Стека има тенденция да се започне от някъде малко под 0xc000. Просто като видя този адрес, знам, че буфер се случва в стека. Възобновяване на програмата ми, бягай, буфер, което видяхме беше тази поредица от знаци , които са почти безсмислени. След отпечатване на файл, какво файл изглежда? [Ученик] Null. >> Да. Файла е * тип файл, така че е указател, и стойността на тази показалеца е нула. Така че fgets ще се опита да прочетете от тази показалеца, по индиректен начин, но за да се достъп до този показалеца, той трябва да го сочен. Или, с цел достъп до това, което трябва да се посочи, го dereferences. Така че това е dereferencing нулев указател и да го segfaults. Можех да го рестартира. Ако се разделим нашата основна точка и тичам, първия ред на кода е знак * име на файл = "nonexistent.txt"; Това би трябвало да даде доста голям намек защо тази програма не успее. Въвеждане на следващия ме довежда до следващия ред, когато отворите този файл, и после веднага да влязат в нашата линия, където някога ударих, той ще да segfault. Някой иска ли да се изхвърлят причина, поради която може да се segfaulting? [Ученик] Файлът не съществува. >> Да. Това е трябвало да бъде намек , че всеки път, когато при отваряне на файла трябва да се провери, че файлът действително съществува. Така че тук, "nonexistent.txt"; Когато fopen име на файла за четене, ние трябва да се каже (файл == NULL) и да кажа ФОРМАТ ("Файлът не съществува!" или - още по-добре - име на файл); връщане 1; Така че сега ние проверяваме, за да се види дали е NULL преди всъщност да продължава и се опитва да чете от този файл. Ние можем да го правя само за да видя, че това работи. Имах намерение да се включи нов ред. Така че сега nonexistent.txt не съществува. Винаги трябва да проверявате за този вид на нещо. Винаги трябва да проверите, за да видите, ако fopen връща NULL. Винаги трябва да проверите, за да сте сигурни, че изчистване не се връща NULL, или друго, което segfault. Сега buggy4.c. В града. Предполагам, че това е, че трябва да се изчака за вход или евентуално безкрайно обикаляне. Да, това е безкрайно обикаляне. Така buggy4. Изглежда, че ние сме безкрайно обикаляне. Ние можем да пробие в основния, стартирайте програмата ни. GDB, толкова дълго, колкото съкращението, която използвате, е недвусмислено или специални съкращения, които те предоставят за вас, тогава можете да използвате N, за да се използва по-нататък вместо да се налага да пишете следващата целия път. И сега, че съм ударен н веднъж, мога да просто да натиснете Enter, за да се запази ще следващия вместо да се налага да се удари н Въведете, N Enter, N Въведете. Изглежда, че съм в някаква линия, че настройката на масив [I] 0. Тя изглежда като аз никога не съм се счупи тази линия. Ако аз отпечатате, така че е два, тогава ще се върви напред. Ще отпечатам, аз е 3, тогава ще се върви напред. Ще отпечатам и аз 3. На следващо място, аз печатате, е 4. Всъщност, печат sizeof (масив), така че големината на масива е 20. Но изглежда, че има някаква специална команда GDB, докато нещо се случи. Това е като състояние на стойността на променливата. Но аз не си спомням какво е то. Така че, ако продължават Какво искаш да кажеш? Какво ви донесе? [Ученик] се показва добавя - >> Да. Така че покажете мога да помогна. Ако ние просто показва, тя ще постави тук, каква е стойността на АЗ така че аз не трябва да го разпечатате всеки път. Ако ние просто продължавай следващия, ние виждаме, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Нещо става ужасно погрешно, и аз се върнете към 0. Поглед към buggy4.c, ние виждаме всичко, което се случва, е INT масив [5]; за (I = 0; и <= sizeof (масив), аз + +) масив [] = 0; Какво виждаме, че не е наред тук? Като един намек, когато правех GDB buggy4 - нека прекъсне основната писта - Печат sizeof (масив) само за да видя какво е състоянието е мястото, където най-после трябва да избухне. Къде съм? Съм тичал? Не са декларирали все още. Така отпечатате sizeof (масив) и това е 20, която се очаква, тъй като моята масив е с размер 5 и е на пет числа, , така че цялото нещо трябва да бъде 5 * sizeof (INT) байта, където sizeof (INT) Претендира да е 4. Така че sizeof (масив) е 20. Какво трябва да стане това? [Ученик] Разделени от sizeof (INT). >> Да, / sizeof (INT). Изглежда, че все още има проблем тук. Мисля, че това трябва да бъде само < тъй като това е почти винаги <и никога не <=. Сега нека помислим защо това всъщност е разбит. Някой има ли предположения защо бях възстановите 0 през всяка итерация на цикъла? Единственото нещо вътре от тук, което се случва е, че масив [I] е настроен на 0. Така че по някакъв начин, тази линия на код причинява вътр и да бъде поставено на 0. [Ученик] Може ли да бъде, защото това е незачитане на паметта на тази част от I , когато мисли, че е следващия елемент на масива? >> Bowden Да. Когато отиваме след края на нашия масив, по някакъв начин, че пространството, че ние сме висш висш стойността на I. И така, ако се вгледаме в buggy4, почивка на Майн, план, да отпечатате адрес на АЗ. Тя изглежда като че ли е bffff124. Сега нека да отпечатате адрес на масив [0]. 110. Ами [1]? 114. [2], 118. 11в, 120. масив [5] е bfff124. Така масив [5] има същия адрес като мен, което означава, че този масив [5]. Ако те имат един и същ адрес, те са едно и също нещо. Така че, когато ние се създаде масив [5], за да 0, ние сме и определяне на 0. И ако мислите за това по отношение на стека, Int I е обявен за първият, което означава, че получава малко пространство в стека. Тогава масив [5] се разпределят, така че след това се разпределят 20 байта в стека. Така че аз се разпределят най-напред, а след това се разпределят тези 20 байта. Така че аз се случва точно преди масив, защото от начина, като казах миналата седмица, когато е технически стека расте надолу, когато индекс в масив, ние се гарантира, че 0th позиция в масива винаги се случва преди първата позиция в масива. Това е един вид как да го привлече миналата седмица. Забележете, че в дъното имаме адрес 0 и в горната част имаме адрес Макс. Стека стават все повече. Да кажем, че аз разпределят. Разпределят цяло число, което означава, нека просто кажем, че тук цяло число и получава разпределени. Тогава ще разпредели нашата гама от пет числа, което означава, че под това, от стека се разраства, тези пет числа се разпределят. Но тъй как масиви, гарантира, че ние сме на първа позиция в масива винаги има адрес по-малко от второто нещо в масива. Така 0 масив позиция винаги трябва да се случи първо в паметта, като има предвид, че масив позиция 1 трябва да се случи след това и масив позиция 2 трябва да се случи след това, което означава, че позицията, че 0 масив ще се случи някъде тук, масив позиция 1 ще се случи горе, че тъй като се движи нагоре означава по-високи адреси, тъй като максималната адрес е тук. Така масив [0] тук, масив [1], масив [2], масив [3] тук. Забележете как преди разпределени цяло число и по целия път до тук, както ние се движат повече и повече в нашия масив, ние сме все по-близо и по-близо до нашето Аз цяло число. Просто така се случва, че масив [5], която е една позиция извън нашия масив, е точно където цяло число се случи да се разпределят. Така че това е точката, където ние се случи да се удари пространство в стека , които бяха отпуснати за цяло число и, и ние сме на посочената до 0. Ето как работи. Въпроси? Да. [Ученик] Никога не обръщай внимание. Добре. [Ученик] Как да избегнем тези вид на грешки? Този вид грешки? Да не се използва като език за програмиране. Използвайте език, който има масив границите проверка. Докато сте внимателни, просто трябва да избегне покрай границите на вашата масив. [Ученик] Така че тук, когато мина покрай границите на вашата масив - [Bowden] Това е, когато нещата започват да се обърка. >> [Ученик] О, добре. Толкова дълго, колкото да останеш в паметта, разпределена за вашия масив, всичко е наред. Но C не прави проверка за грешки. Ако го направя масив [1000], с удоволствие ще просто променя каквото и да се случи - Той отива в началото на масива, а след това той отива 1000 позиции след и го постави на 0. Той не направи никаква проверка, че о, това не действително да има хиляда неща в него. 1000 е по-далеч от това, което трябва да се променя, като има предвид, че Java или нещо ще се масив на индекса на границите или индекс границите изключение. Ето защо много по-високи езици ниво тези неща където, ако излизат извън границите на масива, не успеете така че да не може да промени нещата под вас и тогава нещата вървят много по-лошо, отколкото просто да се изключение кажеш, че си отиде след края на масива. [Ученик] И така, трябва ли току-що са се променили <= просто > Bowden Да. Той трябва да бъде > [Ученик] Точно така. Още въпроси? Добре. [Ученик] Аз имам един въпрос. >> Да. [Ученик] Каква е реалната променлива масив? [Bowden като какво е масив? Array само по себе си е символ. Това е само адреса на началото на 20 байта, че ние се обръщате. Можете да мислите за него като показалка, но той е константа показалеца. Веднага след като нещата се събират, променливата масив вече не съществува. [Ученик] И така, как го намери големината на масива? Размер на масива се отнася до размера на този блок, че този символ се отнася до. Когато правя нещо подобно ФОРМАТ ("% P \ N", масив); нека да го стартирате. Какво съм направил погрешно? "Масив" Array отчитат тук. О, тук. Звъня е умен, и то се случва да забележите, че аз заявих на масива 5 елемента но аз съм индексиране в позиция 1000. Той може да направи това, защото това са само константи. Той може само да отиде толкова далеч, забележи, че аз отивам извън границите на масива. Но забележете преди, когато ние трябваше да бъда неправилно, той не може да се определи колко стойности може да поеме, така че не може да определи, че щях след края на масива. Това е просто звъня е умен. Но сега buggy4. Така че, какво съм аз греша? Мълчаливо обявяване библиотека функция "ФОРМАТ". Отивам да искате да # включват . Добре. Сега работи buggy4. Отпечатване на стойността на масива, както направих аз тук, да го отпечатате като показалка разпечатки нещо, което прилича на това - bfb8805c - което е с около адрес това е в стак-Иш регион. Array само по себе си е като показалеца, но това не е действителен показалеца, тъй като редовно показалеца можем да променим. Array е просто някакъв постоянен. 20 блокове памет започват адрес 0xbfb8805c. Толкова bfb8805c чрез този адрес +20- или аз предполагам, -20 - на паметта, разпределени за този масив. Array, самата променлива не се съхранява никъде. Когато сте съставянето, компилаторът ръка вълна - но компилатора просто ще използвате, когато го знае масив да бъде. Той знае къде започва този масив, и така винаги може да просто правя нещата по отношение на компенсации от това начало. Тя не се нуждае самата променлива да представлява масив. Но когато правя нещо подобно Int * р = масив, сега стр. е указател, който сочи към този масив, и сега стр. всъщност не съществува на стека. Аз съм безплатно да променят стр.. Мога да направя р = изчистване. Така че първоначално посочи в масив, а сега сочи малко пространство на куп. Не мога да направя масив = изчистване. Ако е умен звъня, той ще крещи по мен правото на разстояние бухалката. Всъщност, аз съм сигурен, че GCC ще направите това също. Така масив тип "Int [5]" не е отнесен. Вие не може да възлага нещо за типа масив защото масивът е просто постоянно. Това е символ, който референции тези 20 байта. Не мога да го променя. [Ученик], където се съхраняват размера на масива? [Bowden] не се съхранява никъде. Това е, когато е съставянето. Е, къде е размерът на масива съхранява? Можете да използвате само sizeof (масив), в рамките на функцията, която масива се обявява. Така че, ако го направя някаква функция, Foo, и аз правя (INT масив []) ФОРМАТ ("% г \ N", sizeof (масив)); и след това тук аз наричам Foo (масив); в рамките на тази функция - да го стартирате. Това е звъня умен отново. Той ми казва, че sizeof параметър масив функция ще се върне размера на "INT *". Това би било грешка, ако то не е това, което исках да се случи. Да изключите Werror. Внимание. Предупрежденията са добре. Той все още ще съставят толкова дълго, тъй като има предупреждение. / A.out ще да отпечатате 4. Предупреждението, че е генериран е ясен показател за това какво се е объркало. Това INT масив е просто да отпечатате sizeof (INT *). Дори ако сложа на масив [5] тук, е все още само ще да отпечатате sizeof (INT *). Така че веднага щом го премине във функция, разликата между масиви и указатели е несъществуваща. Това се случва да бъде масив, който е обявен в стека, но веднага след като минаваме покрай тази стойност, че 0xbf дрън, дрън, дрън в тази функция, този указател сочи към този масив на стека. Така че това означава, че sizeof се прилага само във функцията, че масивът е обявена, което означава, че когато съставянето на тази функция, , когато звъня минава през тази функция, той вижда масив е INT масив с размер 5. Тогава го вижда sizeof (масив). Е, това е 20. Това е всъщност как sizeof основно работи за почти всички случаи. Sizeof не е функция, той е оператор. Не наричаш sizeof функция. Sizeof (INT), компилаторът ще просто да преобразува 4. Ясно ли е? Добре. [Ученик] Така че каква е разликата между sizeof (масив) в основния и в Foo? Това е така, защото ние казваш sizeof (масив), която е от тип Int *, като има предвид, че масива тук не е * тип Int, това е едно цяло число масив. [Ученик] Така че, ако сте имали параметър масив [] вместо INT масив *, това означава, че вие ​​все още може да промени масив, защото сега тя е указател? Bowden] Харесвам това? >> Студент Да. Може да промените масив в рамките на функцията сега? [Bowden] може да се промени масив в двата случая. В двата случая, вие сте свободни да се каже, масив [4] = 0. [Ученик] Но можете ли да направите точка масив с нещо друго? [Bowden О Да. И в двата случая - >> [ученик] Да. Bowden] Разликата между масив [] и INT масив *, няма никой. Можете да получите също и някои многомерен масив тук за някои удобен синтаксис, но това е все още само на показалеца. Това означава, че аз съм свободен да правя масив = изчистване (sizeof (INT)), а сега се отбележи някъде другаде. Но точно като как работи вечно и винаги, промяна на този масив, като го сочат към нещо друго не променя този масив тук, защото това е копие на аргумента, това не е показалеца на този довод. И всъщност, точно както повече индикации, че това е точно същото - ние вече видяхме какви разпечатки печат масив - какво ще стане, ако се печата адреса на масива или адреса на адреса на масива Единия от тези? Да се ​​игнорира това. Добре. Това е добре. В момента текат. / A.out. Печат масив, след което отпечатване на адреса на масива, са едно и също нещо. Array просто не съществува. Той знае, когато сте отпечатване на масив, отпечатване на символа, който се отнася до тези 20 байта. Отпечатване на адреса на масива, добре, масив не съществува. То не трябва адрес, така че просто отпечатва адрес на тези 20 байта. Веднага след като вие компилирате, като в компилирана buggy4 / a.out, масив е несъществуваща. Указатели съществуват. Масивите не. Блокове памет, представляващи масив все още съществуват, но променлива масив и променливи от този тип не съществуват. Тези, които са като основните разлики между масиви и указатели Веднага след като направите извиквания на функции, няма разлика. Но вътре на функцията, която е обявена за самия масив, sizeof работи по различен начин тъй като сте отпечатване на размера на блоковете, а не на размера на типа, и не можете да го промените, защото то е символ. Отпечатване на нещо и адреса на нещо отпечатва едно и също нещо. И това е доста значително. [Ученик] Може ли да се каже, че още един път? Може да съм пропуснал нещо. Печат масив и адрес на масива отпечатва едно и също нещо, като има предвид, че ако печатате показалеца спрямо адреса на показалеца, единственото нещо, което отпечатва адрес на това, което сочи, отпечатва адрес на показалеца на стека. Можете да промените показалеца, не можете да променяте символ масив. И sizeof показалецът се ще да отпечатате размер на този тип показалеца. Така Int * р sizeof (п) ще да отпечатате 4, но Int масив [5] печат sizeof (масив) ще да отпечатате 20. [Ученик] Така вътр масив [5] ще отпечата 20? >> Да. Ето защо вътре в buggy4, когато той да бъде използван sizeof (масив) това правех <20, което не е това, което искахме. Искаме I <5. >> Студент Добре. [Bowden И тогава, както и веднага след като започнете преминаване на функциите, ако сме направили Int * р = масив; вътрешността на тази функция, ние можем да основно да използвате стр. и масив точно по същия начин, изключение на проблема sizeof и промяна проблем. Но [0] = 1; е същото като каза масив [0] = 1; И веднага след като казваме, Foo (масив), или Foo (п); вътрешността на функцията Foo, това е два пъти същия призив. Няма разлика между тези две повиквания. Всеки добър по този въпрос? Добре. Имаме 10 минути. Ние ще се опитаме да получите чрез тази програма Hacker Typer този сайт, който излезе миналата година или нещо такова. Това е просто трябваше да бъда като теб въведете произволно и отпечатва Каквото файл, се случва да са натоварени, е това, което изглежда, че въвеждате. Тя изглежда като някакъв вид на операционната система код. Това е, което искат да приложат. Трябва да има бинарен изпълним име hacker_typer , който отвежда в един-единствен аргумент, файлът "хакер тип". Изпълнението на изпълним, трябва да изчистите екрана и след това да отпечатате един символ от изминалата във файл всеки път, когато потребителят натисне ключ. Така че каквото и ключ натиснете, трябва да изхвърляме и вместо да отпечатате герой от файла това е аргумент. Аз ще ви кажа доста неща, за които ще трябва да знаете. Но ние искаме да се провери в библиотеката termios. Никога не съм използвал тази библиотека през целия си живот, така че има много минимални цели. Но това ще бъде библиотеката можем да използваме, за да изхвърлите характера ви удари когато пишете в стандарт инча Така hacker_typer.c, и ние ще искате да # включват . Търси в мъжа страница за termios - Аз съм се познае, че е крайно OS или нещо - Аз не знам как да го прочетете. Това се казва да се включат тези два файла, така че ние ще направим това. Първото нещо, което на първо място, искаме да се вземат в един аргумент, който е файл, който трябва да отвори. Така че това, което искам да направя? Как мога да проверя, за да видиш, че има един-единствен аргумент? [Ученик] Ако argc равни. >> Bowden Да. Така че, ако (argc = 2!) ФОРМАТ ("Използване:% [файл, за да отворите]"). Така че сега, ако аз тичам без да предоставя на втория аргумент - О, имам нужда от нов ред - ще видите, че казва използване. / hacker_typer, и след това на втория аргумент трябва да бъде файла, който искате да отворите. Сега какво да правя? Искам да се чете от този файл. Как да се чете от файл? [Ученик] отвори първият. >> Да. Така fopen. Какво fopen изглежда? [Ученик] Име. >> Bowden] Filename ще бъде argv [1]. [Ученик] И тогава това, което искате да правите с него, така че - >> [Bowden Да. Така че, ако не си спомня, просто може да направи fopen човек, , където ще бъде Конст Чар път *, където пътят е името на файла, Конст Чар режим *. Ако ви се случи да не си спомня какво е режим, а след това можете да погледнете за режим. Вътре от синтетични страници, характер черта е това, което можете да използвате, за да търсят неща. Така че аз вид / режим, за да търсите режим. N и N са това, което можете да използвате, за да преминете през мачовете от търсенето. Тук се казва аргумент режим, за да низ започва с един от следните последователности. Така R, Open текстов файл за четене. Това е, което искаме да направим. За четене, и аз искам да се съхранява. Това, което ще бъде файл *. Сега какво искам да направя? Дай ми секунда. Добре. Сега какво искам да направя? [Ученик], ако това е NULL. >> Bowden Да. Всеки път, когато отворите файл, уверете се, че успешно сте в състояние да го отворите. Сега искам да направя това termios неща там, където искам първо да прочетете моите текущите настройки и спести тези в нещо, после искам да променя настройките да изхвърляме всеки символ, който пиша, и после искам да актуализира тези настройки. И тогава в края на програмата, искам да се върнете към първоначалните си настройки. Така че структурата ще бъде от тип termios, и аз отивам да искат два от тях. Първият от тях ще да ми current_settings, и след това те ще да ми hacker_settings. Първо, аз ще искам да запазя текущите настройки, тогава аз ще искате да актуализирате hacker_settings и след това в края на моята програма, аз искам да се върне към текущите настройки. Така спасяването на текущите настройки, начина, по който работи, Ние човек termios. Ние виждаме, че имаме този INT tcsetattr, вътр tcgetattr. Минавам с termios структура от неговия показалеца. Начинът, по който това ще изглежда - I've вече забравили какво е била извикана. Го копирате и поставите. Така tcgetattr, тогава искам да премине в структурата, че аз съм спестяване на информацията в която ще бъде current_settings, и първият аргумент е файлов дескриптор за нещо, което искате да запишете атрибутите на. Файлов дескриптор е като всеки път, когато отворите файл, той получава файлов дескриптор. Когато fopen argv [1], той получава файлов дескриптор, който сте съотнасяне всеки път, когато искате да прочетете или да го пишете. Това не е файлов дескриптор Искам да използвам тук. Има три файлови дескриптори, които имат по подразбиране, , които са стандарт в стандартния изход и стандартна грешка. По подразбиране, мисля, че е стандарт в е 0, стандарт от 1 и стандартната грешка е 2. Така че това, което искам да променя настройките на? Искам да променя настройките на всеки път, когато удари характер, Искам да хвърлят този характер, вместо да ги извежда на екрана. Какво поток - стандарт, стандарт, или стандартна грешка - отговаря на неща, когато пишете на клавиатурата? >> [Ученик] Серийно инча >> Да. Така че може да направи 0 или мога да направя стандартния вход. Аз съм се current_settings на стандартен инча Сега искам да актуализира тези настройки, така че първо ще копирате в hacker_settings какво ми current_settings. И как structs работа е, че просто ще копирате. Това копира всички полета, както бихте очаквали. Сега искам да се актуализират някои от полетата. Търсите на termios, вие ще трябва да прочетете много от това само за да видя какво вие ще искате да търсите, но знамена, ти започваш да искате да погледнете за ехо, така ECHO входни символи Echo. Първо искам да настроите - I've вече забравил какво полета са. Това е, което структура прилича. Така че режимите за въвеждане Мисля, че ние искаме да променим. Ще разгледаме решение, за да се уверите, това е, което искаме да променим. Ние искаме да променим lflag за да се предотврати е необходимо да се погледне през всички тези. Ние искаме да променим местните режими. Може би трябва да прочетете цялото това нещо да се разбере, където всичко принадлежи че искаме да променим. Но това е в рамките на местните режими, където отиваме да искат да променят това. Така hacker_settings.cc_lmode е това, което се нарича. c_lflag. Това е мястото, където да навлезем в побитовите оператори. Ние си вид от времето, но ние ще отидем през него бързо. Това е мястото, където да навлезем в побитовите оператори, , където мисля, че едно и също време отдавна каза, че винаги, когато започнете да се занимават със знамена, ти започваш да се използва побитови оператора много. Всеки бит в знамето съответства на някакъв вид поведение. Така че тук, на този флаг има един куп различни неща, където всички от тях означава нещо различно. Но това, което искате да направите е да изключите малко, което съответства на ECHO. Така че, за да се превърне, че на разстояние правя и = ¬ ECHO. Всъщност мисля, че това е като Течо или нещо. Аз съм просто ще да се провери отново. Мога да го termios. Това е просто ехо. Хуманитарна помощ "ще бъде едно малко. ¬ Хуманитарна помощ "ще означава всички битове са 1, което означава, че всички флагове да е истина с изключение на за ECHO малко. , Като сложи край на моите местни знамена с това, това означава всички знамена, които са в момента, да е истина ще продължи да се определя да е истина. Ако ми ECHO флаг е установен да е истина, тогава това е задължително да лъжа на ГД "Хуманитарна помощ" знаме. Така че тази линия на код просто изключва ECHO флаг. Реда код, просто ще ги копирате в интерес на времето и след това да ги обясни. В разтвора, каза той на 0. Това е може би по-добре изрично да се каже, стандартния вход. Забележете, че аз също съм ECHO | посочва тук. Посочва се отнася до нещо отделно, което означава, каноничен режим. Какво каноничен режим средство е обикновено, когато пишете на командния ред, стандарт в не обработва нищо, докато не удари нов ред. Така че, когато правиш GetString, въведете куп неща, тогава ви удари нов ред. Това е, когато е изпратено на стандарта инча Това е по подразбиране. Когато изключите каноничен режим, сега всеки един символ, натиснете е това, което се обработват, което обикновено е вид лошо, защото това е бавен да обработват тези неща, Ето защо е добре да го буфер в цели линии. Но аз искам всеки един от героите, за да бъдат обработени тъй като аз не искам тя да ме чака да се появи нов ред преди да преработва всички герои съм пишете. Това изключва каноничен режим. Тези неща просто означава, че когато всъщност обработва герои. Това означава, че ги обработват незабавно, веднага след като съм ги пишете, да ги обработват. И това е функция, която се актуализиране на моите настройки за стандарт в и TCSA средства да го направя точно сега. Другите варианти са изчакате, докато всичко, което в момента е на потока се обработват. Това всъщност няма значение. Точно сега променя настройките, за да бъде, каквото е в момента hacker_typer_settings. Предполагам, че го нарече hacker_settings, така че нека променим това. Промяна всичко hacker_settings. Сега, в края на нашата програма ние ще искате да се върнете на това, което в момента е в рамките на normal_settings, която ще просто изглежда и normal_settings. Забележете, не са променяни по всяко от моите normal_settings, тъй като първоначално го. Тогава просто да си ги върнете, аз ги да премине отново в края. Това е актуализация. Добре. Сега вътре от тук просто ще обясни код в интерес на време. Това не е толкова много код. Ние виждаме, четем символ от файла. Нарекохме го е. Сега можете човек fgetc, но как fgetc ще работи е само, че ще се върне знака, който просто чете или EOF, която съответства на края на файла или някои се случва грешка. Ние сме примка, продължава да чете един символ от файла, докато не изтече от знаци, за да се чете. И докато правим това, ще чакаме по един символ от стандартния инча Всеки път, когато напишете нещо в командния ред, четене в герой от стандарт инча Тогава putchar е просто ще постави Чар четем от файла на стандартния изход. Може ли човек да putchar, но това е само въвеждането на стандарта, това е печат на този характер. Вие също може да направи ФОРМАТ ("в", в), същата идея. Това ще направи по-голямата част от нашата работа. Последното нещо, което започваш да искате да направите, е просто неуспешно файл. Ако не неуспешно, това е изтичане на памет. Ние искаме да неуспешно първоначално отвори файла, и аз мисля, че това е. Ако направим това, аз вече имам проблеми. Да видим. Какво се оплаквате? Вместо "INT", но аргумент е от тип "структура _IO_FILE * '. Ще видим дали това работи. Разрешено само в c99. Augh. Добре, hacker_typer. Сега имаме по-полезни описания. Така че използването на недекларирания идентификатор "normal_settings". Не съм го наричат ​​normal_settings. Го наричат ​​current_settings. Така че нека да променим всичко това. Сега минава аргумент. Ще направя този 0 за сега. Добре. / Hacker_typer cp.c. Аз също не изчистите екрана в началото. Но вие можете да погледнем назад към последния набор проблем, за да видите как можете да изчистите екрана. Това е просто отпечатване на някои символи докато това се прави това, което искам да направя. Добре. И да мисля за това, защо това е необходимо да бъде 0 вместо от стандартния вход, които трябва да бъдат # определят 0, това се оплаква, че Преди, когато казах, че има описания на файлове, но тогава вие също имате * FILE, файлов дескриптор е просто едно число, като има предвид, че FILE * има цял куп неща, свързани с нея. Причината, поради която трябва да се каже, 0 вместо от стандартния вход е, че стандартния вход е файл *, която сочи към това, което е файлов дескриптор по 0. Така че дори и тук, когато правя fopen (argv [1], аз съм се FILE *. Но някъде в тази FILE * е нещо, съответстваща на файлов дескриптор за този файл. Ако погледнете към мъжа страница отворен, така че аз мисля, че ще трябва да направи човек 3 - няма - мъж две отворени - да. Ако се вгледате в страницата за открит, отворен е като по-ниско ниво fopen и това е връщане действителната файлов дескриптор. fopen прави куп неща на върха на отворен които вместо да се завърне точно това файлов дескриптор връща цялата FILE * показалеца вътрешността на която е нашата малка ЕВРОВОК файл. Така стандарт се отнася до нещо, FILE *, като има предвид, че 0 се отнася само стандартен файлов дескриптор в себе си. Въпроси? [Смее се] Издуха през това. Добре. Свършихме. [Смее се] [CS50.TV]