[Powered by Google Translate] [Раздзел 5 - больш камфортнай] [Rob Боуден - Гарвардскі універсітэт] [Гэта CS50. - CS50.TV] Як я сказаў у маёй электроннай пошце, ёсць шмат рэчаў, якія вы можаце выкарыстоўваць акрамя прыбор на самай справе праблема мностваў. Мы рэкамендуем вам зрабіць гэта ў прыбор толькі таму, што тады мы можам больш лёгка дапамагчы вам і мы ведаем, як усё будзе працаваць. Але, як адзін з прыкладаў, дзе вы можаце рабіць рэчы, калі, скажам, вы не маеце доступу з прыборам ці вы хочаце працаваць у падвале цэнтра навукі - які на самай справе ў іх ёсць прыбор занадта - калі вы хочаце працаваць у любым месцы. Адзін прыклад вы бачылі / чулі пра SSH? SSH ў асноўным так жа, як падключыцца да чагосьці. На самай справе, цяпер я SSHed ў прыбор. Я ніколі не працую непасрэдна ў прыладу. Вось прыбор, і калі вы паглядзіце сюды, вы бачыце гэты IP-адрас. Я ніколі не працую ў сам прыбор; Я заўсёды прыходзяць да iTerm2 вокны / акна тэрмінала. Вы можаце SSH да гэтага IP-адрасу, SSH jharvard@192.168.129.128. Я памятаю, што нумар вельмі лёгка, таму што ён такі добры шаблон. Але што будзе прасіць мяне за мой пароль, і цяпер я знаходжуся ў прыбор. У прынцыпе, на дадзены момант, калі вы адкрылі тэрмінал ўнутры самога прыбора, Гэты інтэрфейс, аднак вы павінны выкарыстоўваць яго, сапраўды гэтак жа, ў якасці інтэрфейсу я выкарыстоўваю тут, але цяпер вы SSHed. Вы не павінны SSH да прылады. Адным з прыкладаў іншым месцы Вы маглі SSH на тым, што я ўпэўнены, што ў вас ёсць па змаўчанні - Ох. Больш за. Усе вы павінны быць па змаўчанні ФАС рахунку на серверах ФАС. Для мяне, я б SSH да rbowden@nice.fas.harvard.edu. Ён збіраўся спытаць вас, што ў першы раз, і вы кажаце, так. Мой пароль проста будзе маім ФАС пароль. І вось цяпер, я SSHed у добрыя серверы, і я магу рабіць усё, што хачу тут. Шмат класаў вы можаце ўзяць, напрыклад, 124, будзеце мець вы загружаеце рэчы, каб тут на самай справе прадставіць свае наборы праблема. Але кажуць, што Вы не маеце доступу да прылады. Тады вы можаце рабіць рэчы, як тут ён будзе казаць - Гэта проста наш профіль пытанняў. Ён папросіць вас зрабіць гэта ў прыбор. Замест гэтага я проста зрабіць гэта на серверы. Я хачу, каб распакаваць гэта. Праблема будзе, што вы прывыклі выкарыстоўваць нешта накшталт Gedit ці як там унутры прыбора. Вы не будзеце мець аб тым, што ФАС сервера. Гэта ўсё проста будзе гэта тэкставы інтэрфейс. Такім чынам, вы можаце альбо адзін, паспрабаваць даведацца, тэкставы рэдактар, які ў іх ёсць. Яны маюць Nano. Nano, як правіла, вельмі простая ў выкарыстанні. Вы можаце выкарыстоўваць стрэлкі і ўвядзіце нармальна. Так што не цяжка. Калі вы хочаце атрымаць сапраўды фантазіі вы можаце выкарыстоўваць Emacs, які я, верагодна, не павінны былі адкрыты, таму што я нават не ведаю, як зачыніць Emacs. Упраўленне 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 ў любым выпадку. Так што не павінна быць, што вялікая частка абмежаванняў. А вось што. Я вярнуся да прыбора зараз, мы зробім усё, што ў прыборы. Гледзячы на ​​нашы пытанні часткі, у пачатку, як я сказаў у маёй электроннай пошце, Мы павінны гаварыць аб адным кароткім вы павінны былі глядзець. У нас ёсць Redirecting і трубы, і гэтыя тры пытанні. На што струмень у функцый, такіх як Printf напісаць па змаўчанні? Такім чынам, паток. Што такое плынь? Струмень у асноўным, як гэта толькі некаторыя - Справа нават не ў крыніцы 1 і 0. Паток яна просіць Тут стандартны вывад. І так з стандартных ўяўляе сабой паток, што, калі вы пішаце на яго, ён з'яўляецца на экране. Стандартны выхад, па струмені, гэта азначае, што вы проста напісаць 1 і 0 да яго, , А другі канец стандартнага з проста чытае дадзеныя з патоку. Гэта проста радок 1 і 0. Вы можаце напісаць струменяў або вы можаце прачытаць з патокаў у залежнасці ад таго, што струмень на самай справе. Два іншых патокаў па змаўчанні з'яўляюцца стандартнымі і памылак. Стандарт у гэта кожны раз, калі вы GetString, ён чакае цябе, каб увесці матэрыял. Так што чакаем вас, гэта на самай справе чакання на стандарт, які на самай справе тое, што вы атрымліваеце, калі вы набіраеце на клавіятуры. Вы ўводзіце ў стандартных цалі Стандартная памылка ў асноўным эквівалентная стандартны вывад, але гэта спецыялізаваны у тым, што пры друку на стандартнай памылкі, Вы, як мяркуецца друкаваць толькі паведамленні пра памылкі, што так што вы можаце адрозніваць звычайныя паведамлення выводзяцца на экран у параўнанні з паведамленнямі пра памылкі ў залежнасці ад таго яны пайшлі з стандартных або стандартную памылку. Файлы таксама. Стандартны выхад, стандартны У, і стандартная памылка толькі спецыяльныя патокі, але на самой справе любы файл, пры адкрыцці файла, ён становіцца паток байтаў дзе вы можаце проста прачытаць дадзеныя з патоку. Вы, па большай частцы, можна проста думаць пра файл як паток байтаў. Так што патокі пішуць, каб па змаўчанні? Стандартны выхад. У чым розніца паміж> і >>? Хто-небудзь глядзець відэа загадзя? Добра. > Будзе, як вы перанакіраваць у файл, і >> таксама збіраецца перанакіраваць выснову ў файл, але ён замест гэтага збіраецца дадаць у файл. Напрыклад, скажам, у мяне, аказваецца, ёсць Dict прама тут, і адзіны матэрыял ўнутры Dict гэта коткі, коткі, сабакі, рыбы, сабакі. Адна каманда, што ў вас у камандным радку кошка, якая толькі збіраецца друкаваць тое, што ў файле. Таму, калі я кажу, кошкі слоўнік, ён збіраецца друкаваць коткі, коткі, сабакі, рыбы, сабакі. Гэта ўсё, што робіць котка. Гэта азначае, што ён надрукаваны на стандартны вывад коткі, коткі, сабакі, рыбы, сабакі. Калі б я замест гэтага хочаце перанакіраваць у файл, я магу выкарыстоўваць> і перанакіраваць яго ў любы файл. Я буду называць файл файлам. Так што цяпер, калі я Ls, я пагляджу, у мяне новы файл з імем файла. І калі б я адкрыць яго, ён будзе мець менавіта тое, што кошка пакласці ў камандным радку. Так што цяпер, калі я зраблю гэта зноў, то гэта будзе перанакіраваць выснову ў файл, і я збіраюся мець сапраўды такі жа рэччу. Такім чынам, тэхнічна, ён цалкам пераадолеў што мы мелі. І мы ўбачым, калі я магу змяніць слоўнік, я ўзяў сабаку. Цяпер, калі мы котку Dict ў файл зноў, мы будзем мець новую версію з сабакам выдаленыя. Так што цалкам адхіляе яго. Замест гэтага, калі мы выкарыстоўваем >>, яна збіраецца дадаць файл. Цяпер, адкрываючы файл, мы бачым, у нас ёсць толькі адно і тое ж надрукаваныя двойчы таму што ён быў там адзін раз, то прыкладаецца да арыгіналу. Дык вось што> і >> зрабіць. Ці ёсць наступны спытаць - Гэта не спытаць пра гэта. Іншая, якая ў нас ёсць, <, якая, калі> перанакіроўвае стандартны вывад, <Збіраецца быць перанакіраваць стандартны цалі Давайце паглядзім, калі мы маем прыклад. Я магу напісаць адну вельмі хутка. Давайце возьмем любы файл, hello.c. Адносна просты файл. Я проста атрымліваю радок, а затым друк "Hello" ўсе радкі Я толькі што ўступіў было. Так што прывітанне і потым. / Прывітанне. Цяпер гэта заахвоціла мяне увесці нешта, якая азначае, што ён чакае ад рэчаў, якія будуць уведзены ў стандартную цалі Так, увядзіце тое, што я хачу ў стандартных цалі Мы проста сказаць Прывітанне, Боб! Тады гэта друк на стандартны вывад Прывітанне, Боб! Калі я гэта зраблю. / Прывітанне, а затым перанакіраваць, на дадзены момант вы можаце толькі перанакіраваць з файла. Так што, калі я паклаў у пэўным файле, TXT, і я паклаў Роб, калі я запускаю прывітанне, а затым перанакіраваць у тэкставы файл. / Добры дзень, гэта збіраўся сказаць Прывітанне, Боб! неадкладна. Калі ён упершыню трапляе ў GetString і ён чакае ад стандарту ў, У стандартную больш не чакае на клавіятуры для дадзеных, каб атрымаць ўвайшоў. Замест гэтага, мы перанакіравалі ў стандартным для чытання з тэкставага файла. І так будзе чытаць з тэкставага файла, які знаходзіцца ўсяго ў лінію Роб, а затым ён збіраецца друкаваць Прывітанне, Боб! І калі б я хацеў, я мог бы таксама зрабіць. / Прывітанне Вы робіце 2>, які перанакіраваньні стандартныя памылкі. Так што, калі нешта пайшло не па стандартнай памылкі, ён так бы і не ўведзены ў txt2. Але звернеце ўвагу, калі я зраблю 2>, то яна па-ранейшаму друку Прывітанне, Боб! у камандным радку таму што я толькі перанакіраваньні стандартныя памылкі, я не перасылак стандартны вывад. Стандартныя памылкі і стандартны вывад розныя. Калі вы хочаце на самай справе пісаць у стандартны струмень памылак, тады я мог змяніць гэта, каб быць Fprintf на стандартны вывад. Так Printf, па змаўчанні, выводзіць на стандартны вывад. Калі я хачу для друку на стандартнай памылкі ўручную, то я павінен выкарыстоўваць Fprintf і паказаць, што я хачу, каб друк. Калі замест гэтага я зрабіў Fprintf стандартны вывад, то гэта ў асноўным эквівалентная Printf. Але Fprintf да стандартнай памылку. Так што цяпер, калі я перанакіраваць гэта ў txt2, Прывітанне, Боб! па-ранейшаму атрымліваць друкуецца ў камандным радку так як яна становіцца друку на стандартнай памылкі, і я толькі перанакіраванне стандартны вывад. Калі я зараз перанакіраваць стандартны памылку, цяпер яна не надрукаваная, і txt2 будзе Прывітанне, Боб! Такім чынам, зараз вы можаце друкаваць вашыя фактычныя памылкі ў стандартную памылку і друкаваць вашыя рэгулярныя паведамленні ў стандартны вывад. І таму, калі вы запусціце праграму, вы можаце запусціць яе. / Прывітанне гэтага тыпу з 2> так што ваша праграма будзе працаваць у нармальным рэжыме, але ніякіх паведамленняў пра памылкі, якія вы атрымліваеце вы можаце праверыць пазней у вашым часопісе памылак, таму памылкі, а затым паглядзім пазней, а вашы памылкі файл будзе мець любыя памылкі, што адбылося. Пытанні? Апошні з'яўляецца труба, якую вы можаце думаць, як прымаць стандартныя з адной каманды і зрабіць яго стандартам у чарговы каманды. Напрыклад тут рэха рэч каманднага радка , Якія толькі збіраюцца паўтарыць тое, што я паклаў у якасці аргументу. Я не буду ставіць двукоссі. Рэха бла, бла, бла, бла толькі збіраецца друкаваць бла, бла, бла, бла. Раней, калі я сказаў, што я павінен быў змясціць Роб ў тэкставы файл таму што я магу толькі перанакіраваць тэкставыя файлы, а / я паўтараю, калі Rob , А затым труба яго ў. / Прывітанне, гэта будзе рабіць тое ж самае роду рэчы. Гэта прымаючы выснову гэтай каманды, рэха Роб, і выкарыстаць яго ў якасці ўваходу для. / прывітанне. Вы можаце думаць пра гэта як першае перанакіраваньне рэха Роб ў файл , А затым ўвядзіце ў. / Прывітанне, што файл, які быў проста выводзіцца. Але гэта патрабуе часовых файлаў з карціны. Пытанняў з гэтай нагоды? Наступнае пытанне, ці збіраецца прыцягнуць гэтага. Што трубаправода вы маглі б выкарыстоўваць, каб знайсці лік унікальных імёнаў у файле пад назвай names.txt? Каманды, якія мы збіраемся хочаце выкарыстоўваць тут з'яўляюцца унікальнымі, так Uniq, а затым туалет. Вы можаце зрабіць чалавек Uniq на самай справе глядзець на тое, што робіць, і ён проста будзе фільтраваць суседніх адпаведныя лініі ад уваходу. І чалавек туалет збіраецца надрукаваць радок, слова і байт для кожнага файла. І апошняе, што мы збіраемся хочаце выкарыстоўваць роду, , Які збіраецца толькі выгляд ліній тэкставы файл. Калі я зраблю некаторыя тэкставы файл, names.txt, і гэта Роб, Томі, Іосіф, Томі, Іосіф, RJ, Роб, што я хачу зрабіць, так гэта знайсці колькасць унікальных імёнаў у гэтым файле. Так што ж адказ? >> [Студэнт] 4. >> Так. Гэта павінна быць 4, так як Роб, Томі, Іосіф, RJ з'яўляюцца толькі унікальныя імёны ў гэтым файле. Першы крок, калі я проста раблю колькасць слоў на names.txt, гэта на самай справе казаў мне ўсё. Гэта на самай справе друк - Давайце паглядзім, мужчына туалет - пераклад радка, слова і байта. Калі б я толькі клапаціцца пра лініях, то я магу проста зрабіць туалет-л names.txt. Так што гэта крок 1. Але я не хачу, каб ЧС-л names.txt, таму што 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, які збіраецца даць вам тэчку пад назвай section5, якая будзе мець усе файлы, якія мы збіраемся выкарыстоўваць сёння ўнутры яго. Як імёны гэтых праграм у файл выказаць здагадку, што яны трохі багі, так што ваша місія, каб высветліць, чаму з дапамогай GDB. Ці ўсё было іх загружаная / ведаеце, як прымусіць іх запампаваць у сваю прыбор? Добра. Запуск ./buggy1, ён будзе казаць Памылка сегментацыі (ядро скідалі), якія ў любы час вы атрымаеце сегментацыі, гэта дрэнна. Пры якіх абставінах вы атрымліваеце сегментацыі? [Студэнт] разнаймення нулявога паказальніка. >> Так. Так што гэта адзін з прыкладаў. Разнаймення нулявога паказальніка вы збіраецеся атрымаць сегментацыі. Што сегментацыі азначае, ты дакранаешся памяці, вы не павінны дакранацца. Так разнаймення нулявога паказальніка датыкаецца з адрасам 0, і ў асноўным, усе кампутары ў цяперашні час сказаць, што адрас 0 памяці ў вас не павінна дакранацца. Дык вось чаму разнаймення нулявога паказальніка ў выніках сегментацыі. Калі вы выпадкова не ініцыялізаваць паказальнік, то яна мае значэнне смецця, і таму, калі вы спрабуеце разнаймення гэта, па ўсёй верагоднасці, ты дакранаешся памяці , Што знаходзіцца ў сярэдзіне нідзе. Калі вам пашчасціла атрымаць пашанцавала, і смецце значэння адбылося паказваюць на дзесьці на стэк або нешта, Затым, калі вы разнаймення, што паказальнік, які вы яшчэ не ініцыялізаваны, нічога не памыліцеся. Але калі гэта паказвае на, скажам, дзесьці паміж стэка і кучы, ці гэта проста паказваючы кудысьці, якія не былі выкарыстаны па вашай праграме не менш, Затым ты дакранаешся памяці ў вас не павінна датычыцца, і вы сегментацыі. Пры напісанні рэкурсіўнай функцыі, і гэта рэкурсіўна занадта шмат разоў і ваш стэк становіцца занадта вялікім і стэк сутыкаецца з рэчамі што яно не павінна быць, якія сутыкаюцца з, ты дакранаешся памяці ў вас не павінна дакранацца, так што вы сегментацыі. Гэта тое, што з'яўляецца сегментацыі. Таксама той жа прычыне, калі ў вас ёсць радок выгляду - Давайце вернемся да папярэдняй праграме. У hello.c--я проста збіраюся зрабіць нешта яшчэ. сімвал * S = "прывітанне свет!"; Калі я выкарыстоўваю * S = нешта або з [0] = 'X'; так што прывітанне,. / прывітанне, чаму гэта сегментацыі? Чаму гэта сегментацыі? Што вы чакаеце далей? Калі б я зрабіў Е ("% S \ п", с); тое, што вы чакалі б для друку? [Студэнт] X прывітанне. >> Так. Праблема ў тым, што, калі вы аб'явіце радок, як гэта, а гэта паказальнік, што збіраецца пайсці ў стэку, і што з паказвае на гэта радок, якая змяшчаецца ў ПЗУ. Так што проста па імені, толькі для чытання памяці, вы павінны атрымаць ідэю што калі вы спрабуеце змяніць тое, што ў ПЗУ, Вы робіце тое, што вы не павінны рабіць з памяццю, і вы сегментацыі. Гэта на самай справе вялікі розніцы паміж сімвал * з і знакаў з []. Такім чынам, сімвал S [], цяпер гэтая радок будзе пакласці ў стэк, і стэк не толькі для чытання, гэта азначае, што гэта павінна працаваць выдатна. І гэта робіць. Памятаеце, што калі я раблю знак * S = "прывітанне свет!", А сама знаходзіцца ў стэку але з пункту куды-небудзь яшчэ, і што дзесьці ў іншым месцы, здараецца, толькі для чытання. Але сімвал S [] толькі тое, у стэку. Так што гэта яшчэ адзін прыклад сегментацыі адбываецца. Мы бачылі, што ./buggy1 ў выніку сегментацыі. У тэорыі, вы не павінны глядзець на buggy1.c неадкладна. Замест гэтага, мы будзем глядзець на яго праз GDB. Звярніце ўвагу, што калі вы атрымліваеце Памылка сегментацыі (ядро скідалі), Вы атрымаеце гэты файл тут называюць ядром. Калі мы LS-L, мы ўбачым, што ядро, як правіла, даволі вялікі файл. Гэты лік байтаў файла, так гэта выглядае, як быццам гэта нешта 250-Кбайт. Прычынай гэтага з'яўляецца тое, што дамп на самай справе , Калі ваш збой праграмы, стан памяці вашай праграмы проста атрымлівае скапіяваць і ўставіць у гэты файл. Ён атрымлівае скідаецца ў файл. Гэтая праграма, у той час як яна была запушчана, адбылося мець памяці каля 250 кілабайт, і вось, што атрымаў скідалі ў гэты файл. Цяпер вы можаце глядзець на гэта файл, калі мы робім GDB buggy1 ядра. Мы можам проста зрабіць GDB buggy1, і гэта будзе проста запусціць GDB рэгулярна, buggy1 выкарыстаннем у якасці ўваходнага файла. Але калі вы робіце GDB buggy1 ядра, то гэта вызначана збіраецца запусціць GDB гледзячы на, што асноўнай файл. А вы кажаце, buggy1 сродкамі GDB ведае, што файл ядра адбываецца ад buggy1 праграмы. Такім чынам, GDB buggy1 асноўных збіраецца неадкладна прынесці нам , Дзе праграма адбылося спыніць. Мы бачым тут праграма завяршаецца з сігналам 11, памылкі сегментацыі. Мы здарыцца ўбачыць лінію зборкі, якая, верагодна, не вельмі карысна. Але калі вы тыпу БТ або трасіроўку, што гэта будзе функцыя , Што дае нам спіс нашых бягучых кадраў стэка. Так трасіроўку. Падобна на тое, у нас ёсць толькі два кадра стэка. Па-першае, гэта наш галоўны фрэйм ​​стэка, а другі фрэйм ​​стэка для гэтай функцыі, што мы апынуліся ў, які выглядае як у нас ёсць толькі зборка код. Так што давайце вернемся да нашай асноўнай функцыі, і зрабіць гэта мы можам зрабіць кадр 1, і я думаю, мы можам таксама зрабіць ўніз, але я амаль ніколі не раблю ўніз - або ўверх. Так. Уверх і ўніз. Да прыносіць вам адзін кадр стэка, уніз прыносіць вам ўніз стэка. Я стараюся ніколі не выкарыстоўваць. Я проста канкрэтна сказаць рамы 1, якая перайсці да кадра з пазнакай 1. Кадр 1 збіраецца прывесці нас у асноўны фрэйм ​​стэка, і ён кажа прама тут радкі кода, мы апынуліся ў. Калі б мы хацелі яшчэ пару радкоў кода, мы можам сказаць спісу, і што збіраецца даць нам ўсе радкі кода вакол яго. Лінія мы segfaulted на было 6: калі (STRCMP ("CS50 скалы", ARGV [1]) == 0). Калі гэта не відавочна, тым не менш, вы можаце атрымаць яго прама адсюль проста думаю, чаму ён segfaulted. Але мы можам зрабіць яшчэ адзін крок наперад і сказаў: "Чаму б ARGV [1] сегментацыі?" Друк Давайце ARGV [1], і, падобна, гэта 0x0, які з'яўляецца нулявым паказальнікам. Мы strcmping CS50 парод і нулявыя, і так, што збіраецца падаць. І чаму ARGV [1] NULL? [Студэнт] Таму што мы не давалі яму любыя аргументы каманднага радка. Так. Мы не даць яму любыя аргументы каманднага радка. Так ./buggy1 будзе толькі ў ARGV [0] ./buggy1. Гэта не будзе мець ARGV [1], так што збіраецца падаць. Але калі, замест гэтага, я раблю толькі CS50, ён скажа Вы атрымліваеце D таму што гэта тое, што ён павінен рабіць. Гледзячы на ​​buggy1.c, ён павінен друкаваць "Вы атрымліваеце D" - Калі ARGV [1] не з'яўляецца "CS50 скалы", "Вы атрымліваеце D", інакш "Вы атрымліваеце!" Таму калі мы хочам, мы павінны гэта параўнаць, як праўда, Гэта азначае, што ён параўноўвае з 0. Так ARGV [1] павінна быць "CS50 камяні". Калі вы хочаце, каб зрабіць гэта ў камандным радку, вы павінны выкарыстоўваць \, каб пазбегнуць прастору. Так CS50 \ скалы і Вы атрымаеце! Калі вы гэтага не зробіце зваротную касую рысу, чаму гэта не працуе? [Студэнт] Гэта дзве розныя аргументы. >> Так. ARGV [1] будзе CS50 і ARGV [2] будзе пародах. Добра. Цяпер ./buggy2 збіраецца сегментацыі зноў. Замест таго, каб адкрыць яго з асноўнай файл, мы проста адкрыць buggy2 наўпрост, так што GDB buggy2. Цяпер, калі мы проста запусціць нашу праграму, то яна скажа Праграма атрымала сігнал SIGSEGV, якая з'яўляецца сігналу сегментацыі, і гэта, дзе гэта адбылося здарыцца. Гледзячы на ​​наш след, мы бачым, што мы былі ў функцыі oh_no, якая была выкліканая функцыя хупавы, якая была выкліканая функцыя Бинки, які атрымаў назву па асноўным. Мы таксама бачым, аргументы гэтых функцый. Аргумент хупавы і Бинки была 1. Калі мы пералічым функцыі oh_no, мы бачым, што oh_no проста робіць знак ** ы = NULL; * S = "БУМ"; Чаму гэта не атрымаецца? [Студэнт] Вы не можаце разыменовать нулявы паказальнік? >> Так. Гэта проста кажу ёй гэта NULL, незалежна ад таго, што здараецца, сімвал **, якія, у залежнасці ад таго, як інтэрпрэтаваць яго, гэта можа быць паказальнікам на паказальнік на радок або масіў радкоў. Гэта з пустой, так * з у разнаймення нулявога паказальніка, і так гэта будзе крах. Гэта адзін з самых хуткіх спосабаў магчыма, вы можаце падаць. Гэта проста абвясціўшы нулявы паказальнік і адразу ж памылку сегментацыі. Вось што oh_no робіць. Калі мы пойдзем на адзін кадр, то мы збіраемся трапіць у функцыю, якая завецца oh_no. Мне трэба зрабіць, што ўніз. Калі вы не ўвядзеце каманду, і вы проста націсніце Enter зноў, гэта будзе проста паўтарыць папярэднюю каманду, якая пабегла. Мы знаходзімся ў кадры 1. Аб'ява гэтай сістэме, мы бачым тут наша функцыя. Вы можаце націснуць спіс яшчэ раз, ці вы можаце зрабіць спіс 20 і яна будзе ўтрымліваць больш. Функцыя хупавы кажа, што калі я роўная 1, а затым перайсці да функцыі oh_no, яшчэ пайсці ў аблягаў функцыі. І мы ведаем, я = 1, таму што мы здарыцца ўбачыць тут што хупавы быў выкліканы з аргументам 1. Ці вы можаце проста раздрукаваць я, і яна будзе сказаць, што я 1. У цяперашні час мы хупавы, і калі мы пойдзем іншы кадр, мы ведаем, што ў канчатковым выніку ў Бинки. Up. Цяпер мы знаходзімся ў Бинки. Аб'ява гэтай функцыі - спіс да паловы абарваў мяне - Усё пачалося, як калі б я роўны 0, то мы будзем называць яго oh_no, інакш называюць хупавы. Мы ведаем, што я быў 1, так званы хупавы. А зараз мы вернемся ў асноўны, а галоўнае проста будзе Int я = RAND ()% 3; Гэта проста збіраюся даць вам выпадковы лік, якое роўна 0, 1 або 2. Ён збіраўся патэлефанаваць Бинки з гэтым нумарам, і ён будзе вяртаць 0. Гледзячы на ​​гэта, проста хадзіць праз праграму ўручную, не запускаючы яе адразу, вы павінны ўсталяваць кропкі супыну на асноўнай, а гэта значыць, што, калі мы запусцілі праграму ваша праграма працуе, пакуль яна трапляе ў кропку супыну. Такім чынам, запусціўшы праграму, яна будзе працаваць, а затым ён патрапіць у асноўную функцыю і спыніцца. Цяпер мы ўнутры асноўнага, і крок або наступнай збіраецца прывесці нас да наступнай радку кода. Вы можаце зрабіць крок або на наступны. Націск наступны, цяпер я быў усталяваны ў RAND ()% 3, так што мы можам надрукаваць значэнне я, і яно будзе сказаць, што я 1. Цяпер гэта мае значэнне ці будзем мы выкарыстоўваць наступную або крок. Я думаю, гэта не мела значэння ў папярэднім, але мы хацелі б выкарыстоўваць наступны. Калі мы выкарыстоўваем крок, мы ўступаем у функцыю, якая азначае, паглядзім на фактычныя рэчы што адбываецца ўнутры Бинки. Калі мы выкарыстоўваем наступны, то гэта азначае пераход функцыі і проста перайсці да наступнай радку кода ў нашай асноўнай функцыяй. Прама тут, на гэтай лініі, я быў у якім ён сказаў RAND ()% 3; калі б я зрабіў крок, ён будзе ісці ў рэалізацыю рандаў і паглядзіце, што там адбываецца, і я мог ступіць праз функцыю рандаў. Але я не клапачуся аб функцыі рандаў. Я проста хачу, каб перайсці да наступнай радку кода ў асноўны, таму я выкарыстоўваю наступны. Але цяпер я клапачуся аб Бинки функцыі, таму я хачу, каб увайсці ў гэта. Зараз я знаходжуся ў Бинки. Першы радок кода будзе казаць тады, калі (я == 0), я раблю крок, мы бачым, мы ў канчатковым выніку на хупавы. Калі мы рэчы спісу, мы бачым, што ён праверыў гэта я = 0. Я не роўная 0, таму ён пайшоў у іншы стан, які будзем называць хупавы (I). Вы можаце заблытацца. Калі вы толькі паглядзіце на гэтых ліній непасрэдна, можна падумаць, калі (я == 0), Добра, тады я зрабіў крок, і цяпер я ў хупавы (I), Вы думаеце, што павінна азначаць = 0 або нешта. Не, гэта проста азначае, што ён ведае, што можа прытрымлівацца прама на лініі хупавы (I). Таму што я не роўны 0, то наступны крок не збіраецца заканчвацца ў іншым. Астатняе не з'яўляецца прамой ён збіраецца спыніцца. Гэта проста будзе ісці ў наступным радку ён можа рэальна выканаць, якая з'яўляецца хупавы (I). Увайшоўшы ў хупавы (I), мы бачым, калі (I == 1). Мы ведаем, г = 1, так што, калі мы робім крок, мы ведаем, што мы збіраемся ў канчатковым выніку ў oh_no таму што я = 1 выклікае функцыю oh_no, які вы можаце ўступіць, які будзе ўсталяваны знак ** S = NULL, каб і адразу "БУМ". І тады на самай справе, гледзячы на ​​ажыццяўленне buggy2, гэта, я проста атрымліваю выпадковыя ліку - 0, 1 або 2 - пакліканне Бинки, , Які, калі я гэта 0 яно выклікае oh_no, інакш ён называе хупавы, якая прыходзіць сюды. Калі я 1, выклік oh_no, інакш называюць абліпальныя, якія прыдумляюць тут, калі я на 2, патэлефануеце oh_no. Я нават не думаю, што ёсць спосаб - Хто-небудзь гл. спосаб зрабіць гэта праграмы, якія не будуць падаць? Таму што калі я нешта адсутнічае, калі я 0, вы адразу сегментацыі, яшчэ вы ідзяце ў функцыі якога, калі я гэта 1 Вы сегментацыі, яшчэ вы ідзяце ў функцыі якой, калі я гэта 2 вы сегментацыі. Так што не мае значэння, што вы робіце, вы сегментацыі. Я думаю, адзін з спосабаў фіксацыі было б замест таго, каб рабіць знак ** ы = NULL, Вы можаце Malloc прасторы для гэтага радка. Мы маглі б зрабіць таНос (SizeOf) - SizeOf што? [Студэнт] (сімвал) * 5? >> Ці значыць гэта, здаецца, ці не так? Я мяркую, што гэта будзе працаваць, калі я на самай справе запусціў яго, але гэта не тое, што я шукаю. Паглядзіце на тып с. Давайце дадамо Int *, так Int * х. Я хацеў бы зрабіць таНос (SizeOf (INT)). Або калі б я хацеў масіў з 5, я хацеў бы зрабіць (SizeOf (INT) * 5); Што рабіць, калі ў мяне ёсць Int **? Што б я таНос? [Студэнт] Памер паказальніка. >> Так. (SizeOf (INT *)); Тое ж самае і тут. Я жадаю (SizeOf (Char *)); Гэта будзе вылучыць месца для паказальніка, які паказвае на «БУМ». Мне не трэба, каб вылучыць прастора для «БУМ» сам таму што гэта ў асноўным эквівалентныя таго, што я казаў раней з сімвалаў * х = "БУМ". "БУМ" ужо існуе. Гэта адбываецца існаваць у даступным толькі для чытання вобласць памяці. Але ён ужо існуе, а значыць гэты радок кода, калі ёй гэта сімвал **, Затым * S з'яўляецца знак * і вы ўсталёўваеце гэты знак *, каб паказаць на "БУМ". Калі б я хацеў, каб скапіяваць "БУМ" у с, то я павінен быў бы вылучыць месца для с. Я зраблю * S = таНос (SizeOf (Char) * 5); Чаму 5? Чаму не 4? Падобна на тое, «БУМ» складае 4 знакаў. >> [Студэнт] нулявой сімвал. Так. Усе вашыя радкі будуць мець патрэбу нулявой сімвал. Цяпер я магу зрабіць нешта накшталт Strcat - Якая функцыя для капіявання радкі? [Студэнт] КПЮ? >> ЗЬгсру. Чалавек зЬгсру. Так зЬгсру або зЪгпсру. зЪгпсру трохі бяспечней, паколькі вы можаце дакладна ўказаць, колькі знакаў, але тут гэта не мае значэння, таму што мы ведаем. Так зЬгсру і паглядзець у аргументах. Першым аргументам з'яўляецца наша прызначэння. Другім аргументам з'яўляецца нашым крыніцай. Мы збіраемся капіяваць у нашай прызначэння * з паказальнікам «БУМ». Чаму можа вы хочаце зрабіць гэта з зЬгсру, а не толькі тое, што было раней з * S = "БУМ"? Існуе прычына, вы можаце зрабіць гэта, але што гэта за прычына? [Студэнт] Калі вы хочаце нешта змяніць у "БУМ". >> Так. Цяпер я магу зрабіць нешта накшталт S [0] = 'X'; таму што з пункту да кучы, і што месца на кучы, а паказвае на з'яўляецца паказальнікам на большае прастору ў кучы, якая захоўвае "БУМ". Так што гэта копія «БУМ» у цяперашні час захоўваецца ў кучы. Ёсць тэхнічна дзве копіі "БУМ" у нашай праграме. Там у першы той, які толькі што даў гэтым «БУМ» радковыя канстанту, і другі асобнік «БУМ», зЬгсру стварыў копію "БУМ". Але копія «БУМ» у цяперашні час захоўваецца ў кучы, і куча вы вольныя змяніць. Куча не толькі для чытання, так што азначае, што з [0] збіраецца дазволіць вам змяніць значэнне "БУМ". Гэта будзе дазваляюць змяняць гэтыя сімвалы. Пытанні? Добра. Пераходзячы да buggy3, давайце GDB buggy3. Мы проста запусціце яго і мы бачым, мы атрымліваем сегментацыі. Калі мы трасіроўку, ёсць толькі дзве функцыі. Калі мы пойдзем у наш асноўнай функцыі, мы бачым, што segfaulted на гэтай лініі. Так што, гледзячы на ​​гэтую лінію, для (INT лініі = 0; ЕдеЬз гэты матэрыял не роўна NULL; лінія + +). Наш папярэдні кадр быў названы _IO_fgets. Вы ўбачыце, што многія з убудаванымі функцыямі C, , Што, калі вы атрымліваеце сегментацыі, будзе сапраўды загадкавыя імёны функцый як гэта _IO_fgets. Але гэта будзе ставіцца да гэтага выкліку ЕдеЬз. Дзесьці ўнутры тут, мы памылку сегментацыі. Калі мы паглядзім на аргументы ЕдеЬз, мы можам надрукаваць буфер. Давайце друкаваць як - О, няма. Друк не будзе працаваць так, як я хачу яго бачыць. Давайце паглядзім на фактычную праграму. Буфер масіў знакаў. Гэта масіў сімвалаў 128 знакаў. Таму калі я кажу буфера друку, ён збіраецца друкаваць гэтыя 128 знакаў, які я мяркую, што чакаецца. Тое, што я шукаў, гэта раздрукаваць адрас буфера, але гэта на самай справе не кажаце мне многае. Таму, калі я, здараецца, гавораць тут х буфера, ён паказвае мне 0xbffff090, , Які, калі вы памятаеце з папярэдніх ці нейкі момант, Oxbffff, як правіла, стэк-иш рэгіёну. Стэк мае тэндэнцыю пачынацца дзесьці крыху менш 0xc000. Проста, убачыўшы гэты адрас, я ведаю, што буфера адбываецца ў стэку. Перазагрузка маёй праграмы, запусціць, уверх, буфер мы бачылі, было гэта паслядоўнасць сімвалаў якія ў значнай ступені бессэнсоўна. Тады друку файла, што робіць файл выглядае? [Студэнт] Null. >> Так. Размовы тыпу FILE *, так што гэта паказальнік, і значэнне гэтага паказальніка з'яўляецца нулявым. Так ЕдеЬз будзе спрабаваць чытаць з гэтага паказальніка, ўскосным чынам, але для таго, каб атрымаць доступ да гэтага паказальніку, яна павінна разыменовать яго. Або для таго, каб атрымаць доступ да якой яна павінна быць накіравана на гэта разыменовывает яго. Так што гэта разнаймення нулявога паказальніка і сегментацыі. Я мог бы перазапуск яго там. Калі разбіць на нашай галоўнай кропкай і бегчы, Першы радок кода з'яўляецца знак * Файл = "nonexistent.txt"; Гэта павінна даць даволі вялікі намёк на тое, чаму гэтая праграма не выконваецца. Увод наступнага падводзіць мяне да наступнай радку, дзе я адкрыць гэты файл, і тады я адразу трапляюць у нашу лінію, дзе калісьці я ўдарыў іншы, ён збіраецца падаць. Хто-небудзь хоча выкінуць з прычын, чаму мы маглі б памылку сегментацыі? [Студэнт] Файл не існуе. >> Так. Гэта павінна быць намёкам што кожны раз, калі вы адкрываеце файл, які вы павінны пераканацца, што файл сапраўды існуе. Дык вось, "nonexistent.txt"; Калі мы Еореп файла для чытання, то мы павінны сказаць, калі (файл == NULL) і сказаць, Е ("Файл не існуе!" або - яшчэ лепш - імя файла); вяртанне 1; Такім чынам, зараз мы праверым, калі гэта NULL Перад фактычна працягваюцца і спрабуе чытаць з гэтага файла. Мы можам перарабіць яго, каб убачыць, што гэта працуе. Я меў намер уключыць новую радок. Так што цяпер nonexistent.txt не існуе. Вы заўсёды павінны праверыць для такога роду рэчы. Вы заўсёды павінны праверыць, калі Еореп вяртае NULL. Вы заўсёды павінны праверыць, каб пераканацца, што таНос не вяртае NULL, , Інакш вы сегментацыі. Цяпер buggy4.c. Запуск. Я мяркую, што гэта чакае ўводу або, магчыма, бясконцы цыкл. Так, гэта бясконцы цыкл. Так buggy4. Падобна на тое, мы бясконцы цыкл. Мы можам разбіць на асноўныя, запусціць нашу праграму. У GDB, пакуль скарачэнне выкарыстання адназначны або спецыяльных скарачэнняў, якія яны падаюць для вас, Затым вы можаце выкарыстоўваць п выкарыстоўваць наступную замест таго, каб уводзіць наступны ўсю дарогу. І зараз, калі я трапіў п раз, то я магу проста націснуць Enter, каб працягваць ісці наступная замест таго, каб ударыць п Enter, Enter п, п Enter. Падобна на тое, што я знаходжуся ў нейкі цыкл, што сядзіць масіў [я] = 0. Падобна на тое, я ніколі не вырвацца з гэтага цыклу. Калі я друкую я, так што я = 2, то я буду ісці далей. Я буду друкаваць Я, Я = 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, мы бачым усё, што адбываецца, цэлалікавых масіў [5]; для (я = 0; I <= SizeOf (масіў), я + +) Масіў [я] = 0; Што ж мы бачым, што тут не так? Як намёк, калі я рабіў GDB buggy4 - давайце разбярэм асноўныя, запускаем - Я друку SizeOf (масіў), каб паглядзець, у якім стане, дзе я павінна, нарэшце, вырвацца. Дзе я? А я бягу? Я не заяўляю яшчэ няма. Такім чынам, друк SizeOf (масіў), і гэта 20, якая, як чакаецца, паколькі мой масіў мае памер 5, і гэта з 5 цэлых лікаў, Такім чынам, уся рэч павінна быць 5 * SizeOf (INT) байт, дзе SizeOf (INT), як правіла, 4. Такім чынам, SizeOf (масіў) 20. Што гэта можа быць? [Студэнт] Падзеленыя па SizeOf (INT). >> Так, / SizeOf (INT). Падобна на тое, ёсць яшчэ праблема. Я думаю, што гэта павінна быць проста < так як гэта амаль заўсёды <і ніколі <=. А цяпер давайце падумаем аб тым, чаму гэта было на самай справе зламаны. Хто-небудзь ёсць здагадкі чаму я скідаецца на 0 праз кожную ітэрацыю цыклу? Адзінае, што ўсярэдзіне, што тут адбываецца, што масіў [I] у цяперашні час ўстаноўлена ў 0. Так ці інакш, гэты радок кода выклікае нашу Int я павінен быць усталяваны на 0. [Студэнт] Можа быць, таму што гэта пераазначэнне памяць аб гэтай частцы я , Калі ён думае, што гэта чарговы элемент масіва? >> [Боуден] Так. Калі мы збіраемся за межамі нашага масіва, нейкім чынам, што прастора, якое мы пераазначэння перавызначаць значэнне я. І таму, калі мы паглядзім на buggy4, разбіць асноўныя, бег, Давайце друкаваць адрас я. Падобна на тое, гэта bffff124. Зараз давайце друкаваць адрас масіва [0]. 110. Як наконт [1]? 114. [2], 118. 11с, 120. Масіў [5] bfff124. Такім чынам, масіў [5] мае той жа адрас, як я, гэта азначае, што масіў [5] я. Калі яны маюць аднолькавы адрас, яны тое ж самае. Таму, калі мы ўсталёўваем масіў [5] 0, мы ўсталёўваем я да 0. І калі вы думаеце пра гэта з пункту гледжання стэка, Int я аб'яўлены першы, які азначае, што я атрымлівае некаторы прастору ў стэку. Тады масіў [5] вылучаецца, так што потым 20 байтаў ў стэку. Так што я вылучыў атрымлівае першы, то гэтыя 20 байт атрымаць выдзелены. Так што я адбываецца прама перад масівам, і з-за спосабу, як я ўжо казаў на мінулым тыдні, дзе гэта тэхнічна стэк расце ўніз, Пры індэкс ў масіве, мы гарантавалі, што 0-я пазіцыя ў масіве заўсёды бывае перад першай пазіцыі ў масіве. Гэта свайго роду, як я звярнуў яго на мінулым тыдні. Звярніце ўвагу, што ўнізе у нас ёсць адрас 0, а ў верхнім у нас ёсць адрас Макс. Стэк пастаянна расце ўніз. Скажам, мы вылучаем я. Мы вылучаем цэлае я, што азначае, давайце проста скажам, тут я цэлымі атрымлівае вылучаныя. Тады мы вылучаем наш масіў з 5 цэлых лікаў, якое азначае, што пад гэтым, Паколькі стэк расце ўніз, гэтыя 5 лікаў атрымаць выдзелены. Але, паколькі, як масівамі працаваць, мы гарантавалі, што першая пазіцыя ў масіве заўсёды мае адрас менш, чым другая рэч у масіве. Такім чынам, 0 масіве пазіцыя заўсёды павінна адбыцца першая ў памяці, у той час як масіў становішча 1 павінна адбыцца пасля гэтага і масіў становішча 2 павінна адбыцца пасля гэтага, Гэта азначае, што 0 масіў пазіцый адбудзецца дзесьці тут, Масіў пазіцыя 1 будзе адбывацца вышэй, што таму што рух уверх азначае больш высокія адрасы з максімальнай адрас тут. Такім чынам, масіў [0] тут, масіў [1] тут, масіў [2] тут, масіў [3] тут. Звярніце ўвагу, што перш, чым мы выдзелена цэлае я ўсю дарогу сюды, як мы ўсё далей і далей у нашым масіве, мы ўсё бліжэй і бліжэй да нашай цэлае я. Проста так атрымалася, што масіў [5], якая з'яўляецца адной пазіцыі за межамі нашага масіва, Менавіта тут цэлых Мне давялося быць вылучаныя. Так што гэта кропкі, дзе мы, здараецца, ўдары прасторы ў стэку , Які быў выдзелены для цэлага я, і мы ўсталёўваем, што на 0. Вось як гэта працуе. Пытанні? Так. [Студэнт] Не бярыце ў галаву. Добра. [Студэнт] Як пазбегнуць гэтых роду памылкі? Гэтыя роду памылкі? Не выкарыстоўвайце C у якасці мовы праграмавання. Выкарыстоўвайце мову, які мае праверкі межаў масіва. Пакуль вы асцярожныя, вам проста трэба, каб не ісці міма межы масіва. [Студэнт] Дык вось, калі мы праходзілі міма рамкі вашага масіва - [Боуден] Вось дзе рэчы пачынаюць адбываецца не так. >> [Студэнт] О, усё ў парадку. Да таго часу, пакуль вы застаяцеся ў памяці, выдзеленай для масіва, усё ў парадку. Але C не робіць памылак. Калі я раблю масіў [1000], ён будзе з задавальненнем проста змяніць што б ні здарылася - Ён ідзе на пачатак масіва, то яна ідзе пасля 1000 пазіцый і ўстанаўлівае яго ў 0. Яна не робіць ніякіх праверак, што о-о, гэта на самай справе не 1000 рэчаў у ім. 1000 з'яўляецца далёка за межы таго, што я павінна мяняцца, у той час як Java або нешта, вы атрымаеце масіў з індэкса межаў або індэкс выключэння з межаў. Вось чаму многія мовах больш высокага ўзроўню гэтых рэчаў дзе, калі вы ідзяце за межы масіва, вы не так што вы не можаце змяніць сітуацыю з-пад вас а потым усё пойдзе нашмат горш, чым проста атрымліваю выключэнне кажуць, што вы выйшлі за канец масіва. [Студэнт] і таму павінны мы змянілі <= проста > [Боуден] Так. Яна павінна быць > [Студэнт] Дакладна. Яшчэ пытанні? Добра. [Студэнт] У мяне ёсць пытанне. >> Так. [Студэнт] Што такое фактычнай зменнай масіва? [Боуден] Як і тое, што масіў? Масіў само па сабе з'яўляецца сімвалам. Ён знаходзіцца ўсяго ў адрас пачатку 20 байт, што мы спасылак. Вы можаце думаць пра гэта як паказальнік, але гэта сталы паказальнік. Як толькі рэчы кампілююцца, зменная масіва больш не існуе. [Студэнт] Так, як гэта вызначыць памер масіва? Памер масіва ставіцца да памеру гэтага блока, што гэты знак ставіцца. Калі я раблю нешта накшталт Е ("% п \ п", масіў); Давайце запусціць яго. Што я толькі не так? "Масіў" масіў, абвешчаны тут. О, тут. Clang разумны, і гэта адбываецца заўважыць, што я абвясціў масіў на 5 элементаў Але я індэксацыі ў становішчы 1000. Ён можа зрабіць, таму што гэта толькі канстанты. Ён можа толькі пайсці так далёка ў заўважыўшы, што я збіраюся за межы масіва. Але заўважце, раней, калі мы былі я б няправільным, ён не можа вызначыць, колькі значэнняў я мог бы ўзяць на сябе, таму ён не можа вызначыць, што я збіраюся пасля заканчэння масіва. Вось толькі Clang хітруе. Але цяпер зрабіць buggy4. Так што ж я раблю не так? Невідавочнае аб'яву бібліятэчную функцыю "Printf. Я збіраюся хочаце # ўключыць . Добра. Зараз працуе buggy4. Друк значэнне масіва, як я зрабіў тут, раздрукаваць яго як паказальнік адбіткі нешта накшталт гэтага - bfb8805c - што некаторыя адрасы вось у стэк-иш рэгіёну. Масіў сябе, як паказальнік, але гэта не фактычны паказальнік, З рэгулярнымі паказальнік мы можам змяніць. Масіў проста некаторая канстанта. 20 блокаў памяці пачынаецца з адрасу 0xbfb8805c. Так bfb8805c праз гэты адрас +20- ці я мяркую, -20 - гэта ўсё з памяці, выдзеленай для гэтага масіва. Array, сама пераменная нідзе не захоўваецца. Калі вы збіраеце, кампілятар - рука хваляй на яе - але кампілятар будзе выкарыстоўваць толькі дзе яно ведае, масіў будзе. Ён ведае, дзе, што масіў пачынаецца, і таму ён заўсёды можа проста рабіць рэчы з пункту гледжання зрушэння ад пачатку. Яна не патрабуе сама пераменная ўяўляе масіва. Але калі я раблю нешта накшталт Int * р = масіве, цяпер р-паказальнік, які паказвае, што масіў, і цяпер р сапраўды існуе ў стэку. Я свабодна змяняць р. Я магу зрабіць р = таНос. Так што можна сказаць паказваў на масіў, а цяпер ён паказвае на некаторыя месцы на кучы. Я не магу зрабіць таНос масіва =. Калі Clang разумны, ён будзе крычаць на мяне з месца ў кар'ер. На самай справе, я ўпэўнены, GCC будзе гэта рабіць. Такім чынам, масіў тыпу "INT [5] 'не можа быць прызначаны. Вы не можаце прызначыць нешта тыпу масіва таму што масіў гэта проста канстанта. Гэта сімвал, які спасылаецца на гэтыя 20 байт. Я не магу змяніць яго. [Студэнт] А дзе памер масіва захоўваецца? [Боуден] Гэта нідзе не захоўваюцца. Гэта калі гэта кампіляцыя. Дык дзе ж памер масіва захоўваецца? Вы можаце выкарыстоўваць SizeOf (масіў) усярэдзіне функцыі, што масіў заявіў пра сябе. Так што, калі я раблю некаторыя функцыі, Foo, і я раблю (INT масіва []) Е ("% г \ п", SizeOf (масіў)); , А затым тут я называю Foo (масіў); Унутры гэтай функцыі - давайце запусцім яго. Гэта Clang хітруе зноў. Ён казаў мне, што SizeOf на масіў параметраў функцыі верне памер 'Int' *. Гэта было б памылкай, калі гэта не тое, што я хацеў, каб гэта адбылося. Давайце на самай справе адключыць Werror. Папярэджанне. Папярэджанні усё ў парадку. Гэта будзе па-ранейшаму складаць тых часоў, пакуль яна мае папярэджанне. . / A.out будзе друкаваць 4. Папярэджанне, які быў створаны дакладны паказчык таго, што пайшло не так. Гэты масіў Int толькі збіраецца друкаваць SizeOf (INT *). Нават калі я пастаўлю масіў [5] тут, гэта яшчэ толькі збіраецеся друкаваць SizeOf (INT *). Таму, як толькі вы перадаеце яго ў функцыю, адрозненне паміж масівамі і паказальнікамі не існуе. Гэта адбываецца з масівам, які быў абвешчаны ў стэку, Але як толькі мы пераходзім гэтага значэння, што 0xbf бла, бла, бла, бла ў гэтай функцыі, Затым гэты паказальнік паказвае на гэты масіў у стэку. Такім чынам, гэта азначае, што SizeOf ўжываецца толькі ў функцыі, што масіў быў абвешчаны, Гэта азначае, што пры кампіляцыі гэтай функцыі, Clang, калі праходзіць праз гэтую функцыю, ён бачыць масіў Int масіў памерам 5. І тады ён бачыць SizeOf (масіў). Ну, вось і 20. Гэта на самай справе як SizeOf ў асноўным працуе амаль для ўсіх выпадкаў. Sizeof гэта не функцыя, гэта аператар. Вы не выклікаеце функцыю SizeOf. Sizeof (INT), кампілятар будзе проста перавесці гэта на 4. Зразумеў? Добра. [Студэнт] Так у чым жа розніца паміж SizeOf (масіва) у асноўны і ў Foo? Гэта таму, што мы гаворым, SizeOf (масіў), які мае тып * INT, у той час як масіў сюды не адносіцца да тыпу Int *, гэта Int масіва. [Студэнт] Так што калі ў вас параметр у масіве [] замест Int масіва *, гэта азначала б, што яшчэ можна было змяніць масіў, таму што зараз гэта паказальнік? [Боуден] Як гэта? >> [Студэнт] Так. Ці можаце вы змяніць масіў ўнутры функцыі зараз? [Боуден] Вы можаце змяніць масіў у абодвух выпадках. У абодвух гэтых выпадках вы можаце сказаць масіва [4] = 0. [Студэнт] Але вы можаце зрабіць масіва паказваюць на нешта іншае? [Боуден] Ох. Так. У любым выпадку - >> [студэнт] Так. [Боуден] адрозненне паміж масіва [] і Int масіва *, няма ні аднаго. Вы можаце таксама атрымаць некаторыя шматмерныя масівы тут для некаторых зручны сінтаксіс, але гэта яшчэ толькі паказальнік. Гэта азначае, што я вольны рабіць масіва = таНос (SizeOf (INT)), а цяпер паказваюць дзесьці ў іншым месцы. Але проста падабаецца, як гэта працуе вечна і заўсёды, Змены ў гэтым масіве, зрабіўшы яго паказваць на нешта іншае не змяняе гэты масіў сюды, таму што гэта копія аргументу, гэта не паказальнік на гэты аргумент. А на самай справе, як і больш прыкмет таго, што гэта сапраўды гэтак жа - Мы ўжо бачылі, што друкаваны масіва - Што рабіць, калі мы друкуем адрас масіва або адрас адрас масіва небудзь з іх? Давайце ігнараваць гэта. Добра. Гэта нармальна. Цяпер ён працуе. / A.out. Друк масіва, то друкуючы адрас масіва, адно і тое ж. Масіў проста не існуе. Ён ведае, калі вы друкуеце масіва, вы друкуеце сімвал, які адносіцца да тых 20 байт. Друк адрас масіва, а, масіў не існуе. Ён не мае адрас, таму ён проста друкуе ў адрас гэтых 20 байт. Як толькі вы кампілявацца, як і ў скампіляваны buggy4. / A.out, Масіў не існуе. Паказальнікі існуе. Масівы не робяць. Блокі памяці, які ўяўляе масіў ўсё яшчэ існуюць, але пераменная масіва і зменных гэтага тыпу не існуе. Гэта як асноўныя адрозненні паміж масівамі і паказальнікамі якія, як толькі вы робіце выклікі функцый, няма ніякай розніцы. Але ўнутры функцыі, што сам масіў абвешчаны, SizeOf працуе па-рознаму так як вы друкуеце памер блока, а памер тыпу, і вы не можаце яго змяніць, таму што гэта знак. Друк рэчы і адрас рэч друкуе тое ж самае. І гэта даволі шмат яго. [Студэнт] Не маглі б вы сказаць, што яшчэ раз? Я, магчыма, нешта прапусціў. Масіў друк і адрас масіва друкуе тое ж самае, а калі вы друкуеце паказальнік па параўнанні з адрасам паказальніка, адна рэч, друкуе адрас, што вы паказваючы на, іншы друкуе адрас паказальніка на стэк. Вы можаце змяніць паказальнік, вы не можаце змяніць масіў знакаў. І SizeOf паказальнік у друк памер, што тып паказальніка. Так Int * р SizeOf (р) у друк 4, але цэлалікавых масіў [5] друк SizeOf (масіва) у друк 20. [Студэнт] Так Int масіва [5] будзе друкаваць 20? >> Так. Вось чаму ўнутры buggy4, калі гэта было SizeOf (масіў) гэта раблю я <20, які з'яўляецца не тое, што мы хацелі. Мы хочам, каб я <5. >> [Студэнт] Добра. [Боуден], а затым, як толькі вы пачынаеце праходзіць у функцыях, калі б мы зрабілі Int * р = масіў; Унутры гэтай функцыі мы можам у асноўным выкарыстоўваюць р і масіў ў дакладна такой жа спосабамі, за выключэннем праблемы SizeOf і змяненне праблема. Але P [0] = 1; такое ж, як кажуць масіў [0] = 1; І як толькі мы кажам, Foo (масіў), або Foo (р); Усярэдзіне функцыі Фу, гэта ж выклік у два разы. Існуе ніякай розніцы паміж гэтымі двума выклікамі. Усё добра на гэтым? Добра. У нас ёсць 10 хвілін. Мы пастараемся, каб прайсці праз гэтую праграму Typer Hacker, гэта сайт, які выйшаў у мінулым годзе ці яшчэ што-небудзь. Гэта проста павінна быць, як вы ўвядзеце выпадкова, і гэта выводзіць - Незалежна файл гэта адбываецца з загружанай з'яўляецца тое, што яна выглядае, як вы друкуеце. Гэта выглядае як свайго роду кода аперацыйнай сістэмы. Гэта тое, што мы хочам рэалізаваць. Вы павінны мець двайковы выкананы файл з імем hacker_typer , Якая ў адзін аргумент, файл "хакер тыпу". Запуск выкананага файла павінны ачысціць экран , А затым раздрукаваць адзін персанаж з перададзены файл кожны раз, калі карыстальнік націскае клавішу. Таму, што б націску клавішы, варта выкінуць, а замест гэтага надрукаваць знак з файла , Што з'яўляецца аргументам. Я буду вельмі шмат чаго сказаць вам, што тое, што мы збіраемся трэба ведаць. Але мы хочам праверыць бібліятэку termios. Я ніколі не выкарыстоўваў гэтую бібліятэку ў маім жыцці, таму яна мае вельмі мінімальныя мэтаў. Але гэта будзе бібліятэка, мы можам выкарыстоўваць, каб выкінуць характару вы націснеце калі вы набіраеце ў стандартных цалі Так hacker_typer.c, і мы збіраемся хочаце # ўключыць . Гледзячы на ​​мужчыну старонкі для termios - я мяркую, што гэта тэрмінал OS або что-то - Я не ведаю, як яе чытаць. Гледзячы на ​​гэта, ён кажа, каб уключыць гэтыя 2 файла, так што мы будзем рабіць. Першае, што па-першае, мы хочам, каб узяць у адзін аргумент, які з'яўляецца файл, які мы павінны адкрыць. Такім чынам, што ж я хачу зрабіць? Як я магу праверыць, каб убачыць мяне ёсць адзін аргумент? [Студэнт] Калі ARGC роўна гэта. >> [Боуден] Так. Так што, калі (ARGC = 2!) Е ("Выкарыстанне:% S [файл, каб адкрыць]"). Так што цяпер, калі я запускаю гэта без прадастаўлення другі аргумент - ой, мне патрэбна новая лінія - Вы ўбачыце гэта кажа выкарыстання:. / Hacker_typer, , А затым другі аргумент павінен быць файл я хачу адкрыць. І што цяпер мне рабіць? Я хачу прачытаць з гэтага файла. Як я магу чытаць з файла? [Студэнт] Вы адкрыць яго ў першую чаргу. >> Так. Так Еореп. Што Еореп выглядаць? [Студэнт] Імя файла. >> [Боуден] Імя файла будзе ARGV [1]. [Студэнт] І тое, што вы хочаце з ім рабіць, таму - >> [Боуден] Так. Так што, калі вы не памятаеце, вы проста маглі б зрабіць чалавек Еореп, дзе ён будзе константного сімвал шляху *, дзе шлях файла, сопзЬ сЬаг рэжыме *. Калі вы выпадкова не памятаеце, што рэжым, то вы можаце шукаць рэжыму. Усярэдзіне чалавека старонак, касой рысай з'яўляецца тое, што вы можаце выкарыстоўваць для пошуку рэчаў. Так што я друкую / рэжым для пошуку рэжыму. п і тое, што вы можаце выкарыстоўваць, каб цыкл праз пошук супадзенняў. Вось ён кажа, гэты аргумент рэжыму ў радок пачынаючы з адной з наступных паслядоўнасцяў. Такім чынам, г, Адкрыць тэкставы файл для чытання. Вось што мы хочам зрабіць. Для чытання, і я хачу захаваць гэта. Справа будзе файл *. Цяпер тое, што я хачу рабіць? Дай мне секунду. Добра. Цяпер тое, што я хачу рабіць? [Студэнт] Калі ласка, праверце, калі гэта NULL. >> [Боуден] Так. Кожны раз, калі вы адкрываеце файл, пераканайцеся, што вы паспяхова зможаце адкрыць яго. Цяпер я хачу зрабіць гэта termios рэчы, дзе я хачу спачатку прачытаць мае налады і захаваць тых, хто ў чымсьці, то я хачу змяніць мае налады выкінуць любы знак, які я друкую, а потым я хачу, каб абнавіць гэтыя параметры. А потым у канцы праграмы, я хачу вярнуцца да маёй першапачатковай налады. Такім чынам, структура будзе тыпу termios, і я збіраюся хочаце, каб два з іх. Першы з іх будзе мой current_settings, а затым яны збіраюцца, каб быць маім hacker_settings. Па-першае, я збіраюся хочаце захаваць бягучыя параметры, Затым я збіраюся хочаце абнавіць hacker_settings, , А затым шлях у канцы маёй праграмы, я хачу вярнуцца да бягучай налады. Такім чынам, захаванне бягучых налад, то, як гэта працуе, мы termios чалавек. Мы бачым, што ў нас ёсць гэтая tcsetattr Int, Int tcgetattr. Я праходжу ў termios структуры яго паказальнік. Як гэта будзе выглядаць гэта - дыскі ўжо забыліся, што такое функцыя была выклікана. Скапіюйце і ўстаўце яго. Так tcgetattr, то я хачу перадаць у структуру, што я захавання інфармацыі ў, які будзе current_settings, і першым аргументам з'яўляецца дэскрыптар файла для чаго я хачу, каб захаваць атрыбуты. Што дэскрыптар файла, як і любы раз, калі вы адкрываеце файл, ён атрымлівае дэскрыптар файла. Калі я Еореп ARGV [1], ён атрымлівае дэскрыптар файла, які вы спасылаецеся кожны раз, калі вы хочаце, каб чытаць ці пісаць у яго. Гэта не файлавы дэскрыптар я хачу выкарыстоўваць тут. Ёсць тры файлавых дэскрыптараў ў вас ёсць па змаўчанні, якія ўваходзяць у стандартную камплектацыю, стандартны вывад і стандартная памылка. Па змаўчанні, я думаю, што гэта стандарт роўны 0, стандартны вывад 1, і стандартная памылка роўная 2. Так што я хачу змяніць налады? Я хачу змяніць налады кожны раз, калі я ударыў характар, Я хачу, каб кінуць гэты персанаж далёка замест друку на экран. Які паток - стандарт, стандарт у арэнду, або стандартная памылка - рэагуе на рэчы, калі я друкую на клавіятуры? >> [Студэнт] Стандартны цалі >> Так. Так што я магу зрабіць, альбо 0, альбо я магу зрабіць стандартнага ўводу. Я атрымліваю current_settings стандартнага цалі Цяпер я хачу, каб абнавіць гэтыя параметры, так што спачатку я буду капіяваць у hacker_settings, што мой current_settings ёсць. І як структуры працы ён будзе проста скапіяваць. Гэта капіюе усе палі, як можна было чакаць. Цяпер я хачу, каб абнавіць некаторыя поля. Гледзячы на ​​termios, то вам давядзецца прачытаць шмат што з гэтага проста каб паглядзець, што вы хацелі б, каб шукаць, але сцягамі вы збіраецеся хочаце паглядзець на гэта рэха, так ECHO Echo сімвалаў ўваходу. Спачатку я хачу задаць - дыскі ўжо забыліся, што такое палёў. Гэта тое, што структура выглядае наступным чынам. Так рэжымаў ўводу Я думаю, што мы хочам змяніць. Мы будзем глядзець на рашэнні, каб пераканацца, што тое, што мы хочам змяніць. Мы хочам змяніць lflag ў мэтах прадухілення неабходнасці праглядаць усё гэта. Мы хочам змяніць мясцовых рэжымаў. Вам прыйдзецца прачытаць усю гэтую рэч, каб зразумець, дзе ўсё належыць што мы хочам змяніць. Але гэта ўнутры мясцовых рэжымаў, дзе мы збіраемся хочам змяніць гэтую сітуацыю. Так hacker_settings.cc_lmode тое, што гэта называецца. c_lflag. Тут мы трапляем у бітавай аператары. Мы быццам па-за часам, але мы будзем прайсці праз гэта вельмі хутка. Тут мы трапляем у пабітава аператары, , Дзе я думаю, што я аднойчы сказаў даўно, што кожны раз, калі вы пачынаеце справу з сцягамі, Вы збіраецеся выкарыстоўваць аператар пабітава шмат. Кожны біт у сцяг адпавядае свайго роду паводзінаў. Дык вось, гэты сьцяг мае кучу розных рэчаў, дзе ўсе яны азначаюць нешта іншае. Але тое, што я хачу зрабіць, гэта проста выключыць біт, які адпавядае рэха. Такім чынам, каб адключыць гэтую функцыю я і = ¬ ECHO. На самай справе, я думаю, што гэта як Techo ці чамусьці. Я проста хачу, каб праверыць яшчэ раз. Я магу termios яго. Гэта проста рэха. ECHO будзе адзін біт. ¬ ECHO будзе азначаць усе біты ўстаноўлены ў 1, што азначае, што усе сцягі устаноўлены на праўдзівы за выключэннем ECHO няшмат. Па канчатак майго мясцовага сцягі з гэтым, гэта азначае, што ўсе сцягі, якія ў цяперашні час усталяваны ў праўдзівае будзе па-ранейшаму устаноўлены на праўдай. Калі мая ECHO сцяг усталяваны ў сапраўднае, то гэта абавязкова мае значэнне ХЛУСНЯ на ECHO сцяг. Такім чынам, гэты радок кода проста выключае ECHO сцяг. Іншыя радкі кода, я проста скапіяваць іх у інтарэсах часу, а затым растлумачыць іх. У рашэнні, ён сказаў 0. Гэта, напэўна, лепш прама сказаць стандартнага ўводу. Звярніце ўвагу, што я таксама раблю ECHO | ICANON тут. ICANON спасылаецца на нешта асобнае, што азначае кананічным рэжыме. Якія кананічныя сродкі рэжыме, як правіла, калі вы друкуеце з каманднага радка, У стандартную не апрацоўвае нічога, пакуль вы патрапілі радка. Таму, калі вы GetString, вы набіраеце кучу рэчаў, то вы патрапілі радка. Вось калі ён адпраўляецца на стандартны цалі Гэта значэнне па змаўчанні. Калі я выключаю кананічным рэжыме, зараз кожны персанаж вы націснеце гэта тое, што атрымлівае апрацоўваюцца, якія, як правіла, выгляд дрэнна, таму што гэта павольна, каб апрацаваць гэтыя рэчы, Менавіта таму гэта добра для змякчэння яго ў цэлыя радка. Але я хачу, каб кожны знак, які будзе апрацоўвацца так як я не хачу, каб мяне чакаць, каб ударыць новага радка перад апрацоўкай ўсіх персанажаў я друкаваць. Гэта адключае кананічным рэжыме. Гэты матэрыял проста азначае, што, калі ён на самай справе апрацоўвае сімвалы. Гэта азначае, апрацоўваць іх адразу ж, як толькі я друкую іх, апрацоўваць іх. І гэта функцыя, якая абнаўляе мае налады для стандарту, і TCSA сродкаў зрабіць гэта прама цяпер. Іншыя варыянты чакаць, пакуль усё, што ёсць у цяперашні час на паток апрацоўваецца. Гэта не мае вялікага значэння. Проста зараз змяніць мае налады, каб быць усё, што ў цяперашні час у hacker_typer_settings. Я мяркую, што я назваў яго hacker_settings, таму давайце зменім гэта. Змяніць усё, каб hacker_settings. Цяпер у канцы нашай праграмы мы збіраемся хочаце вярнуцца да таго, што ў цяперашні час унутры normal_settings, які будзе проста глядзець, як і normal_settings. Звярніце ўвагу, я не змяніў ні аднаму з маіх normal_settings так як першапачаткова яго атрымання. Тады проста змяніць іх назад, я перадаю іх назад у канцы. Гэта было абнаўленне. Добра. Цяпер усярэдзіне тут я проста растлумачу код у інтарэсах эканоміі часу. Гэта не так ужо шмат кода. Мы бачым, чытаем знак з файла. Мы назвалі яе ф. Цяпер вы можаце fgetc чалавек, але як fgetc будзе працаваць проста ён збіраецца вярнуцца сімвал, які вы толькі што прачыталі або EOF, што адпавядае канца файла ці некаторыя памылкі адбываецца. Мы цыкл, працягваючы чытаць адзін знак з файла, пакуль мы не вычарпалі знакаў для чытання. І пакуль мы робім гэта, мы чакаем на адзін знак з стандартнага цалі Кожны раз, калі вы набіраеце нешта ў камандным радку, што чытае ў знак з стандартнага цалі Тады риЬспаг толькі збіраецца паставіць знак чытаем тут з файла ў стандартны вывад. Вы можаце риЬспаг чалавек, але гэта проста пакласці на стандартны вывад, гэта друк гэтага знака. Вы таксама можаце проста зрабіць Е ("% С", С); ж ідэю. Гэта збіраецца зрабіць большую частку нашай працы. Апошняе, што мы збіраемся трэба зрабіць, гэта проста Fclose наш файл. Калі вы не Fclose, што гэта ўцечка памяці. Мы хочам, каб Fclose файл, які мы першапачаткова адкрыты, і я думаю, што гэта яна. Калі мы зробім гэта, у мяне ўжо ёсць праблемы. Давайце паглядзім. Што гэта скардзіцца? Чакаецца 'INT', але аргумент тыпу "структура _IO_FILE * '. Мы ўбачым, што працуе. Дазваляецца толькі ў C99. Augh. Добра, зрабіць hacker_typer. Цяпер мы атрымліваем больш карысныя апісання. Такім чынам, выкарыстанне неаб'яўленай ідэнтыфікатар "normal_settings. Я не называю гэта normal_settings. Я назваў яго current_settings. Так давайце зменім ўсё гэта. Пераходзячы аргумент. Я зраблю гэта 0 на дадзены момант. Добра. . / Hacker_typer cp.c. Я таксама не ачысціць экран у самым пачатку. Але вы можаце азірнуцца на апошнія пастаўленай задачы, каб убачыць, як вы ачысціць экран. Гэта проста друкаваць некаторыя сімвалы у той час як гэта раблю тое, што я хачу зрабіць. Добра. І, думаючы пра тое, чаму гэтая павінна быць 0 замест стандартнага ўводу, якое павінна быць # вызначыць 0, гэта скардзіцца, што - Раней, калі я сказаў, што ёсць дэскрыптараў файлаў, але тады ў вас таксама ёсць файл * дэскрыптар файла знаходзіцца ўсяго ў адно цэлае, у той час як файл * мае цэлую кучу рэчаў, звязаных з ім. Таму мы павінны сказаць 0 замест стандартнага ўводу з'яўляецца тое, што стандартны ўвод з'яўляецца FILE *, які паказвае на тое, што спасылаецца файлавы дэскрыптар 0. Так што нават тут, калі я раблю Еореп (ARGV [1], я атрымліваю файл * назад. Але дзесьці ў файле * рэч адпаведны файлавы дэскрыптар для гэтага файла. Калі вы паглядзіце на даведачнай старонцы для адкрытай, так што я думаю, што вы павінны зрабіць чалавеку адкрыты 3 - Няма - Чалавек 2 адкрытых - так. Калі вы паглядзіце на старонку адкрытай, адкрытай, як больш нізкага ўзроўню Еореп, і ён вяртаецца фактычнае дэскрыптар файла. Еореп робіць кучу рэчаў на верхняй адкрытай, якія замест вяртання за ўсё, што файлавы дэскрыптар вяртае цэлае FILE * паказальнік унутры якой знаходзіцца наша маленькая дэскрыптар файла. Такім чынам, стандарт ставіцца да рэчы * FILE, а 0 азначае проста стандартны файлавы дэскрыптар ў сабе. Пытанні? [Смяецца] дзьмуў праз гэта. Добра. Мы зрабілі. [Смяецца] [CS50.TV]