[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, так як мы папрасілі 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]