[Powered by Google Translate] [CS50 הספרייה]
[נייט Hardison] [אוניברסיטת הרווארד]
[זה CS50. CS50.TV]
ספריית CS50 היא כלי מועיל שהתקנו על המכשיר
כדי לעשות את זה יותר קל לך לכתוב תוכניות שיוזמי קלט.
בסרטון הזה, אנחנו מסיטים את הווילון ומסתכלים על מה בדיוק נמצא בספריית CS50.
>> בוידאו בספריות C, אנחנו מדברים על איך אתה כולל קבצי כותרות #
של הספרייה בקוד המקור שלך,
ואז אתה מקשר עם קובץ בינארי ספרייה בשלב הקישור
של תהליך ההידור.
את קבצי הכותרת לציין את הממשק של הספרייה.
כלומר, הם מפרטים את כל המשאבים שיש בספרייה זמינה עבורך לשימוש,
כמו הצהרות פונקציות, קבועים, וסוגי נתונים.
הקובץ בינארי הספרייה מכיל את היישום של הספרייה,
הנאסף מקבצי הכותרת של הספרייה והספרייה של קבצי קוד מקור. ג.
>> קובץ ספריית בינארי הוא לא מאוד מעניין להסתכל על זה מאז, ובכן, בינארי.
אז, בואו נסתכל על קבצי הכותרת לספרייה במקום.
במקרה זה, יש רק אחד בשם קובץ הכותרת cs50.h.
אנחנו כבר התקנו אותו בספריית המשתמש כולל
יחד עם כותרת קבצים בספריות האחרות במערכת.
>> אחד הדברים הראשונים שתבחינו הוא שcs50.h # כולל קבצי כותרת מספרית אחרות -
לצוף, גבולות, סטנדרטי בול, וlib הסטנדרטי.
שוב, בעקבות העיקרון שלא להמציא את הגלגל מחדש,
בנינו ספריית CS0 שימוש בכלים אחרים שספקו לנו.
>> הדבר הבא שתראו בספרייה הוא שאנחנו מגדירים סוג חדש בשם "מחרוזת".
קו זה באמת רק יוצר כינוי לטיפוס * char,
כך שזה לא פלא להחדיר סוג המחרוזת החדש עם תכונות
נפוץ הקשורים אובייקטי מחרוזת בשפות אחרות,
כגון אורך.
הסיבה שעשינו זאת היא כדי להגן על מתכנתים חדשים מאת הפרטים העסיסיים
של מצביעים עד שהם מוכנים.
>> החלק הבא של קובץ הכותרת הוא ההכרזה על הפונקציות
שCS50 הספרייה מספקת יחד עם תיעוד.
שים לב לרמת פירוט בהערות כאן.
זה סופר חשוב, כך שאנשים יודעים כיצד להשתמש בפונקציות אלה.
אנו מצהירים, בתורו, מתפקד להציג למשתמש ותווים חוזרים, זוגות, המרחף, ints,
עוד מתגעגע, ומחרוזות, באמצעות טיפוס המחרוזת שלנו.
בעקבות העיקרון של הסתרת מידע,
יש לנו את ההגדרה שלנו בקובץ נפרד יישום ג -. cs50.c--
ממוקם בספריית מקור המשתמש.
אנו מספקים קובץ, כך שאתה יכול להעיף מבט על זה,
ללמוד ממנה, והדר אותו על מחשבים שונים, אם תרצה,
למרות שאנחנו חושבים שעדיף לעבוד על המכשיר במעמד הזה.
בכל אופן, בואו נסתכל על זה עכשיו.
>> פונקציות getchar, GetDouble, GetFloat, GetInt, וGetLongLong
כולם נבנו על גבי פונקצית GetString.
מתברר שכולם בצעו את אותו הדפוס במהות.
הם משתמשים בלולאה תוך כדי לבקש ממשתמש שורה אחת של קלט.
הם חוזרים ערך מיוחד אם תשומות משתמש קו ריק.
הם מנסים לנתח את הקלט של המשתמש כסוג המתאים,
יהיה זה char, כפול, מצוף, וכו '
ואז הם גם מחזירים את התוצאה אם ​​הקלט היה מנותח בהצלחה
או שהם reprompt המשתמש.
>> ברמה גבוהה, אין שום דבר ממש מסובך כאן.
היית יכול לכתוב קוד דומה מובנה עצמך בעבר.
אולי החלק הכי מסתורי למראה הוא שיחת sscanf שמנתח הקלט של המשתמש.
Sscanf הוא חלק ממשפחת המרה לפורמט הקלט.
הוא חי בio.h הסטנדרטי, ואת העבודה שלה היא לנתח מחרוזת C,
על פי פורמט מסוים, אחסון תוצאות הניתוח במשתנה
המסופק על ידי המתקשר.
מאז את פונקציות המרת פורמט הקלט הן פונקציות שימושיות מאוד, המשמשים באופן נרחב
שלא סופר אינטואיטיבית בהתחלה,
נעבור על כמה sscanf עובד.
>> הטיעון הראשון הוא sscanf * char - מצביע לתו.
לפונקציה כדי לעבוד כמו שצריכה,
דמות שצריכה להיות התו הראשון של מחרוזת C,
הסתיים עם אפס \ 0 אופי.
זה הוא המחרוזת לנתח
הטיעון השני לsscanf הוא מחרוזת תבנית,
מועבר בדרך כלל כבקבוע מחרוזת,
ושאולי ראה מחרוזת כזה בעת שימוש printf.
סימן אחוזים במחרוזת התבנית מציין מציין המרה.
הדמות בעקבות שלט אחוזים באופן מיידי,
מציין את סוג C שאנחנו רוצים sscanf להמיר.
בGetInt, אתה רואה שיש ד% וג%.
משמעות הדבר הוא כי sscanf ינסה int עשרוני -% ד - ו char - ג%.
לכל אחד מציין גיור במחרוזת התבנית,
sscanf מצפה טיעון מקביל מאוחר יותר ברשימת הארגומנטים.
טיעון שחייב להצביע על מיקום הולם מוקלד
שבו לאחסון תוצאה מההמרה.
>> הדרך האופיינית לעשות זאת היא ליצור משתנית במחסנית לפני קריאת sscanf
לכל פריט שברצונך לנתח מהמחרוזת
ולאחר מכן להשתמש באופרטור הכתובת - האמפרסנד - להעביר מצביעים
למשתנים האלה בשיחת sscanf.
ניתן לראות כי בGetInt אנחנו עושים בדיוק את זה.
ממש לפני שיחת sscanf, אנו מצהירים בשם int n וג שיחה char במחסנית,
ואנחנו מעבירים אותם למצביעים לשיחת sscanf.
לשים המשתנים הללו במחסנית עדיפה על שימוש בשטח שהוקצה
בערמה עם malloc, מאחר שאתה למנוע את התקורה של שיחת malloc,
ואתה לא צריך לדאוג לדליפת זיכרון.
דמויות לא התחילית של סימן אחוזים אינן מנחות המרה.
במקום זאת הם רק להוסיף למפרט התבנית.
>> לדוגמה, אם מחרוזת התבנית בGetInt הייתה ד% במקום,
sscanf הייתי לחפש את המכתב ואחריו int,
ובזמן זה ינסה להמיר int, זה לא לעשות שום דבר אחר עם.
החריג היחיד לכלל זה הוא ברווחים.
תווי רווח לבנים במחרוזת התבנית להתאים לכל כמות הרווחים -
אפילו בכלל לא.
לכן, זו הסיבה שהתגובה מזכירה אולי עם מובילים ו / או רווח.
לכן, בשלב זה, זה נראה כמו שיחת sscanf אנסה לנתח מחרוזת הקלט של המשתמש
בבדיקה לרווחים מובילים אפשריים,
אחרי int שיומר ומאוחסן במשתנת n int
אחרי כמות מסוימת של שטח ריק, ואחריו תו
מאוחסן במשתנה char.
>> מה לגבי ערך ההחזרה?
Sscanf יהיה לנתח את שורת הקלט מההתחלה ועד הסוף,
לעצור כשהוא מגיע לסוף או כאשר דמות בקלט
אינו תואם את דמות תבנית, או כשהוא לא יכול לעשות את המרה.
זה ערך ההחזרה משמש לבודד בו היא מפסיקה.
אם זה הפסיק, כי זה הגיע לסוף מחרוזת הקלט
לפני ביצוע המרות ולפני שלא הצליח להתאים חלק מהמחרוזת בתבנית,
אז EOF הקבוע המיוחד הוא חזר.
אחרת, זה מחזיר את מספר ההמרות מוצלחות,
אשר יכול להיות 0, 1 או 2, מאז שבקשנו שתי מרות.
במקרה שלנו, אנו רוצים לוודא שמשתמש הקליד בint ורק int.
>> לכן, אנחנו רוצים לחזור sscanf 1. רואה למה?
אם sscanf חזר 0, לא גיורים שנעשו,
כך שהמשתמש הקליד משהו אחר מאשר int בתחילת הקלט.
אם sscanf מחזיר 2, ואז המשתמש לא מקליד אותו כראוי בתחילת הקלט,
אבל אז הם הקלידו באיזו דמות שאינה רווח לבן אחרי
מאז% ג ההמרה הצליחה.
וואו, זה ממש הסבר ארוך לקריאה לפונקציה אחת.
בכל מקרה, אם אתה רוצה מידע נוסף על sscanf והאחים שלו,
לבדוק את הדפים, גוגל, או שניהם.
יש המון אפשרויות מחרוזת תבנית,
ואלה יכולים לחסוך לך הרבה עבודת כפות כאשר מנסים לנתח מחרוזות ב C.
>> הפונקציה האחרונה בספרייה היא להסתכל GetString.
מתברר כי GetString היא פונקציה מסובכת לכתוב כמו שצריך,
למרות שזה נראה כמו משימה כה פשוטה, משותפת.
מדוע זה כך?
ובכן, בואו נחשוב על איך אנחנו הולכים לאחסן את הקו שהמשתמש מקליד פנימה
מאז מחרוזת היא רצף של תווים,
יתכן שעלינו לאחסן אותו במערך במחסנית,
אבל הייתי צריכים לדעת כמה זמן המערך הולך להיות כאשר אנו מצהירים את זה.
כמו כן, אם אנחנו רוצים לשים אותו על הערמה,
אנחנו צריכים לעבור לmalloc את מספר הבתים שאנחנו רוצים להזמין,
אבל זה בלתי אפשרי.
אין לנו מושג כמה תווי המשתמש להקליד
לפני שהמשתמש למעשה אין להקליד אותם.
>> פתרון נאיבי לבעיה זו הוא פשוט להזמין נתח גדול מחלל, אומר,
בלוק של 1000 תווים לקלט של המשתמש,
בהנחה שהמשתמש לא יקליד במחרוזת כך הרבה זמן.
זה רעיון רע משתי סיבות.
ראשית, בהנחה שמשתמשת באופן טיפוסי לא להקליד בחוטים כל כך הרבה זמן,
אתה יכול לבזבז הרבה זיכרון.
במכונות מודרניות, זה לא יכול להיות בעיה אם אתה עושה את זה
בהזדמנות אחת או שתיים מבודדת,
אבל אם אתה לוקח את הקלט של המשתמש בלולאה ואחסון לשימוש מאוחר יותר,
אתה יכול לשאוב את מהירות טונה של זיכרון.
בנוסף, אם התכנית שאתה כותב היא למחשב קטן יותר -
מכשיר כמו טלפון חכם או משהו אחר עם זיכרון מוגבל -
פתרון זה יגרום לבעיות הרבה יותר מהר.
הסיבה השנייה, רצינית יותר לא לעשות את זה היא שזה משאיר את התכנית שלך לפגיע
עם מה שמכונה התקפת גלישת מאגר.
בתכנות, מאגר נמצא בשימוש באופן זמני זיכרון לאחסון נתוני קלט או פלט
אשר במקרה זה הוא הבלוק שלנו 1000-char.
גלישת חוצץ מתרחשת כאשר נתונים נכתב עבר סוף הבלוק.
>> לדוגמה, אם משתמש עושה בפועל בסוג יותר מ 1000 תווים.
ייתכן חווה את זה בטעות כשתכנות עם מערכים.
אם יש לך מערך של 10 ints, שום דבר לא עוצר אותך מלנסות לקרוא או לכתוב
int 15.
אין אזהרות מהדר או טעויות.
התכנית רק מחדלים ישר קדימה וניגש לזיכרון
שם הוא חושב int 15 יהיה, וזה יכול לדרוס משתנים האחרים שלך.
במקרה הגרוע ביותר, אתה יכול להחליף חלק מהתכנית הפנימית שלך
מנגנוני בקרה, גורם לתכנית שלך בעצם ביצוע הוראות שונות
ממה שהתכוונת.
>> עכשיו, זה לא נפוץ לעשות את זה בטעות,
אבל זו טכניקה נפוצה למדי שרעים להשתמש כדי לשבור תוכניות
ולשים את הקוד זדוני במחשבים של אנשים אחרים.
לכן, אנחנו לא יכולים פשוט להשתמש בפתרון הנאיבי שלנו.
אנחנו זקוקים לדרך כדי למנוע מהתוכניות שלנו מלהיות פגיע
להתקפת גלישת מאגר.
כדי לעשות זאת, אנחנו צריכים לוודא שהמאגר שלנו יכול לגדול ככל שאנו קוראים
יותר קלט מהמשתמש.
הפתרון? אנו משתמשים במאגר שהוקצה ערימה.
מכיוון שאנו יכולים לשנות את גודלו באמצעות שינוי גודל פונקצית realloc,
ואנו עוקבים אחר שני מספרים - מדד למקום הפנוי ליד בחיץ
ואורכו או יכולתו של המאגר.
אנו קוראים בתווים ממשתמש אחד בכל פעם באמצעות פונקצית fgetc.
טיעון פונקצית fgetc לוקחת - stdin - הוא התייחסות למחרוזת הקלט הסטנדרטית,
שערוץ קלט preconnected שמשמש להעברת הקלט של המשתמש
מהמסוף לתכנית.
>> בכל פעם שהמשתמש מקליד בדמות חדשה, שנבדוק אם המדד
לחריץ הפנוי הבא בתוספת 1 הוא גדול מיכולתו של המאגר.
1 מגיע בכי אם מדד התשלום הבא הוא 5,
אז אורכו של המאגר שלנו חייב להיות 6 הודות ל0 אינדוקס.
אם נגמרנו לנו מקום במאגר, ואז אנחנו מנסים לשנות את גודלו,
להכפיל אותו כדי שנצמצם את מספר הפעמים שאנחנו גודל
אם המשתמש מקליד במחרוזת ארוכה באמת.
אם המחרוזת קבלה זמן רבה מדי או אם גמר זכרון ערימה,
אנחנו משחררים את חיצנו ואפס תמורה.
>> לבסוף, אנו לצרף char למאגר.
ברגע שהמשתמש להיכנס או להחזיר, מסמן קו חדש,
או char המיוחד - שליטת ד - מה שמסמן סוף הקלט,
אנחנו עושים בדיקה כדי לראות אם משתמש הקליד בעצם בשום דבר בכלל.
אם לא, תחזירו null.
אחרת, משום שהמאגר שלנו הוא כנראה גדול יותר ממה שאנו צריכים,
במקרה הגרוע ביותר זה כמעט פי שניים גדול כמו שאנחנו צריכים
מאז שנכפיל כל פעם אנחנו גודל,
אנחנו עושים עותק חדש של המחרוזת רק באמצעות כמות השטח שאנחנו צריכים.
אנו מוסיפים תוספת 1 לשיחת malloc,
כך שיש מקום לתו המיוחד null השליחות הקטלנית - \ 0,
בו אנו לצרף למחרוזת ברגע שאנחנו להעתיק בשאר דמויות,
באמצעות strncpy במקום strcpy
כך שאנו יכולים לציין בדיוק כמה תווים שאנו רוצים להעתיק.
Strcpy מעתיק עד שהוא פוגע \ 0.
ואז אנחנו משחררים את החיץ שלנו ולהחזיר את העותק למתקשר.
>> מי ידעה שפונקציה כזו כאילו תמימה יכולה להיות כל כך מסובכת?
עכשיו אתה יודע מה הולך לתוך CS50 הספרייה.
>> השם שלי הוא נייט Hardison, וזה CS50.
[CS50.TV]