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