[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]