[Powered by Google Translate] [CS50 Библиотека]
[Nate Хардисон] [Harvard University]
[Это CS50. CS50.TV]
Библиотека CS50 является полезным инструментом, который мы установили на устройство
чтобы сделать его проще для вас, чтобы написать программу, которая предлагает пользователю для ввода.
В этом видео мы будем тянуть занавес и посмотрим, что именно находится в CS50 библиотеки.
>> В видео на библиотеки C, мы говорим о том, как вы # включить заголовки файлов
библиотеки в исходный код,
а затем вы связываете с двоичного файла библиотеки в стадии компоновки
из процесса компиляции.
В заголовке файла указывается интерфейс библиотеки.
То есть, они подробно все ресурсы, что в библиотеке доступны для использования,
как функция деклараций, константы и типы данных.
Двоичный файл библиотеки содержит реализацию библиотеки,
который составляется из заголовка библиотеки, файлы и библиотеки. с файлами исходного кода.
>> Двоичного файла библиотеки не очень интересно посмотреть на, так как это, ну, в двоичной системе.
Итак, давайте взглянем на файлы заголовков для библиотеки вместо этого.
В этом случае есть только один заголовок файла с именем cs50.h.
Мы установили его в пользовательский каталог включает
вместе с файлами других библиотек системы заголовке.
>> Одна из первых вещей, которые вы заметите, что cs50.h # включает в себя заголовочные файлы из других библиотек -
поплавка, ограничения, стандартные логический, и стандартные LIB.
Опять же, следуя принципу не изобретать колесо,
мы построили CS0 библиотеки с помощью инструментов, которые другие предоставлена ​​для нас.
>> Следующая вещь, которую вы увидите в библиотеке является то, что мы определяем новый тип, называемый «струны».
Эта линия действительно только создает псевдоним для символьного типа *,
так что это не может магическим образом придать новый тип строки с атрибутами
обычно связаны с строковых объектов на других языках,
таких как длина.
Поэтому мы сделали это, чтобы оградить новых программистов кровавые подробности
указателей, пока они не готовы.
>> Следующая часть заголовка файла декларации функций
CS50, что библиотека предоставляет вместе с документацией.
Обратите внимание на уровень детализации в комментариях здесь.
Это супер важно, чтобы люди знали, как использовать эти функции.
Мы заявляем, в свою очередь, функциями, чтобы побудить пользователей и возвращения символов, двухместные, поплавки, целыми,
давно мечтает, и строки, используя наш собственный тип строки.
Следуя принципу сокрытия информации,
мы поставили нашим определением в отдельном файле реализации с -. cs50.c--
расположен в каталоге источник пользователь.
Мы при условии, что файл, так что вы можете взглянуть на нее,
узнать от него, и перекомпилировать его на разных машинах, если вы хотите,
даже если мы думаем, что лучше работать на приборе для этого класса.
В любом случае, давайте посмотрим на это сейчас.
>> Функции GetChar, GetDouble, GetFloat, GetInt, и GetLongLong
Все построено на вершине GetString функции.
Оказывается, что все они следуют по существу той же схеме.
Они используют то время как цикл, чтобы побудить пользователей для одной строкой ввода.
Они возвращают специальное значение, если пользователь вводит пустую строку.
Они пытаются разобрать входа пользователя в качестве соответствующего типа,
будь это символ, двойные, с плавающей точкой, и т.д.
И тогда они либо возвращают результат, если вход был успешно завершен
или они reprompt пользователь.
>> На высоком уровне, нет ничего действительно сложно здесь.
Вы могли бы написать так же структурированный код себе в прошлом.
Пожалуй, самый загадочный вид части Sscanf вызов, который анализирует введенные пользователем данные.
Sscanf является частью преобразования семьи формат ввода.
Он живет в io.h стандарт, и его работа заключается в разобрать строку C,
в соответствии с определенным форматом, хранения разбора результатов в переменную
предоставляемых абоненту.
Поскольку входной функции преобразования формата очень полезно, широко используемые функции
, которые не являются супер интуитивно сначала,
мы пойдем над тем, как Sscanf работает.
>> Первый аргумент Sscanf это символ * - указатель на символ.
Для того чтобы функция работала правильно,
, что символ должен быть первым символом строки C,
прекращается с нулевым \ 0 характера.
Это строка для разбора
Второй аргумент Sscanf это формат строки,
обычно передается в виде строки постоянной,
и вы могли видеть строку, как это раньше при использовании Printf.
Знак процента в строке формата указывает на спецификатор преобразования.
Характер сразу после знака процента,
указывает на тип C, который мы хотим Sscanf преобразования.
В GetInt, вы видите, что есть д% и% о.
Это означает, что Sscanf постараемся десятичной INT -% D - и символ -% с.
Для каждого спецификатор преобразования в формат строки,
Sscanf ожидает, что соответствующий аргумент позже в своем списке аргументов.
Этот аргумент должен указывать на местоположение надлежащим типизированных
, в котором хранится результат преобразования.
>> Типичный способ сделать это состоит в создании переменной в стеке перед вызовом Sscanf
Для каждого элемента, который вы хотите, чтобы разобрать из строки
, а затем использовать оператор адреса - амперсанд - передать указатели
в этих переменных на призыв Sscanf.
Вы можете видеть, что в GetInt мы делаем именно это.
Прямо перед вызова Sscanf, мы заявляем Int называется п и символ с вызова в стеке,
и мы переходим указатели на них в вызове Sscanf.
Подставляя эти переменные в стеке является предпочтительным по сравнению с использованием пространства, выделенного
в куче с таНос, так как позволяет избежать накладных расходов на таНос вызова,
и вам не придется беспокоиться об утечке памяти.
Персонажи не предваряется знаком процента не подскажите преобразования.
Скорее они просто добавить в спецификации формата.
>> Например, если в строке формата в GetInt были% D вместо этого,
Sscanf будет искать буквы следуют INT,
и пока он будет пытаться преобразовать Int, он не будет делать ничего другого с.
Единственное исключение из этого пробела.
Пробелы в строке формата соответствует любое количество пробелов -
даже нет вообще.
Итак, вот почему комментария упоминает, возможно, с ведущими и / или конечных пробелов.
Таким образом, на данный момент это выглядит как вызов нашим Sscanf постараемся разобрать строки ввода пользователем
, проверяя возможные ведущие пробелы,
следует Int, который будет конвертирован и сохранен в Int переменной п
следует некоторое количество пробелов, и следует символ
храниться в символ переменной с.
>> Что о возвращении значение?
Sscanf будет анализировать входные линии от начала до конца,
остановке, когда она достигает конца или когда персонаж во входной
не соответствует формату характер или когда он не может сделать преобразования.
Пришло возврата используется выделить, когда он остановился.
Если он остановился, потому что он достиг конца входной строки
Перед выполнением любых преобразований и перед отказом в соответствии с частью строки формата,
Затем специальная константа EOF возвращается.
В противном случае она возвращает количество успешных переходов,
который может быть 0, 1, или 2, так как мы попросили два преобразования.
В нашем случае, мы хотим убедиться, что пользователь ввел в Int и только Int.
>> Итак, мы хотим, чтобы вернуться Sscanf 1. Узнайте, почему?
Если Sscanf вернула 0, то преобразование не были сделаны,
так что пользователь ввел нечто иное, чем Int в начале входе.
Если Sscanf возвращает 2, то пользователь не правильно ввести его в начале ввода,
но затем они ввели в некоторых не-символу после
с% с успешность преобразования.
Ничего себе, это довольно пространное объяснение для одного вызова функции.
В любом случае, если вы хотите больше информации о Sscanf и его братья и сестры,
проверить мужчину страницы, Google, или обоих.
Есть много вариантов формата строки,
и они могут сохранить вам много ручного труда при попытке разобрать строки в C.
>> Последняя функция в библиотеке смотреть на это GetString.
Оказывается, что GetString является сложной функцией, чтобы написать правильно,
даже если она кажется такой простой, общая задача.
Почему это так?
Ну, давайте подумаем о том, как мы собираемся хранить строку, пользователь вводит дюйма
Так как строка представляет собой последовательность символов,
мы могли бы хранить его в массив в стеке,
но мы должны знать, как долго массива будет, когда мы объявим его.
Точно так же, если мы хотим, чтобы положить его на кучу,
мы должны перейти к таНос количество байт, мы хотим резерва,
Но это невозможно.
Мы понятия не имеем, сколько символов пользователь будет вводить
прежде чем пользователь на самом деле их набора.
>> Наивное решение этой проблемы, это просто оставляем за собой большой кусок пространства, скажем,
Блок из 1000 символов для ввода пользователя,
при условии, что пользователь никогда бы не ввести в строку, которая долго.
Это плохая идея по двум причинам.
Во-первых, предполагается, что пользователи обычно не вводите в строке так долго,
Вы могли бы тратить много памяти.
На современных машинах, это не может быть проблемой, если вы делаете это
в одном или двух отдельных случаях,
Но если вы принимаете входе пользователя в петлю и хранению для последующего использования,
Вы можете быстро поглощать тонны памяти.
Кроме того, если программа, которую вы пишете для меньшего компьютера -
устройства, например, смартфона или что-то другое с ограниченным объемом памяти -
это решение может вызвать проблемы гораздо быстрее.
Вторая, более серьезная причина, чтобы не делать этого в том, что он покидает вашу программу уязвимой
то, что называется атака переполнения буфера.
В программировании, буфер памяти, используемый для временного хранения входных или выходных данных,
который в данном случае является наш 1000-символов блоков.
Переполнение буфера происходит, когда данные записываются в конце прошлого блок.
>> Например, если пользователь на самом деле типа в более чем 1000 символов.
Вы, возможно, испытали это случайно при программировании с массивами.
Если у вас есть массив из 10 целых чисел, ничто не мешает вам пытаюсь читать, ни писать
15 Int.
Есть никаких предупреждений компилятора или ошибки.
Программа просто промахи вперед и обращается к памяти
где она думает, что 15-Int будет, и это может перезаписать другие переменные.
В худшем случае, вы можете перезаписать некоторые внутренние вашей программы
механизмы контроля, в результате чего ваша программа на самом деле выполнять различные инструкции
чем вы рассчитывали.
>> Так вот, это не является общим для этого случайно,
Но это довольно распространенная техника, плохие парни использовать, чтобы разбить программ
и положить вредоносного кода на компьютеры других людей.
Таким образом, мы не можем просто использовать наши наивные решения.
Нам нужно найти способ, чтобы наши программы с уязвимыми
к атаке переполнения буфера.
Чтобы сделать это, мы должны убедиться, что наши буфера может расти, как мы читаем
больше входных данных от пользователя.
Решение? Мы используем буфера кучи выделяется.
Так как мы можем изменить его размер помощи изменения размера перераспределить функции,
и мы отслеживаем два числа - индекс следующей пустой слот в буфере
и длину или емкость буфера.
Мы читаем в символы от пользователей по одному использованием fgetc функции.
Аргумент fgetc функция принимает - стандартный ввод - это ссылка на стандартную строку ввода,
которая является соединителем входного канала, который используется для передачи пользовательского ввода
от терминала к программе.
>> Всякий раз, когда пользователь вводит нового персонажа, мы проверяем, если индекс
на следующий свободный слот плюс 1 больше, чем емкость буфера.
+1 Приходит, потому что если следующий свободный индекс равен 5,
Затем длину наш буфер должен быть 6 Спасибо 0, индексация.
Если мы не хватить места в буфере, то мы пытаемся изменить ее размер,
удвоение его так, что мы сократили количество раз, что мы изменяем
если пользователь печатает в очень длинную строку.
Если строка стал слишком длинным или если мы запустим из кучи памяти,
Мы освободим наши буфера и возвращает нуль.
>> Наконец, мы добавляем символ в буфер.
Как только пользователь нажимает введите или вернуть, сигнализация с новой строки,
или специальный символ - управление D - который сигнализирует о конце ввода,
Мы делаем проверку, чтобы увидеть, если пользователь на самом деле набрали ни во что.
Если нет, то мы возвращаем нуль.
В противном случае, потому что наш буфер, вероятно, больше, чем нужно,
В худшем случае это почти вдвое больше, чем нам нужно
так как мы дважды каждый раз, когда мы изменяем размер,
мы делаем новую копию строки, используя только объем, который нам нужен.
Мы добавили дополнительный 1 к таНос вызова,
так что есть пространство для специального символа нулевого терминатора - \ 0,
которые мы добавляем в строку раз мы копируем в остальных персонажей,
использование зЪгпсру вместо зЬгсру
так что мы можем точно указать, сколько символов мы хотим скопировать.
StrCpy копирует, пока не встретит \ 0.
Тогда мы освободим наши буфер и вернуть копию с вызывающим абонентом.
>> Кто знал, что такое простое на вид функция может быть так сложно?
Теперь вы знаете, что происходит в CS50 библиотеки.
>> Меня зовут Нейт Хардисон, и это CS50.
[CS50.TV]