[Powered by Google Translate] [סעיף 5: פחות נוח]
[נייט Hardison, אוניברסיטת הרווארד]
[זה CS50.] [CS50.TV]
אז ברוך שובך, חבר 'ה.
ברוכים באים לסעיף 5.
בשלב זה, לאחר שהשלים חידון 0 ולאחר שראה את מה שעשית,
אני מקווה שאתה מרגיש ממש טוב כי אני מאוד התרשמתי מהציונים בסעיף זה.
לצופים המקוונים שלנו, היו לנו כמה שאלות
על שתי הבעיות האחרונות על סט הבעיה - או בחידון, ולא.
אז אנחנו הולכים לעבור עליהם ממש מהר, כך שכל אחד רואה את מה שקרה
ואיך לעבור את הפתרון האמיתי ולא רק להצגת הפתרון עצמו.
אנחנו הולכים לעבור על הזוג האחרון של בעיות ממש מהר, 32 ו 33.
רק, שוב, כך שהצופים באינטרנט יכול לראות את זה.
>> אם אתה פונה לבעיה שלך 32, שהוא בעמוד 13,
13 מתוך 16, 32 בעיה הן על כל חילופים.
זה היה הכל על החלפת שני מספרים שלמים.
זה הבעיה שאנחנו הלכנו על כמה פעמים בהרצאה.
וכאן, מה שאנחנו מבקשים ממך לעשות הוא עקבות של זיכרון מהירות.
כדי למלא את הערכים של המשתנים כפי שהם במחסנית
כקוד עובר דרך פונקצית עסקה זו.
בפרט, מה שאנו מחפשים ב-- אני הולך לשים את האייפד הזה -
בפרט, מה שאנחנו מחפשים הוא בקו הזה ממוספר 6 ממש כאן.
וזה ממוספר 6 עבור פשוט רצף עם הבעיה הקודמת.
מה שאנחנו רוצים לעשות הוא להציג או לתייג את מצב הזיכרון
כפי שהוא בתקופה שבה אנו לבצע מספר קו זה 6,
אשר הוא למעשה חזרה מפונקצית החליפין שלנו כאן.
אם אנחנו לגלול למטה כאן, ראינו שאת הכתובות של כל דבר בזיכרון נמסרו לנו.
זה מפתח מאוד, יהיה לנו לחזור אליו ברגע.
ואז כאן למטה בתחתית, היה לנו תרשים זיכרון קטן שאנחנו הולכים להתייחס אליו.
כבר עשיתי את זה על iPad שלי.
אז אני הולך להחליף שוב ושוב בין האייפד ואת הקוד הזה רק עבור הפניה.
>> בואו נתחיל. ראשית, בואו נתמקד בשורות הראשונות של עיקרי כאן.
כדי להתחיל, אנחנו הולכים לאתחל x ו-y ל 1 ל 2.
אז יש לנו שני משתנים שלמים, ששניהם הולכים להיות ממוקמים בערימה.
אנחנו הולכים לשים את 1 ו 2 ב.
אז אם אני מתהפך לiPad שלי, בתקווה, בואו נראים -
שיקוף Apple TV, ושם אנחנו הולכים. אוקיי.
אז אם אני מתהפך לiPad שלי,
אני רוצה לאתחל x ו-y ל 1 ל 2.
אנחנו עושים את זה בפשטות על ידי כתיבת 1 בתיבה המסומנת x
ו2 בתיבה מסומנת y. פשוט למדי.
אז עכשיו בואו נחזור למחשב הנייד, לראות מה קורה הלאה.
אז השורה הבאה זה המקום שבי הדברים מקבלים מסובכים.
אנחנו עוברים את הכתובת של x ו-y ככתובת של הפרמטרים וb לפונקצית swap.
הכתובת של x ואת הכתובת של y הן דברים שאנחנו לא יכולים לחשב
מבלי להתייחס לכדור אלה מצביע כאן למטה.
ולמרבה המזל, שתי נקודתי הכדור הראשונות לספר לנו בדיוק מה התשובות.
הכתובת של x בזיכרון היא 10, ואת הכתובת של Y בזיכרון היא 14.
אז אלה הם הערכים שמקבלים עבר וכבב למעלה בתפקוד החליפין שלנו.
אז שוב, חזור לתרשים שלנו, אני יכול לכתוב ב10
ו14 בב.
עכשיו, בשלב זה שבו אנחנו להמשיך בהחלפה.
אז מתהפך לאחור למחשב הנייד שוב,
אנו רואים שדרך ההחלפה עובדת היא אני dereference 1 וחנות התוצאה בtmp.
אז מפעיל dereference אומר, "היי. פנק את התוכן משתנה ככתובת.
עבור לכל מה שמאוחסן באותה כתובת, ולטעון אותו. "
מה שאתה טוען מתוך משתנה הולך להיות מאוחסן במשתנת tmp שלנו.
לדפדף אחורה לאייפד.
אם נלך לטיפול 10, אנחנו יודעים שכתובת היא 10 x varible
משום שנאמרנו לנו על ידי נקודת הכדור שלנו שהכתובת של x בזיכרון היא 10.
אז אנחנו יכולים ללכת לשם, לקבל את הערך שלו, שהוא 1, כפי שאנו רואים על האייפד שלנו,
ולטעון כי לtmp.
שוב, זה לא את התוכן הסופי.
אנחנו הולכים לעבור ואנחנו נגיע למצב הסופי של התכנית שלנו בסוף.
אבל כרגע, יש לנו את הערך 1 המאוחסן בtmp.
>> ויש שאלה מהירה לכאן.
[אלכסנדר] האם מפעיל dereference - זה פשוט ממש הכוכב בחזית משתנים?
>> כן. אז מפעיל dereference, כפי שאנו מדפדפים חזרה למחשב הנייד שלנו שוב,
זה כוכב ממש בפתח.
במובן זה, שהיא - אתה לעמת אותו עם מפעיל הכפל
אשר דורש שני דברים; מפעיל dereference הוא מפעיל יונארית.
מיושם רק לערך אחד בניגוד למפעיל בינארי,
בך תחול על שני ערכים שונים.
אז זה מה שקורה בתחום הזה.
העמסנו את הערך 1 ואחסנתי אותו לתוך משתנה השלם הזמני שלנו.
השורה הבאה, אנו מאחסנים את התוכן של b ל--
או, לייתר דיוק, אנו מאחסנים את התוכן שהוא ב מצביע אל המקום שבו הוא מצביע.
אם תנתחו את זה מימין לשמאל, אנחנו הולכים ב dereference,
אנחנו הולכים לטפל 14, אנחנו הולכים לתפוס את המספר השלם שהוא שם,
ואז אנחנו הולכים לכתובת 10,
ואנחנו הולכים לזרוק את התוצאה של dereference של b לתוך השטח.
לדפדף אחורה לאייפד שלנו, שבו אנחנו יכולים לעשות את זה קצת יותר קונקרטי,
זה עשוי לעזור אם אני כותב מספרים על כל הכתובות כאן.
כך אנו יודעים כי בy, אנו נמצאים בכתובת 14, x נמצא בכתובת 10.
כאשר אנחנו מתחילים בב, אנו dereference ב, אנחנו הולכים לתפוס את הערך 2.
אנחנו הולכים לתפוס ערך זה משום שזהו הערך שמתגורר בכתובת 14.
ואנחנו הולכים לשים אותו לתוך משתנים שמתגוררים בכתובת 10,
שהוא ממש שם, מקביל לx המשתנה שלנו.
אז אנחנו יכולים לעשות קצת דריסה כאן
איפה אנחנו להיפטר מ1 שלנו, ובמקום זאת אנו כותבים 2.
אז הכול טוב ויפה בעולם, למרות שיש לנו x נדרס עכשיו.
יש לנו לאחסן הערך הישן של x במשתנת tmp שלנו.
אז אנחנו יכולים להשלים את העסקה בשורה הבאה.
לדפדף אחורה למחשב הנייד שלנו.
כעת, כל שנותר הוא להוציא את התוכן שלנו משתנים השלם הזמני
ולאחסן אותם לתוך משתנה שגר בכתובת שהוא מחזיק ב.
אז אנחנו הולכים ליעילות ב dereference כדי לקבל גישה למשתנה
שנמצא בכתובת שמחזיקה ב בזה,
ואנחנו הולכים למלא את הערך שהוא מחזיק tmp לתוכו.
לדפדף אחורה לאייפד פעם נוספת.
אני יכול למחוק ערך זה כאן, 2,
ובמקום שאליו יעתיק את הזכות 1 לתוכו.
אז בשורה הבאה שמבצעת, כמובן -
אם אנחנו מדפדפים חזרה למחשב הנייד - זה 6 נקודות,
המהווה את הנקודה שבה אנחנו רוצים שנהיה לנו התרשים מלא לגמרי.
אז מתהפך לאחור לאייפד עוד פעם אחת, רק כדי שתוכל לראות בתרשים הושלם,
אתה יכול לראות שיש לנו 10 ב, 14 בב, 1 בtmp, 2 בx, ו1 בy.
האם יש שאלות כלשהן לגבי זה?
האם זה הגיוני יותר, אחרי שהלך לדרכו?
הפוך פחות הגיוני? אני מקווה שלא. אוקיי.
>> מצביעים הם נושא מאוד מסובך.
אחד מהחבר 'ה שאנחנו עובדים איתי יש אמרה נפוצה מאוד:
"כדי להבין מצביעים, אתה חייב להבין מצביעים ראשונים."
אני חושב שזה נכון מאוד. זה לוקח זמן להתרגל אליו.
ציור הרבה תמונות, הגרלה של דיאגרמות זיכרון כמו זה מאוד מועיל,
אחרי שאתה עובר דרך דוגמה אחרי דוגמה אחרי דוגמה ו,
זה יתחיל לעשות קצת יותר הגיוני וקצת יותר הגיוני ויותר הגיוני.
לבסוף, יום אחד, תהיה לך את כל זה שולט לגמרי.
כל שאלה לפני שאנחנו עוברים לבעיה הבאה? בסדר.
אז להפוך בחזרה למחשב הנייד.
הבעיה הבאה היא שיש לנו בעית מספר 33 בקובץ I / O.
להתמקד על זה קצת.
בעיה 33 - כן?
>> [דניאל] פשוט הייתה לי שאלה מהירה. כוכב זה, או כוכבי,
זה נקרא ביטול הפניה בעת השימוש בכוכבית.
איך קוראים לזה בעת שימוש באמפרסנד לפני?
>> האמפרסנד לפני הוא הכתובת של מפעיל.
אז בואו לגלול חזרה למעלה.
אופס. אני במצב זום אז אני לא ממש יכול גלילה.
אם נסתכל על הקוד הזה ממש מהר ממש כאן,
שוב, אותו דבר קורה.
אם נסתכל על הקוד הזה כאן, בקו הזה שבו אנו עושים את הקריאה להחלפה,
האמפרסנד פשוט אומר "לקבל את הכתובת שבחיי x משתנים."
כאשר המהדר שלך הידור הקוד שלך,
שיש לו ממש פיזי לסמן את המקום בזיכרון עבור כל המשתנים שלך לחיות.
ואז מה מהדר יכול לעשות אז ברגע שהוא חבר את הכל,
הוא יודע, "אה, אני מניח x בכתובת 10. שמתי y בכתובת 14."
אז זה יכול למלא בערכים אלה עבורך.
אז אתה יכול אז - זה יכול לאחר מכן להעביר את זה ובמעבר & y גם כן.
החבר 'ה האלה מקבל את הכתובת, אבל הם גם, בעת שתעברו אותם לפונקצית swap,
סוג מידע זה, int * זה ממש כאן, אומר למהדר,
"טוב, אנחנו הולכים לפרשנות כתובת זו ככתובתו של משתנה שלם."
ככתובת של int, שהוא שונה מהכתובת של משתנה אופי
בגלל int תופס, במכונת 32-bit, תופס 4 בתים של שטח,
בעוד שהדמות תופסת רק בית 1 של מרחב.
לכן חשוב לדעת גם מה הוא - מה שחי, איזה סוג של ערך
הוא גר בכתובה שעברה מייד פנימה
או את הכתובת שיש לך עסק עם.
באופן זה, אתה יודע כמה בתים של מידע למעשה לטעון מתוך זכרון RAM שלך.
ואז, כן, מפעיל זה dereference, כמו ששאל,
הולך וניגש למידע בכתובת מסוימת.
אז זה אומר, עם משתנה זה כאן, יתייחס לתוכן של ככתובת,
ללכת לכתובת זו, ולשלוף, לטעון לתוך המעבד, עומס להרשמה
הערכים האמיתיים או את התוכן שגר באותה כתובת.
עוד שאלות? אלה שאלות טובות.
זה הרבה מינוח חדש מדי.
זה גם סוג של פאנקי, רואה & ו* במקומות שונים.
>> בסדר.
אז בחזרה לבעיה 33, להגיש I / O.
זו הייתה אחת מהבעיות האלה שאני חושב ששני דברים קרו.
אחד, זה נושא חדש יחסית.
היא הוצגה די מהר לפני החידון,
ואז אני חושב שזה היה כמו סוג של אחת מבעיות המילים האלה במתמטיקה
שם הם נותנים לך הרבה מידע, אבל למעשה אתה לא בסופו של דבר נאלץ להשתמש בטון שלו.
החלק הראשון של בעיה זו הוא מתאר מה הוא קובץ CSV.
עכשיו, קובץ CSV, על פי התיאור, הוא קובץ ערכים מופרד באמצעות פסיקים.
הסיבה אלה הם בכלל מעניינים, והסיבה לך אי פעם להשתמש בם,
הוא, כי, כמה מכם אי פעם נעשה שימוש דברים כמו אקסל?
להבין שרובכם, כנראה, או ישתמשו בשלב כלשהו בחיים שלך.
אתה משתמש במשהו כמו Excel.
על מנת לקבל את הנתונים מתוך גיליון אלקטרוני של Excel או לעשות כל סוג של עיבוד עם זה,
אם אתה רוצה לכתוב תכנית C או תכנית פייתון, תכנית Java,
כדי להתמודד עם נתונים שמאוחסנים שם,
אחת הדרכים הנפוצות ביותר כדי לקבל אותו הוא בקובץ CSV.
ואתה יכול לפתוח את אקסל וכשאתה הולך "השמירה בשם" דיאלוג,
אתה יכול לצאת קובץ CSV בפועל.
>> שימושי לדעת איך להתמודד עם הדברים האלה.
ככה זה עובד הוא שזה דומה ל-- אני מתכוון, זה בעצם מחקה גיליון אלקטרוני,
שם, כפי שאנו רואים כאן, בשמאל הכי החתיכה מאוד,
יש לנו את כל השמות האחרונים.
אז יש לנו מלאן, אז Hardison, ולאחר מכן באודן, MacWilliam, ולאחר מכן צ'אן.
כל השמות האחרונים. ולאחר מכן פסיק מפריד את שמות משפחה מהשמות הפרטיים.
דוד, נייט, רוב, טומי, וZamyla.
אני תמיד לערבב את רובי וטום.
ואז, סוף סוף, העמודה השלישית את כתובות הדוא"ל.
ברגע שאתה מבין את זה, שאר התכנית הוא פשוט למדי ליישום.
מה שעשינו על מנת לחקות את אותו מבנה זה בתכנית C
הוא שאנחנו מנצלים מבנה.
נתחיל לשחק עם אלה קטנים יותר גם כן.
ראינו אותם לקצת הראשון בסט הבעיה 3, כאשר אנחנו עוסקים במילונים.
אבל struct צוות זה מאחסן את שם משפחה, שם פרטי, ודואר אלקטרוני.
בדיוק כמו קובץ CSV אחסון.
אז זה רק המרה מתבנית אחת לאחרת.
יש לנו להמיר, במקרה זה, struct צוות לקו,
קו מופרד באמצעות פסיקים, ככה סתם.
האם זה הגיוני? אתם כל לקחתם את החידון,
לכן אני מניח שלפחות יש לך קצת זמן לחשוב על זה.
>> בפונקציה לשכור, הבעיה מבקשת מאתנו לקחת ב-- זום להכין מזה קצת -
תיקח במבנה צוות, struct צוות, עם השם של,
ולצרף את התוכן שלה לקובץ staff.csv.
מתברר כי זו היא פשוטה למדי לשימוש.
אנחנו סוג שלך לשחק עם הפונקציות האלה קצת יותר היום.
אבל במקרה הזה, פונקצית fprintf היא באמת המפתח.
אז עם fprintf, אנחנו יכולים להדפיס, בדיוק כמו שאתם כבר משתמשים כל printf מונח זה.
אתה יכול printf קו לקובץ.
אז במקום רק עושה שיחת printf הרגילה שבו אתה נותן לו את מחרוזת הפורמט
ואז אתה תחליף את כל המשתנים בטיעונים הבאים,
עם fprintf, הטיעון הראשון שלך הוא במקום קובץ שאתה רוצה לכתוב.
אם היינו מסתכל על זה במכשיר, לדוגמה, אדם fprintf,
אנחנו יכולים לראות את ההבדל בין printf וfprintf.
אני להתקרב כאן קצת.
אז עם printf, אנחנו נותנים לו מחרוזת בתבנית, ולאחר מכן את הטענות הבאות
כל המשתנים להחלפה או להחלפה במחרוזת התבנית שלנו.
ואילו עם fprintf, הטענה הראשונה היא אכן * הקובץ הזה שנקרא נחל.
>> לחזור לגור כאן להשכרה שלנו,
כבר יש לנו הזרם * הקובץ שלנו נפתח עבורנו.
זה מה שהשורה הראשונה זה עושה, זה פותח את קובץ staff.csv,
הוא פותח אותו במצב הוספה, וכל מה שנותר לנו לעשות הוא
לכתוב את מבנה צוות לקובץ.
ו, בואו נראים, אם אני רוצה להשתמש באייפד?
אני אשתמש באייפד. יש לנו חלל - בואו נשים את זה על השולחן כדי שאוכל לכתוב קצת יותר טוב -
לבטל את ההשכרה וזה לוקח בטיעון אחד, מבנה צוות הנקרא s.
קבל הפלטה שלנו, יש לנו קובץ * נקרא קובץ,
יש לנו קו fopen ניתן לנו,
ואני רק כותב את זה כנקודות מכיוון שזה כבר בפדייה.
ואז בשורה הבאה שלנו, אנחנו הולכים לבצע שיחה לfprintf
ואנחנו הולכים להעביר בקובץ שאנחנו רוצים להדפיס,
ואז המחרוזת שלנו בפורמט, אשר -
אני אתן לכם לספר לי איך זה נראה.
מה איתך, סטלה? האם אתה יודע מה החלק הראשון של מחרוזת התבנית נראה?
[סטלה] אני לא בטוח. >> אל יהסס לשאול ג'ימי.
אתה יודע, ג'ימי?
[ג'ימי] האם זה פשוט תהיה אחרון? אני לא יודע. אני לא לגמרי בטוח.
>> אוקיי. מה דעתך על, מישהו מקבל את זה נכון בבחינה?
מספר בסדר.
מתברר כי כאן כל מה שאנחנו צריכים לעשות הוא שאנחנו רוצים כל חלק של מבנה צוות שלנו
ליודפס כמחרוזת לתוך הקובץ שלנו.
אנחנו רק להשתמש בדמות החלפת המחרוזת שלוש פעמים שונות כי יש לנו שם משפחה
אחרי פסיק, ולאחר מכן את השם פרטי ואחריו פסיק,
ולבסוף כתובת הדוא"ל ואחריו - שהוא לא
התאמה על המסך שלי - אבל זה אחרי תו שורה חדשה.
אז אני הולך לכתוב אותו בדיוק שם למטה.
ואז לאחר מחרוזת התבנית שלנו,
אנחנו רק צריכים את ההחלפות, שאנו גישה באמצעות סימון הנקודה
שראינו בסט הבעיה 3.
אנחנו יכולים להשתמש בs.last, s.first, וs.email
להחליף בשלושה הערכים האלה לתוך מחרוזת התבנית שלנו.
אז איך זה הלך? הגיוני?
כן? לא? אולי? אוקיי.
>> הדבר האחרון שאנחנו עושים אחרי שאנחנו מודפסים ואחרי שפתחנו את התיק שלנו:
בכל פעם שאנחנו פתחנו תיק, אנחנו תמיד צריכים לזכור לסגור אותו.
כי אחר אנחנו בסופו של דולף הזיכרון,
באמצעות עד מתארי קובץ.
אז כדי לסגור אותו, שפונקציה שאנחנו משתמשים? דניאל?
[דניאל] fclose? >> Fclose, בדיוק.
אז את החלק האחרון של בעיה זו היה לסגור כראוי את הקובץ, באמצעות פונקצית fclose,
שרק נראה ככה.
לא יותר מדי מטורף.
מגניב.
אז זה בעיה 33 בחידון.
יהיה לנו בהחלט יותר קובץ הקלט / פלט מתקרב.
אנחנו נעשה קצת יותר בהרצאה היום, או בסעיף היום,
כי זה מה שעומד להקים את חלק הארי של pset הקרוב.
בואו לעבור מהחידון בשלב זה. כן?
>> [שארלוט]] למה fclose (קובץ) במקום fclose (staff.csv)?
>> אה. כי מסתבר ש-- כך השאלה, שהוא אחד גדול,
לכן, כאשר אנו כותבים fclose, אנחנו כותבים משתנים fclose (קובץ) כוכב
בניגוד לשם הקובץ, staff.csv? האם זה נכון? כן.
אז בואו נסתכל. אם אני עובר בחזרה למחשב הנייד שלי,
ובואו נסתכל על פונקצית fclose.
אז פונקצית fclose סוגרת זרם וזה לוקח במצביע לזרם שאנחנו רוצים לסגור,
בניגוד לשם הקובץ עצמו שאנחנו רוצים לסגור.
וזה בגלל שמאחורי הקלעים, בעת ביצוע שיחה לfopen,
כאשר אתה פותח את קובץ, אתם למעשה הקצאת זיכרון לאחסון מידע אודות הקובץ.
אז יש לך מצביע קובץ המכיל מידע אודות הקובץ,
כמו שזה פתוח, הגודל שלו, שבו אתה נמצא כעת בקובץ,
כך שתוכל להפוך את הקריאה וכתיבה לשיחות שמקום מסוים בתוך הקובץ.
בסוף אתה סוגר את המצביע במקום לסגור את שם הקובץ.
>> כן? [דניאל] לכן, כדי להשתמש בהשכרה, היית אומר - איך זה לקבל קלט מהמשתמש?
fprintf האם להתנהג כמו GetString במובן זה שהיא פשוט יחכה לקלט מהמשתמש
ותשאל אותך לסוג זה - או לחכות לך להקליד שלושה הדברים האלה ב?
או שאתה צריך להשתמש במשהו ליישם לשכור?
>> כן. אז אנחנו לא - השאלה הייתה, איך אנחנו מקבלים את קלט המשתמש
כדי ליישם לשכור? ומה יש לנו כאן הוא מתקשר מהשכרה,
עבר בstruct צוות הזה עם כל נתונים המאוחסנים בstruct כבר.
אז fprintf יכול פשוט לכתוב את הנתונים ישירות לקובץ.
אין הצורך להמתין לקלט מהמשתמש.
המשתמש כבר נתן קלט על ידי לשים את זה כמו שצריך בstruct צוות הזה.
ודברים, כמובן, ישברו אם כל המצביעים האלה היו ריקים,
לכן אנחנו לגלול חזרה לכאן, ואנחנו מסתכלים על struct שלנו.
יש לנו מחרוזת האחרונה, מחרוזת הראשונה, דוא"ל מחרוזת.
עכשיו אנחנו יודעים שכל אלה באמת, מתחת למכסת המנוע, משתנים * char.
זה עשוי או לא עשוי להיות הצבעה על null.
הם עשויים יצביעו לזיכרון על הערימה,
אולי זיכרון על המחסנית.
אנחנו לא באמת יודעים, אבל אם כל אחד מהמצביעים האלה הם אפס, או לא חוקיים,
כי בהחלט לקרוס הפונקציה לשכור שלנו.
זה היה משהו שהיה סוג של מעבר להיקף של הבחינה.
אנחנו לא נצטרך לדאוג לזה.
גדול. אוקיי. כל כך מרגש בחידון.
>> בואו נסגור את הבחור הזה, ואנחנו הולכים להסתכל על pset 4.
אז אם אתם מסתכלים על מפרט pset, ברגע שאתה יכול לגשת אליו, cs50.net/quizzes,
אנחנו הולכים לעבור כמה מבעיות הסעיף היום.
אני גלילה למטה - קטע של שאלות מתחיל בעמוד השלישי של מפרט pset.
והחלק הראשון מבקש ממך ללכת ולצפות בהפניית הקצרה וצינורות.
וזה היה סוג של קצר מגניב, מראה לך כמה טריקים חדשים, מגניבי שורת פקודה שאתה יכול להשתמש.
ואז אנחנו צריכים גם לי כמה שאלות עבורך.
זו שאלה ראשונה על זרמים, כדי שprintf כותב כברירת מחדל,
אנחנו סוג של נגענו בקצת לפני רגע.
זה fprintf שבדיוק דבר לוקח בזרם * קובץ כטענתה.
fclose לוקח בזרם * קובץ, כמו גם,
וערך ההחזרה של fopen נותן לך זרם * קובץ גם כן.
הסיבה שלא ראתה אותם קודם לכן, כאשר יש לנו עסק עם printf
בגלל printf יש זרם ברירת מחדל.
וזרם ברירת המחדל שאליו הוא כותב
תוכל לברר על בטווח הקצר.
אז בהחלט תסתכל על זה.
>> בסעיף של היום, אנחנו הולכים לדבר קצת על GDB,
מאז מוכר יותר אתה עם זה, בפועל יותר אתה מקבל עם זה,
היכולת טובה יותר אתה תהיה ממש לצוד באגים בקוד שלך.
זה מאיץ את התהליך של ניפוי עד מאוד.
זאת באמצעות printf, בכל פעם שאתה עושה כי יש לך להדר את הקוד שלך,
אתה צריך להפעיל אותו שוב, לפעמים יש לך להעביר את שיחת printf מסביב,
הערה את הקוד, זה פשוט לוקח זמן.
המטרה שלנו היא לנסות ולשכנע אותך שעם GDB, אתה יכול למעשה
דבר printf בכל נקודה בקוד שלך ואתה לא צריכים להדר מחדש את זה.
אתה לא צריך להתחיל ולשמור מנחש איפה printf הבא.
הדבר הראשון שצריך לעשות הוא להעתיק את הקו הזה ולקבל את קוד סעיף הנחה של האינטרנט.
אני מעתיק את שורת הקוד שאומרת, "http://cdn.cs50.net wget".
אני הולך להעתיק אותו.
אני הולך לעבור למכשיר שלי, להקטין את התצוגה כך שתוכל לראות מה אני עושה,
ההדבקה לשם, וכשאני מקיש Enter, פקודת wget זה ממש היא לקבל אינטרנט.
זה הולך למשוך למטה את הקובץ הזה מהאינטרנט,
וזה הולך לשמור אותו לספרייה הנוכחית.
עכשיו, אם אני ברשימת הספרייה הנוכחית שלי אתה יכול לראות שיש לי קובץ section5.zip זה ממש שם.
הדרך להתמודד עם הבחור הזה היא לפתוח את זה,
שאתה יכול לעשות בשורת הפקודה, בדיוק כמו זה.
Section5.zip.
שתהיה לפתוח אותו, ליצור את התיקייה בשבילי,
לנפח את כל התכולה, לשים אותם שם.
אז עכשיו אני יכול ללכת לספריית סעיף 5 שלי באמצעות פקודת cd.
נקה את המסך בעזרת ברור. אז לנקות את המסך.
עכשיו יש לי מסוף נקי נחמד להתמודד איתו.
>> עכשיו, אם אני ברשימה כל הקבצים שאני רואה במדריך זה,
אתה רואה שיש לי ארבעה קבצים: buggy1, buggy2, buggy3 וbuggy4.
יש לי גם קבצי ג המתאימים..
אנחנו לא הולכים להסתכל על הקבצים. ג לעת עתה.
במקום זאת, אנחנו הולכים להשתמש בם כאשר אנו פותחים את GDB.
שמרנו עליהם בסביבה כך שיש לנו גישה לקוד המקור הממשי כאשר אנו משתמשים GDB,
אבל מטרתו של חלק זה של המדור היא להתעסק עם GDB
ולראות איך אנחנו יכולים להשתמש בו כדי להבין מה השתבשנו עם כל אחת מארבע תוכניות כרכרה אלה.
אז אנחנו פשוט הולכים סביב החדר ממש מהר,
ואני הולך לשאול מישהו לרוץ אחת מתוכניות הכרכרה,
ואחר כך תלכו כקבוצה דרך GDB, ונראה מה אנחנו יכולים לעשות כדי לתקן את התוכניות הללו,
או לפחות לזהות מה השתבש בכל אחד מהם.
בואו נתחיל כאן עם דניאל. האם אתה מפעיל buggy1? בואו לראות מה קורה.
[דניאל] זה אומר שיש תקלת יישום. >> כן. בדיוק.
אז אם אני מפעיל buggy1, אני מקבל את אשמת צינוק.
בשלב זה, אני יכול ללכת ולפתוח buggy1.c,
לנסות ולהבין מה השתבש,
אבל אחד הדברים המעצבנים ביותר על שגיאת אשמת הצינוק הזה
הוא שזה לא אומר לך באיזה שורה של הדברים שלמעשה התכנית השתבשה ונשברה.
אתה סוג של צריך להסתכל על הקוד
ולהבין באמצעות ניחוש ולבדוק או printf כדי לראות מה השתבש.
אחד הדברים הכי המגניבים על GDB הוא שזה ממש ממש קל
כדי להבין את הקו שבו תכנית הקריסות שלך.
זה לגמרי שווה את זה כדי להשתמש בו, גם אם רק בשביל זה.
אז לאתחל את GDB, אני מקליד GDB, ואז לתת לו את הנתיב אל קובץ ההפעלה שאני רוצה לרוץ.
הנה אני מקליד gdb ./buggy1.
על Enter. נותן לי את כל מידע על זכויות יוצרים זה,
וכאן תוכל לראות את הקו הזה שאומר, "סמלי קריאה מהבית / /
jharvard/section5/buggy1. "
ואם הכל ילך כשורה, תראה אותו להדפיס את הודעה שנראית כך.
זה יהיה לקרוא סמלים, הוא יגיד "אני קורא סמלים מקובץ ההפעלה שלך,"
ואז זה יהיה המסר הזה "עשה" כאן.
אם אתה רואה איזו וריאציה אחרת של זה, או שאתה רואה זה לא הצליח למצוא את הסמלים
או משהו כזה, מה שזה אומר זה שאתה פשוט לא חברת ההפעלה שלך כראוי.
כאשר אנו עורכים תוכניות לשימוש עם GDB, אנחנו צריכים להשתמש בדגל מיוחד שגרם,
וזה נעשה על ידי ברירת מחדל אם לקמפל את התוכניות שלך, פשוט על ידי ההקלדה להפוך
או להפוך את המרכבה או להפוך להתאושש, אף אחד מאלה.
אבל אם אתה קומפילציה ידנית עם קלאנג, אז אתה צריך ללכת ובכולל דגל ש- G.
>> בשלב זה, עכשיו שיש לנו GDB הפקודה,
זה די פשוט להפעלת התכנית.
אנחנו יכולים להקליד ריצה, או שאנחנו יכולים פשוט להקליד r.
רוב פקודות gdb יכולות להיות מצוינות.
בדרך כלל רק לאחד או כמה אותיות, וזה די נחמד.
אז סעד, אם תקליד r ועל Enter, מה קורה?
[סעד] יש לי SIGSEGV, אשמת פילוח, ואז כל הקשקוש הזה.
>> כן.
כמו שאנחנו רואים על המסך כרגע, וכמו הסעד אמר,
כאשר אנו מקלידים ריצה או r ועל Enter, אנחנו עדיין מקבלים את אותה אשמת צינוק.
אז באמצעות GDB לא פותר את הבעיה שלנו.
אבל זה נותן לנו קצת קשקושים, ומתברר שזה קשקוש
בעצם אומר לנו איפה זה קורה.
כדי לנתח קצת זה, קצת ראשון הוא הפונקציה שבה הכול משתבש.
יש __ strcmp_sse4_2 הזה, וזה אומר לנו שזה קורה בקובץ זה
קרא sysdeps/i386, כל זה, שוב, סוג של בלגן - אבל קו 254.
זה קצת קשה לנתח. בדרך כלל כשאתה רואה דברים כאלה,
זה אומר שזה שגיאה בצינוק באחת מספריות המערכת.
אז משהו לעשות עם strcmp. אתם ראיתם strcmp לפני.
לא משוגע מדי, אבל האם זה אומר שstrcmp שבור או שיש בעיה עם strcmp?
מה אתה חושב, אלכסנדר?
[אלכסנדר] האם זה - הוא 254 הקו? ו-- לא בינארי, אבל זה לא מתקרותיהם,
ואז יש עוד שפה לכל פונקציה. האם זה 254 שבפונקציה או -?
>> זה קו 254. זה נראה כמו ב. קובץ של זה, כך שזה קוד הרכבה של קרוב לוודאי.
>> אבל, אני מניח שהדבר הדחוף יותר הוא, בגלל שאנחנו קבלנו אשמת צינוק,
וזה נראה כאילו הוא בא מפונקצית strcmp,
האם זה מרמז, אפוא, שstrcmp הוא שבור?
זה לא אמור לפגוע, בתקווה. אז רק בגלל שיש לך תקלת פילוח
באחד מתפקידיה של המערכת, בדרך כלל זה אומר שאתה פשוט לא קראת לזה בצורה נכונה.
הדבר המהיר ביותר לעשות כדי להבין מה באמת קורה
כשאתה רואה משהו מטורף כמו זה, בכל פעם שאתה רואה אשמת צינוק,
במיוחד אם יש לך תכנית שמשתמשת ביותר מסתם ראשי,
הוא להשתמש backtrace.
אני לקצר backtrace באמצעות כתיבת BT, בניגוד למילת backtrace המלאה.
אבל שרלוט, מה קורה כאשר אתה מקליד BT ולחץ על Enter?
[שארלוט] זה מראה לי שני קווים, 0 קו וקו 1.
>> כן. אז קו 0 וקו 1.
אלו הן מסגרות מחסנית בפועל שהיו כרגע במשחק, כאשר התכנית שלך התרסקה.
החל מהמסגרת העליונה, 0 המסגרת, והולך לתחתונים ביותר, שהוא 1 מסגרת.
המסגרת העליונה ביותר שלנו היא מסגרת strcmp.
אתה יכול לחשוב על זה כדומה לבעיה הזאת פשוט עשתה בחידון עם המצביעים,
המקום ממנו להחליף מסגרת ערימה על גבי ערימת המסגרת ראשית,
והיו לנו את המשתנים שההחלפה הייתה בשימוש על גבי המשתנים עיקריים שמשתמשים.
כאן ההתרסקות שלנו קרתה בפונקציה שלנו strcmp, שנקרא על ידי הפונקציה העיקרית שלנו,
וbacktrace הוא נותן לנו לא רק את הפונקציות שבו דברים כושלים,
אבל זה גם אומר לנו שהכול בו התקשר מ.
אז אם אני לגלול על קצת יותר ימינה,
אנו יכולים לראות שכן, היינו על קו 254 של קובץ strcmp-sse4.s זה.
אבל השיחה נעשתה בbuggy1.c, 6 בקו.
אז זה אומר שאנחנו יכולים לעשות - הוא שאנחנו פשוט יכולים ללכת לבדוק ולראות מה קורה
בbuggy1.c, 6 בקו.
שוב, יש כמה דרכים לעשות זאת. אחת היא לצאת מGDB
או שיש לך הקוד לפתוח בחלון אחר וקישור.
זה, כשלעצמו, הוא די שימושי כי עכשיו אם אתה בשעתי עבודה
ויש לך תקלת בידוד, וTF תוהה היכן הכל עומד להישבר
אתה יכול פשוט לומר, "אה, קו 6. אני לא יודע מה קורה,
אבל משהו בקו 6 גורם לתכנית שלי להישבר ".
הדרך אחרת לעשות את זה היא שאתה יכול להשתמש בפקודה זו בשם רשימה בGDB.
אתה יכול גם לקצר אותו באני.
אז אם פגענו אני, מה שאנחנו מקבלים כאן?
אנחנו מקבלים כל מיני דברים מוזרים.
זהו קוד ההרכבה בפועל
כי הוא בstrcmp_sse4_2.
זה נראה סוג של פאנקי,
והסיבה שאנחנו מקבלים זה בגלל שעכשיו,
GDB יש לנו במסגרת 0.
>> אז בכל פעם שאנחנו מסתכלים על משתנים, כל פעם שאנחנו מסתכלים על קוד מקור,
אנחנו מסתכלים קוד מקור הנוגע למסגרת החבילה שאנו פנימה
אז כדי לקבל שום דבר משמעותי, אנחנו צריכים
לעבור למסגרת מחסנית שהגיונית יותר.
במקרה זה, מסגרת המחסנית העיקרית תגרום מעט יותר,
מכיוון שהיה למעשה קוד שאנחנו כתבנו.
לא קוד strcmp.
הדרך בה אתה יכול לנוע בין מסגרות, במקרה זה, כי יש לנו שתיים,
יש לנו 0 ו 1,
אתה עושה את זה עם פקודות ולמטה.
אם אני מזיז את פריים אחד,
עכשיו אני במסגרת המחסנית הראשית.
אני יכול לנוע למטה כדי לחזור למקום שהייתי,
לעלות שוב, לרדת שוב, ולעלות שוב.
אם אי פעם תעשה את התכנית שלך בGDB, אתה מקבל התרסקות, אתה מקבל backtrace,
ואתה רואה שזה בקובץ מסוים שאתה לא יודע מה קורה.
אתה מנסה רשימה, הקוד לא נראה לך מוכר,
יסתכל במסגרות שלך ולהבין איפה אתה נמצא.
אתה כנראה במסגרת המחסנית הלא הנכונה.
או לפחות אתה במסגרת חבילה שהוא לא אחד שאתה באמת יכול ניפוי.
עכשיו שאנחנו במסגרת המחסנית המתאימה, אנחנו בראשיים,
עכשיו אנחנו יכולים להשתמש בפקודת הרשימה כדי להבין מה היה הקו.
ואתה יכול לראות את זה, זה הדפיס את זה בשבילנו ממש כאן.
אבל אנחנו יכולים להכות את הרשימה כולה, ונותנת לנו רשימת תדפיס זה נחמד
של קוד המקור הממשי שקורה כאן.
>> בפרט, אנו יכולים להסתכל בקו 6.
אנחנו יכולים לראות מה קורה כאן.
וזה נראה כאילו אנחנו עושים את השוואת מחרוזת
בין מייתרים "CS50 סלעים" וargv [1].
משהו בעניין הזה היה מתרסק.
אז מסים, האם יש לך מחשבות על מה שאולי קרה כאן?
[מסים] אני לא יודע למה היא מתרסקת. >> אתה לא יודע למה זה מתרסק?
ג'ימי, כל מחשבות?
[ג'ימי] אני לא לגמרי בטוח, אבל בפעם האחרונה שהשתמשנו במחרוזת להשוות,
או strcmp, היו לנו כמו שלושה מקרים שונים תחתו.
לא היה לנו ==, אני לא חושב, ממש שבשורה הראשונה.
במקומו היה מופרד לשלוש, ואחד היה == 0,
אחד היה <0, אני חושב, ואחד היה> 0.
אז אולי משהו כזה? >> כן. אז יש את הבעיה
מאנחנו עושים השוואה בצורה נכונה?
סטלה? כל מחשבות?
[סטלה] אני לא בטוח. >> לא בטוח. דניאל? מחשבות? אוקיי.
מתברר מה שקורה כאן הוא כשנגמרנו התכנית
ויש לנו אשמת הצינוק, כשנהל את התכנית בפעם הראשונה, דניאל,
האם לתת לו את כל הארגומנטים בשורת פקודה?
[דניאל] מס >> מס במקרה זה, מהו הערך של argv [1]?
>> אין ערך. >> ימני.
ובכן, אין שום ערך מחרוזת מתאימה.
אבל יש איזה שהוא ערך. מהו הערך שמקבל מאוחסן שם?
>> ערך זבל? >> זה או ערך אשפה או, במקרה זה,
הסוף של מערך argv תמיד הסתיים עם אפס.
אז מה בעצם קבל מאוחסנת שם הוא ריק.
הדרך אחרת לפתור את זה, ולא לחשוב דרכו,
הוא לנסות להדפיס אותו החוצה.
זה המקום בו אני אומר שבאמצעות GDB הוא גדול,
כי אתה יכול להדפיס את כל המשתנים, את כל הערכים שברצונך
באמצעות פקודת p-הגנדרנית השימושית הזה.
אז אם אני מקליד עמ 'ואז אני מקליד את הערך של משתנה או את שמו של משתנה,
אומרים, argc, אני רואה שargc הוא 1.
אם אני רוצה להדפיס argv [0], אני יכול לעשות זאת סתם כך.
וכמו שראינו, argv [0] הוא תמיד את השם של התכנית שלך,
תמיד את שמו של קובץ ההפעלה.
כאן אתה רואה אותו יש לו את שם הנתיב המלא.
אני יכול גם להדפיס את argv [1] ולראות מה קורה.
>> יש לנו כאן סוג של ערך המיסטי.
יש לנו 0x0 זה.
זכור בתחילת הכהונה כשדברנו על מספרים הקסדצימליים?
או שהשאלה הקטנה בסוף pset 0 על אופן מייצג 50 ב hex?
הדרך בה אנו כותבים מספרי hex ב CS, רק כדי לא לבלבל את עצמנו
עם מספרים עשרוניים, הוא תמיד להקדים אותם עם 0x.
אז קידומת 0x זה תמיד רק אומרת לפרש את המספר הבא כמספר הקסדצימלי,
לא כמחרוזת, ולא כמספר עשרוני, ולא כמספר בינארי.
מאז המספר 5-0 הוא מספר חוקי בהקסדצימלי.
וזה מספר עשרוני, 50.
כך שזה רק איך אנחנו disambiguate.
אז 0x0 אמצעי 0 הקסדצימלי, שהיא גם נקודה עשרונית 0, 0 בינאריים.
זה רק ערך 0.
מסתבר שזה מה שnull הוא, למעשה, לזכרו.
ריק הוא בדיוק 0.
כאן, אלמנט מאוחסן בargv [1] הוא ריק.
אז אנחנו מנסים להשוות המחרוזת שלנו "CS50 הסלעים" למחרוזת ריקה.
אז ביטול הפניה ריקה, מנסה לגשת לדברים בריק,
אלה בדרך כלל הולכים לגרום לסוג מסוים של אשמת פילוח או דברים רעים אחרים לקרות.
ומתברר שstrcmp לא בודק
או אם אתה לא כבר עברת בערך זה ריק.
במקום זאת, הוא פשוט הולכת קדימה, מנסה לעשות את שלו,
ואם זה בחור, תקלות, זה צינוק תקלות, וזו הבעיה שלך. אתה צריך ללכת לתקן את זה.
מהר מאוד, איך ניתן לפתור בעיה זו? שרלוט?
[שארלוט] אתה יכול לבדוק אם שימוש.
אז אם argv [1] הוא ריק, == 0, ואז לחזור 1, או משהו [לא מובן].
>> כן. אז זהו אחד דרך מצוינת לעשות את זה, כפי שאנו יכולים לבדוק,
ערך שאנחנו עומדים לעבור לstrcmp, argv [1], הוא זה null?
אם null זה, אז אנחנו יכולים לומר בסדר, להפיל.
>> דרך נפוצה יותר לעשות זאת היא להשתמש בערך argc.
אתה יכול לראות ממש כאן בתחילה ראשית,
אנחנו השמטנו שהבדיקה הראשונה, כי בדרך כלל אנחנו עושים כאשר אנחנו משתמשים בטיעונים של שורת פקודה,
אשר הוא לבדוק אם ערך argc שלנו הוא מה שאנחנו מצפים.
במקרה זה, אנחנו מצפים לפחות שני טיעונים,
השם של התכנית ועוד אחד אחר.
מכיוון שאנחנו עומדים להשתמש בטיעון השני ממש כאן.
כך שיש סוג מסוים של מבחן מראש, לפני שיחת strcmp
כי בדיקות או לא argv הוא לפחות 2, היינו גם לעשות את אותו דבר.
אנחנו יכולים לראות אם זה עובד על ידי הפעלת התכנית שוב.
אתה תמיד יכול להפעיל מחדש את התכנית שלך בתוך GDB, שהוא ממש נחמד.
אתה יכול לרוץ, וכשאתה עובר בטיעונים לתכנית שלך,
שתעביר אותם בעת שיחה לרוץ, לא כאשר אתה לאתחל את GDB.
כך תוכל לשמור על פניית התכנית שלך עם טיעונים שונים בכל פעם.
אז לרוץ, או שוב, אני יכול להקליד r, ובואו נראים מה יקרה אם תקליד "שלום".
תמיד ישאל אותך אם אתה רוצה להתחיל אותו מההתחלה שוב.
בדרך כלל, אתה רוצה להתחיל אותו מההתחלה שוב.
ובנקודה הזאת, זה מחדש את זה שוב, זה מדפיס את
התכנית שאנחנו רצים, buggy1, בטענת שלום,
וזה מדפיס את התקן זה, הוא אומר, "אתה מקבל D," פרצוף עצוב.
אבל אנחנו לא אשמי צינוק. זה אומר תהליך שיצא באופן נורמלי.
אז זה נראה די טוב.
לא באשמה יותר צינוק, שעשינו את זה בעבר,
כך זה נראה כמו שאכן היה באג אשמת הצינוק שאנחנו מקבלים.
למרבה הצער, זה אומר לנו שאנחנו מקבלים ד
>> אנחנו יכולים לחזור ולהסתכל על הקוד ולראות מה קורים שם
כדי להבין מה היה - מדוע הוא אומר לנו שיש לנו ד
בואו לראות, כאן היה זה printf אומר שיש לך ד '
אם תקלידו רשימה, כפי שאתה לשמור את רשימת הקלדה, זה שומר iterating למטה דרך התכנית שלך,
כך יראו לך את השורות הראשונות של התכנית שלך.
אז יראה לך את השורות הבאות, ואת הנתח הבא והנתח הבא.
ואנסה שוב ושוב לרדת.
ועכשיו אנחנו נתחיל את "קו המספר 16 הוא מחוץ לטווח."
בגלל זה יש 15 קווים בלבד.
אם להגיע לנקודה הזו ושלך תוהה, "מה עליי לעשות?" אתה יכול להשתמש בפקודת העזרה.
השתמש בעזרה ואז לתת לו את השם של פקודה.
ואתה רואה את GDB נותן לנו כל מיני דברים כאלו.
זה אומר, "עם כל ויכוח, מפרט עוד עשר שורות אחרי או סביב הרישום הקודם.
רשימה - מפרטת את העשר שורות לפני - "
אז בואו נסו להשתמש מינוס רשימה.
ושמפרט את 10 השורות קודמות, אתה יכול לשחק עם רשימה קצת.
אתה יכול לעשות רשימה, רשימה -, אתה יכול אפילו לתת לי רשימת מספר, כמו רשימה 8,
ואציין את 10 שורות סביב קו 8.
ואתה יכול לראות מה קורה כאן הוא שיש לך פשוט אם אחר.
אם תקליד בCS50 סלעים, זה מדפיס את "אתה מקבל א '"
אחר זה מדפיס "אתה מקבל ד"
עיירת באסה. בסדר. כן?
>> [דניאל] אז כשניסיתי לעשות CS50 סלעים בלי המרכאות,
זה אומר "אתה מקבל ד"
אני צריך את הציטוטים כדי לקבל אותו לעבודה; מדוע זה כך?
>> כן. מתברר שכאשר - זה עוד פרט קטן קצת כיף -
כשאתה מפעיל את התכנית, אם אנחנו מפעילים את זה ואנחנו להקליד CS50 סלעים,
בדיוק כמו שדניאל אומר שהוא עשה, ותקיש Enter,
זה עדיין אומר שאנחנו מקבלים ד
והשאלה היא, למה זה?
ומתברר שגם המסוף וGDB לנתח אותם כשתי טענות נפרדות.
כי כאשר יש מקום, זה משתמע כ
הוויכוח הראשון הסתיים; הטיעון הבא הוא עומד להתחיל.
דרך לשלב אותם לתוך שתיים, או מצטער, לטיעון אחד,
הוא להשתמש בציטוטים.
אז עכשיו, אם אנחנו שמים אותו במרכאות ולהפעיל אותו שוב, אנחנו מקבלים א
אז רק כדי לסכם, ללא מרכאות, CS50 וסלעים מנותחים כשתי טענות נפרדות.
עם ציטוטים, זה מנותח כטיעון אחד לגמרי.
>> אנחנו יכולים לראות את זה בנקודת עצירה.
עד כה אנו כבר מפעילים התכנית שלנו, וזה כבר פועל
עד שאחד אותו הבחור, שגיאות או ליקויי להיטים
או עד שהוא יצא והכל היה בסדר גמור.
זה לא בהכרח הדבר המועיל ביותר, כי לפעמים
יש לך שגיאה בתכנית שלך, אבל זה לא גורם לתקלות פילוח.
זה לא גורם לתכנית שלך להפסיק או משהו כזה.
הדרך להגיע GDB כדי להשהות את התכנית שלך בנקודה מסוימת
הוא לקבוע נקודת עצירה.
אתה יכול לעשות זאת על ידי קביעת נקודת עצירה בשם פונקציה
או שאתה יכול להגדיר נקודת עצירה בקו מסוים של קוד.
אני אוהב להגדיר נקודתי עצירה על שמות פונקציות, כי - קל לזכור,
אם אתה באמת להיכנס ולשנות את קוד המקור שלך וקצת,
אז נקודת העצירה שלך היא בעצם תישאר באותו המקום בתוך הקוד שלך.
לעומת זאת, אם אתה משתמש במספרי שורות, ולשנות את מספרי השורות
בגלל שאתה להוסיף או למחוק קצת קוד, ולאחר מכן נקודתי העצירה שלך דפוקות לחלוטין.
אחד הדברים הנפוצים ביותר שאני עושה הוא להגדיר נקודת עצירה בפונקציה העיקרית.
לעתים קרובות אני לאתחל את GDB, אני סוג B ראשי, על Enter, ושאקבע את נקודת עצירה
על הפונקציה העיקרית שפשוט אומרת, "השהה את התכנית ברגע שאתה מתחיל לרוץ",
וככה, כשאני מפעיל את התכנית שלי, למשל, CS50 סלעים כשני טיעונים
והקש על Enter, הוא מגיע לתפקידו העיקרי והוא עוצר ממש בקו הראשון,
ממש לפני שמעריך את תפקוד strcmp.
>> מאז אני מושהה, עכשיו אני יכול להתחיל להתבטל ולראות מה קורה
עם כל המשתנים השונים, כי הם עברו לתכנית שלי.
כאן אני יכול להדפיס את argc ולראות מה קורה.
ראה שargc הוא 3, כי יש בזה 3 ערכים שונים בו.
יש לו את שמה של התכנית, יש לזה הנימוק הראשון והנימוק השני.
אנחנו יכולים להדפיס את אלה מלהסתכל argv [0], argv [1], וargv [2].
אז עכשיו אתה יכול גם לראות מדוע שיחת strcmp זה הולך להיכשל,
כי אתה רואה שזה לא נפרד CS50 והסלעים לשתי טענות נפרדות.
בשלב זה, לאחר שפגעת בנקודת עצירה, אתה יכול להמשיך ולעבור דרך התכנית שלך
שורה אחרת שורה, בניגוד לתכנית שלך מתחיל שוב.
אז אם אתה לא רוצה להתחיל את התכנית שוב ופשוט להמשיך מכאן,
אתה יכול להשתמש בפקודה תמשיך ותמשיך יהיה להפעיל את התכנית עד הסוף.
בדיוק כמו שעשה כאן.
עם זאת, אם אני מחדש את התכנית, CS50 סלעים, זה פוגע נקודת העצירה שלי שוב,
והפעם, אם אני לא רוצה סתם ללכת כל הדרך עד סוף התכנית,
אני יכול להשתמש בפקודה הבאה, שאני גם לקצר עם n.
וזה יהיה צעד דרך קו התכנית על ידי קו.
אז אתה יכול לראות איך דברים לבצע, כשינוי משתנה, כדברים להתעדכן.
וזה די נחמד.
הדבר המגניב האחר הוא לא חוזר על אותה הפקודה שוב ושוב ושוב,
אם אתה רק על Enter - אז הנה אתה רואה אני לא הדפסתי משהו -
אם אני רק Enter, היא עלולה לחזור על הפקודה הקודמת,
או פקודת GDB הקודמת שאני פשוט מכניס פנימה
אני יכול לשמור להכות זן והוא ימנע דריכה דרך שורת הקוד שלי בשורה.
הייתי ממליץ לכם ללכת לבדוק את תוכניות כרכרה האחרות גם כן.
אין לנו זמן לעבור את כולם היום בסעיף.
קוד המקור הוא שם, כך שאתה יכול לראות סוג של מה קורה
מאחורי הקלעים אם אתה מקבל באמת תקוע,
אבל לכל הפחות, התאמן באתחול GDB,
הפעלת התכנית עד שהוא נשבר עליך,
מקבל backtrace, להבין מה פונקצית ההתרסקות הייתה ב,
מה זה היה על קו, תוך הדפסה כמה ערכים משתנים,
רק כדי שתקבל תחושה שלו, כי זה יהיה באמת לעזור לך בהמשך.
בשלב זה, אנחנו הולכים לפרוש מGDB, שאתה משתמש לפרוש או רק ש.
אם התכנית שלך היא באמצע הריצה עדיין, וזה לא יצא,
תמיד ישאל אותך, "האם אתה בטוח שאתה באמת רוצה לפרוש?"
אתה פשוט יכול להכות כן.
>> עכשיו אנחנו הולכים להסתכל על הבעיה הבאה שיש לנו, שהיא התכנית לחתולים.
אם אתם צופים בהפניית קצרה וצינורות, תראה שמשתמש טומי תכנית זו
כי בעצם מדפיס את כל הפלט של קובץ על גבי המסך.
אז אם אני מפעיל את החתול, זה הוא למעשה תכנית מובנית למכשיר,
ואם יש לך מקינטוש אתה יכול לעשות את זה ב-Mac שלך יותר מדי, אם אתה פותח את המסוף.
ואנחנו - חתול, יניח, cp.c, והקישו על Enter.
מה זה עשה, אם אנחנו לגלול למעלה קצת ולראות איפה אנחנו רצנו קו,
או שבו אנו רצים את פקודת החתול, זה ממש פשוט הדפיס את תוכן cp.c למסך שלנו.
אנחנו יכולים להפעיל אותו שוב ואתה יכול לשים במספר קבצים יחד.
אז אתה יכול לעשות cp.c חתול, ואז אנחנו גם יכולים לשרשר את קובץ cat.c,
אשר היא התכנית שאנחנו עומדים לכתוב,
וזה ידפיס את שני קבצי גב אל גב למסך שלנו.
אז אם אנחנו לגלול למעלה קצת, אנו רואים שכאשר רצנו cp.c זה חתול, cat.c,
ראשון זה הדפיס קובץ cp, ולאחר מכן בהמשכו, הוא הדפיס את קובץ cat.c כאן למטה.
אנחנו הולכים להשתמש בזה רק כדי לקבל את רגלינו רטובות.
שחק עם הדפסה פשוטה לטרמינל, לראות איך זה עובד.
אם אתם פותחים עם gedit cat.c, Enter,
אתה יכול לראות את התכנית שאנו עומדים לכתוב.
צרפנו צלחת הדוד הנחמד הזה, ולכן אנחנו לא צריכים לבזבז זמן הקלדה את כל זה.
גם אנחנו בודקים במספר הטיעונים עברנו פנימה
אנחנו להדפיס הודעת שימוש נחמדה.
>> זה מסוג הדברים ששוב, כמו שכבר דברו עליו,
זה כמעט כמו זכרון שריר.
רק תזכור להמשיך לעשות את אותו סוג של חומר
ותמיד מדפיס על איזו הודעה מועילה
כך שאנשים יודעים איך להפעיל את התכנית.
עם חתול, זה די פשוט: הרי רק הולכים לעבור את כל הטענות השונות
שהועברו לתכנית שלנו, ואנחנו הולכים להדפסה
התכנים מתוכם למסך אחד בכל פעם.
כדי להדפיס את קבצים על המסך, אנחנו הולכים לעשות משהו דומה מאוד
למה שעשינו בסופו של החידון.
בסופו של החידון, ששוכר תכנית, היו לנו לפתוח את קובץ,
ואז היינו צריכים להדפיס אותו.
במקרה זה, אנחנו הולכים לפתוח את קובץ, ואנחנו הולכים לקרוא ממנה במקום.
ואז אנחנו הולכים להדפסה, במקום לקובץ, אנחנו הולכים להדפיס על גבי המסך.
אז מדפיס על המסך כל מה שעשית בעבר עם printf.
אז שלא משוגע מדי.
אבל קריאת קובץ היא קצת מוזר.
אנחנו נלך דרך שקצת בכל פעם.
אם אתם חוזרים לאותו הבעיה האחרונה בחידון שלך, בעית 33,
הקו הראשון שאנחנו הולכים לעשות כאן, פתיחת הקובץ, הוא מאוד דומה למה שעשינו שם.
אז סטלה, מה שנראה כמו קו, כשפותחים את קובץ?
[סטלה] * הון קובץ, קובץ - >> אוקיי. >> - שווה לfopen. >> כן.
אשר במקרה זה הוא? זה בתגובה.
>> זה בתגובה? argv [i] ו R?
>> בדיוק. יפה מאוד. אז סטלה לגמרי נכונה.
זה מה שנראה כמו הקו.
אנחנו הולכים לקבל זרם משתנה קובץ, לאחסן אותו ב* קובץ, כך שכל הכובעים,
קובץ, *, ושמו של משתנה זה יהיה קובץ.
אנחנו יכולים לקרוא לזה איך שנרצה. יכולנו לקרוא לזה first_file, או file_i, כל מה שאנחנו רוצים.
ולאחר מכן את השם של הקובץ הועבר בשורת הפקודה לתכנית זו.
אז הוא מאוחסן בargv [i], ולאחר מכן אנחנו הולכים לפתוח קובץ זה במצב קריאה.
עכשיו שאנחנו כבר פתחנו את הקובץ, מה הדבר שתמיד יש לנו לזכור לעשות
בכל פעם שאנחנו פתחנו תיק? לסגור אותו.
אז מסים, איך אנחנו לסגור תיק?
[המסים] fclose (קובץ) >> fclose (קובץ). בדיוק.
גדול. אוקיי. אם נסתכל על זה לעשות תגובה ממש כאן,
הוא אומר, "פתח argv [i] ולהדפיס התוכן שלה ל stdout."
>> מתוך סטנדרטי הוא שם מוזר. Stdout הוא פשוט הדרך שלנו לומר
אנחנו רוצים להדפיס אותו למסוף, אנחנו רוצים להדפיס אותו אל זרם הפלט הסטנדרטי.
אנחנו באמת יכולים להיפטר מהערה זו כאן.
אני הולך להעתיק אותו ולהדביק אותו מכיוון שזה מה שעשינו.
בשלב זה, עכשיו יש לנו לקרוא קצת על ידי הקובץ קצת.
כבר דברנו על כמה דרכים של קבצי קריאה.
אילו הם המועדפים שלך עד כה?
איזה דרכים יש לך לראות או שאתה זוכר, לקרוא קבצים?
[דניאל] fread? >> Fread? אז fread הוא אחד. ג'ימי, האם אתה מכיר אנשים אחרים?
[ג'ימי] מס >> אוקיי. לא. שרלוט? אלכסנדר? עוד מישהו? אוקיי.
אז האחרים הם fgetc, הוא אחד שאנחנו משתמשים בהרבה.
יש גם fscanf; אתם רואים כאן דפוס?
כולן מתחיל עם f. מה לעשות עם קובץ.
יש fread, fgetc, fscanf. כל אלה הם מתפקידיו בקריאה.
לכתיבה יש לנו fwrite, יש לנו fputc במקום fgetc.
אנחנו גם fprintf לי אוהבים שראינו בחידון.
מכיוון שמדובר בבעיה שכרוכה בקריאה מקובץ,
אנחנו הולכים להשתמש באחד משלושת התפקידים האלה.
אנחנו לא הולכים להשתמש בפונקציות אלו יורדות כאן.
פונקציות אלה נמצאות כולם בספריית הקלט / פלט הסטנדרטית.
אז אם אתה מסתכל על החלק העליון של תכנית זו,
אתה יכול לראות שאנחנו כבר כללנו בקובץ הכותרת לספריית הקלט / פלט הסטנדרטית.
אם ברצוננו להבין איזה מהם אנחנו רוצים להשתמש בו,
אנחנו תמיד יכולים לפתוח את הדפים.
אז אנחנו יכולים להקליד stdio אדם
ולקרוא על כל קלט stdio ופונקציות פלט בג
ואנו כבר רואים הו, נראים. זה להזכיר fgetc, זה להזכיר fputc.
אז אתה יכול להסתעף קצת ומסתכל, אומר, fgetc
ולהסתכל על דף האיש שלה.
אתה יכול לראות שזה הולך יחד עם חבורה שלמה של פונקציות אחרות:
fgetc, fgets, getc, getchar, מקבל, ungetc, והקלט של תווים ומחרוזות.
אז ככה אנו קוראים בתווים ומחרוזות מקבצים מקלט סטנדרטי,
שהוא למעשה ממשתמש.
וזה איך אנחנו עושים את זה בג בפועל
אז זה הוא לא משתמש GetString ופונקציות getchar
שהיינו מCS50 הספרייה.
אנחנו הולכים לעשות את הבעיה בכמה דרכים
כך שאתה יכול לראות שתי דרכים שונות לעשות את זה.
שניהם fread הפונקציה שדניאל ציין וfgetc דרכים טובות לעשות את זה.
אני חושב fgetc הוא קצת יותר קל, כי יש לו רק, כפי שאתה רואה,
טיעון אחד, * הקובץ שאנחנו מנסים לקרוא את התווים מ,
וערך ההחזרה שלו הוא int.
וזה קצת מבלבל, נכון?
>> כי אנחנו מקבלים אופי, אז למה לא מחזיר את זה char?
יש לכם רעיונות במה זה עלול לא לחזור char?
[תשובות ילדונת, לא מובן] כן. >> אז המסים לגמרי נכונים.
אם זה ASCII, אז מספר שלם זה יכול להיות ממופה לchar בפועל.
יכול להיות תו ASCII, וזה נכון.
זה בדיוק מה שקורה.
אנחנו משתמשים int פשוט כי יש לו עוד דברים.
זה יותר גדול מchar; char יש 8 סיביים בלבד, כי בית 1 על המכונות שלנו של 32-bit.
ויש int שווי כל 4 הבתים של שטח.
ומתברר שדרך fgetc עובדת,
אם אנחנו לגלול למטה בתקציר שלנו בדף האיש הזה קצת,
לגלול את כל הדרך למטה. מתבררים שהם משתמשים בערך המיוחד הזה שנקרא EOF.
זה קבוע מיוחד כערך ההחזרה של פונקצית fgetc
בכל פעם שאתה מכה את סוף הקובץ, או אם אתה מקבל הודעת שגיאה.
ומתברר שכדי לעשות את ההשוואות האלה עם EOF כראוי,
אתה רוצה להיות שסכום נוסף של מידע שיש לך בint
בניגוד לשימוש במשתנת char.
למרות יעילות fgetc הוא מקבל תו מתיק,
אתה רוצה לזכור שהוא חוזר משהו שהוא של הסוג int אליך.
עם זאת, זה די קל לשימוש.
זה הולך לתת לנו אופי, ולכן כל שעלינו לעשות הוא להמשיך לשאול את הקובץ,
"תן לי את התו הבא, תן לי את התו הבא, תן לי את התו הבא,"
עד שנגיע לסוף הקובץ.
ושימשוך בתו אחד בכל זמן מהקובץ שלנו,
ואז אנחנו יכולים לעשות מה שאנחנו אוהבים בו.
אנחנו יכולים לאחסן אותו, אנחנו יכולים להוסיף אותו למחרוזת, אנחנו יכולים להדפיס אותו.
האם משהו מכל זה.
>> טסתי חזרה החוצה ולחזור לתכנית cat.c,
אם אנחנו הולכים להשתמש fgetc,
איך ייתכן שאנחנו מתקרבים השורה הבאה של קוד?
אנחנו הולכים להשתמש - fread יעשה משהו שונה במקצת.
והפעם, אנחנו פשוט הולכים להשתמש fgetc לקבל תו אחד בכל פעם.
כדי לעבד את כל קובץ, מה שאולי יש לנו לעשות?
כמה תווים יש בקובץ?
יש הרבה. אז אתה בטח רוצה לקבל אחד
ולאחר מכן לקבל עוד ולקבל עוד ולקבל עוד.
איזה סוג של אלגוריתם אתה חושב שאולי אנחנו צריכים להשתמש כאן?
איזה סוג של -? [אלכסנדר] ללולאה? >> בדיוק.
איזה סוג של לולאה.
ללולאה היא בעצם גדול, במקרה זה.
וכמו שאמרתם, זה נשמע כאילו אתה רוצה לולאה על כל הקובץ,
מקבל אופי בכל פעם.
כל הצעה על מה שאולי ייראה כמו?
[אלכסנדר, לא מובן]
>> אוקיי, רק יגיד לי באנגלית מה אתה מנסה לעשות? [אלכסנדר, לא מובן]
אז במקרה הזה, זה נשמע כאילו אנחנו רק מנסים לולאה על כל הקובץ.
[אלכסנדר] אז אני <הגודל של int? >> הגודל של -?
אני מניח שהגודל של הקובץ, נכון? הגודל - להכין מרק לכתוב את זה ככה.
גודל קובץ לעת עתה, אני + +.
אז מתברר כי הדרך בה אתה עושה זאת באמצעות fgetc, וזה חדש,
הוא שאין דרך קלה, רק כדי לקבל את גודל קובץ
עם סוג זה "sizeof" של בנייה שכבר ראה קודם.
כאשר אנו משתמשים בפונקציה שfgetc, אנחנו משיקים כלשהו
, תחביר חדש פאנקי לזה ללולאה, שבו במקום להשתמש רק דלפק בסיסי
ללכת תו אחר תו, אנחנו הולכים להוציא תו אחד בכל פעם,
תו אחד בכל פעם, והדרך בה אנו יודעים שאנחנו בסוף
לא כשהתחלנו לספור את מספר מסוים של תווים,
אבל כאשר אנו שולפים את האופי הוא שסוף מיוחד של דמות קובץ.
אז אנחנו יכולים לעשות את זה על ידי - אני קורא פרק זה, ואנחנו הולכים לאתחל אותו
בשיחה הראשונה שלנו כדי לקבל את התו הראשון מתוך הקובץ.
אז את החלק הזה ממש כאן, זה הולך כדי לקבל תו מתוך הקובץ
ולאחסן אותו לפרק משתנה.
אנחנו הולכים להמשיך לעשות את זה עד שנגיע לסוף הקובץ,
שאנחנו עושים על ידי בדיקה לתו שלא להיות שווה לזה אופי EOF מיוחד.
ואז במקום לעשות CH + +, שהיה פשוט להגדיל את הערך,
כך שאם אנו קוראים מתוך הקובץ, הון, אומר,
ch + + היה נותן לנו ב ', ואז הייתי בא ג ד ולאחר מכן.
זה בבירור לא מה שאנחנו רוצים. מה שאנחנו רוצים כאן
בחלק אחרון זה שאנחנו רוצים לקבל את התו הבא מהקובץ.
>> אז איך ייתכן שאנחנו מקבלים את התו הבא מהקובץ?
איך אנחנו מקבלים את התו הראשון מהקובץ?
[סטודנטים] fgetfile? >> Fgetc, או, סליחה, היית לגמרי נכון.
שגיתי באיות זה ממש שם. אז כן.
כאן במקום לעשות CH + +,
אנחנו רק הולכים לקרוא fgetc (קובץ) שוב
ולאחסן את התוצאה באותו משתנה הפרק שלנו.
[שאלת סטודנט, לא מובן]
>> זה המקום בו חבר 'ה * הקבצים אלה מיוחדים.
הדרך בה הם עובדים היא שהם - בעת הפתיחה ראשונה - כשאתה עושה ששיחת fopen הראשון,
* הקובץ ביעילות משמש כמצביע לתחילת הקובץ.
ואז כל פעם שאתה קורא fgetc, זה עובר דמות אחת בתיק.
אז כל פעם שאתה קורא לזה, אתה הגדלת מצביע הקובץ על ידי תו אחד.
וכשאתה fgetc שוב, אתה מזיז אותה דמות אחרת
ועוד דמות ודמות אחרת ואופי אחר.
[שאלת סטודנט, לא מובן] ו>> כלומר - כן.
זה סוג של הקסם הזה מתחת למכסת המנוע.
אתה פשוט להגדיל כל דרך.
בשלב זה, אתה יכול ממש לעבוד עם אופי.
אז איך ניתן להדפיס את זה על המסך, עכשיו?
אנחנו יכולים להשתמש באותו הדבר printf שהשתמשנו בעבר.
כי אנחנו כבר משתמשים בכל הסמסטר.
אנחנו יכולים לקרוא printf,
ואנחנו יכולים לעבור עליו בתו סתם ככה.
דרך נוספת לעשות את זה היא לא באמצעות printf והצורך לעשות מחרוזת תבנית זו,
אנחנו יכולים גם להשתמש באחת מהפונקציות האחרות.
אנחנו יכולים להשתמש בfputc, שמדפיס דמות על המסך,
אלא אם אנחנו מסתכלים על fputc - תנו לי להתרחק קצת.
אנחנו רואים מה שיפים הוא שלוקח באופי שאנו קוראים את שימוש fgetc,
אבל אז אנחנו צריכים לתת לו זרם להדפיס ל.
אנחנו יכולים גם להשתמש בפונקצית putchar, שלשים ישירות לפלט סטנדרטי.
אז יש חבורה שלמה של אפשרויות שונות שאנחנו יכולים להשתמש להדפסה.
הם כולם בספריית הקלט / פלט הסטנדרטית.
כל פעם שאתה רוצה להדפיס - כך printf, כברירת מחדל, ידפיס לתקן המיוחד החוצה את הזרם,
אשר stdout ש.
אז אנחנו פשוט יכולים להתייחס אליו כסוג של ערך זה קסם, stdout כאן.
אופס. שים פסיק בחוץ.
>> זה הרבה מידע חדש, פאנקי בפה.
הרבה זה מאוד האידיומטיים, במובן זה שזה קוד
שנכתב בדרך זו רק משום שהיא נקיה לקריאה, קל לקריאה.
ישנן דרכים רבות ושונות כדי לעשות את זה, הרבה פונקציות שונות בן ניתן להשתמש,
אבל אנחנו נוטים פשוט בצעו את אותם הדפוסים הללו שוב ושוב.
אז אל תתפלא אם אתה רואה קוד כזה עולה שוב ושוב.
בסדר. בשלב זה, אנו זקוקים להפסקה ליום.
תודה שבאתם. תודה שצפה, אם אתה במצב מקוון. ונתראה בשבוע הבא.
[CS50.TV]