1 / 43

תרגול 3

תרגול 3. מבנים טענות נכונות (assert). לי-טל משיח litalma@cs.technion.ac.il נערך ע''י ודים אייזנברג. מבנים - Structures. struct < struct name> { <type name1> <field1>; <type name2> <field2>; . . <type namen > < fieldn >; };

gdunphy
Download Presentation

תרגול 3

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. תרגול 3 מבנים טענות נכונות (assert) לי-טל משיח litalma@cs.technion.ac.il נערך ע''י ודים אייזנברג

  2. מבנים - Structures struct <struct name> { <type name1> <field1>; <type name2> <field2>; . . <type namen> <fieldn>; }; • אבסטרקציה על אוסף נתונים עם שמות שונים וטיפוסים שונים • דרך ליצור טיפוסים חדשים ע"י הרכבה מטיפוסים קיימים • הבדלים ממערך: • במערך כל האיברים מאותו טיפוס • במערך אין שמות לאיברים - מתייחסים לאיברים על פי מספרם הסידורי

  3. מבנים - Structures • הגדרת משתנה: struct <struct name> <variable name>; • דוגמא - מבנה המגדיר טפוס תאריך. חודשים שמורים בפורמט של שלושה תווים. structDate_t { int day; char month[4]; int year; }; • הגדרת משתנה מסוג date: structDate_t d; Why 4 chars ?

  4. תכנות בלי מבנים intstart_day, start_year; intend_day, end_year; char start_month[4], end_month[4]; intcompare_dates(int day1, char* month1, int year1, int day2, char* month2, int year2); ... compare_dates(start_day, start_month, start_year, end_day, end_month, end_year);

  5. תכנות עם מבנים structDate_t start; structDate_t end; intcompare_dates(structDate_t date1, structDate_t date2); ... compare_dates(start,end); • הקוד קצר יותר • הקוד קריא יותר • מספר הפרמטרים לפונקציות קטן יותר • פחות סיכוי להתבלבל בין הפרמטרים

  6. מבנים - Structures • פנייה לשדה בתוך משתנה מסוג struct נעשית ע"י: <variable name>.<field name>; • כאשר נתון מצביע לstruct-, ניתן לגשת לשדה בצורה ישירה: (*<pointer name>).<field name> ; • מכיוון שדרך זו מסורבלת, קיימת דרך נוספת ופשוטה יותר לגשת לשדה: <pointer name> -> <field name> ; • דוגמא: structDate_t* p; p=&d; p->day=12; /* or (*p).day=12; */

  7. מבנים - Structures • תוכנית דוגמא: int main() { structDate_t date , *pdate; date.day = 21; strcpy(date.month , "NOV"); pdate = &date ; pdate->year = 1971 ; printf ("The year is %d\n",date.year); }

  8. שם נוסף לטיפוס • בדוגמת התאריך נוצר טיפוס בשם structDate_t • בכדי להימנע מהצורך ברישום struct כל פעם, ניתן לתת לטיפוס הזה שם נוסף המורכב ממלה אחת בלבד, למשל Date • דרך טבעית יותר להתייחס לטיפוס, באותה צורה כמו לטיפוסים הפרימיטיביים של השפה: inti; char c; Date date; • ב-C נותנים לטיפוס שם נוסף ע''יtypedef Date הוא שם נוסף ל-structDate_t

  9. שם נוסף לטיפוס באמצעות typedef typedef <old type name> <new type name> ; • הפקודה נותנת שם נוסף לשם קיים של טיפוס • אחרי הפקודה שני השמות מתייחסים לאותו טיפוס • לדוגמא: typedefint ID; typedefint * Element; ... ID i; Element element; זהה ל-: inti; int * element;

  10. שם נוסף למבנה באמצעות typedef typedefstructDate_t Date ; • ניתן גם להגדיר טיפוס של מצביע למבנה: typedefstructDate_t * pDate; או typedef Date* pDate; • ניתן לבצע את הגדרת המבנה ונתינת שם נוסף יחד: typedefstructDate_t { int day; char month[4]; int year; } Date; typedef Date* pDate;

  11. שימוש במבנה בתוך מבנה • ניתן להגדיר בתוך מבנה שדה מטיפוס מבנה אחר typedefstructDate_t { int day; char month[4]; int year; } Date ; typedefstructPerson_t { char* name; int height; Date birth; } Person ; • גישה לשדה של המבנה הפנימי: Person p ; Person* pp=&p; p.birth.year = 1994 pp->birth.year = 1994; • מדוע לא pp->birth->year?

  12. שימוש במצביע למבנה בתוך מבנה • במקום להגדיר את birth כשדה מטיפוסDate ניתן היה להגדירו כמצביע למבנה מטיפוס Date typedefstructPerson_t { char* name; int height; Date* birth; } Person ; • גישה לשדה של המבנה הפנימי: Person p ; Person* pp=&p; p.birth->year = 1994 pp->birth->year = 1994; • שמו לב: כאשר הקצינו את pPersonלא הקצנו כחלק ממנו מקום למבנה מטיפוס Date אלא למצביע אליו • לכן קטע הקוד הנתון אינו תקין ! • כיצד ניתן לתקנו?

  13. מבנים המתייחסים לעצמם • בהגדרת מבנה ניתן להשתמש במצביעים מטיפוס מצביע למבנה עצמו: typedefstructDate_t { int day; char month[4]; int year; structDate_t* next; } Date ; • הדבר מאפשר לשמור את כתובתו של מבנה אחר מאותו טיפוס בתוך המבנה: int main() { Date dt1, dt2, *pdt ; dt1.day = 31 ; dt1.year = 1992 ; strcpy(dt1.month,”DEC”); dt2.day = 1 ; dt2.year = 1993 ; strcpy(dt2.month,”JAN”); pdt = &dt2; dt1.next = &dt2; printf ("Year in dt1: %d\n Year in dt2: %d\n ",dt1.year, (dt1.next)->year); }

  14. dt1 31 DEC 1992 dt2 1 JAN 1993 ? תמונת הזיכרון לפני ביצוע פקודות ההדפסה • ניתן לשנות את ערכו של השדה year של המשתנה dt2 בכמה אופנים: dt2.year = 1994; pdtyear = 1994; (dt1.next) year = 1994; • נוצרה כאן רשימה מקושרת בת שני איברים • מה צריך להיות ערכו של השדה next ב-dt כדי שנדע שאין איבר נוסף? pdt

  15. רשימה מקושרת של מבנים המוקצים באופן דינמי • הבעיה: נרצה לקרוא מהקלט הסטנדרטי מספר לא ידוע של תאריכים, ולהדפיסם בסדר הפוך לסדר הקריאה • פתרון: רשימה מקושרת של תאריכים

  16. רשימה מקושרת של מבנים המוקצים באופן דינמי האם הבדיקה מספיקה ? int main() { Date *top = NULL, *tmp = NULL ; int day, year ; char month[4]; while (scanf("%d %s %d", &day ,month , &year) != EOF){ tmp = (Date*) malloc(sizeof(Date)); if (tmp == NULL) { free_date_list(top); exit(0); } tmp->day = day; tmp->year = year; strcpy(tmp->month ,month); tmp->next = top ; top = tmp; } print_date_list(top); return 0; } מה חסר בסוף הפונקציה ?

  17. רשימה מקושרת של מבנים המוקצים באופן דינמי void free_date_list(Date *top){ while (top != NULL) { Date * tmp=top; top=top->next; free(tmp); } }

  18. רשימה מקושרת של מבנים המוקצים באופן דינמי void print_date_list(Date *top){ Date *tmp = top; while (tmp != NULL) { printf("%d %s %d\n", tmp->day, tmp->mon, tmp->year); tmp = tmp->next ; } }

  19. שיפורים לקוד הקודם int main() { Date *top = NULL; Date date; while (scanf("%d %s %d", &date.day ,date.month , &date.year) == 3){ Date*tmp = (Date*) malloc(sizeof(Date)); if (tmp == NULL) { free_date_list(top); exit(0); } *tmp = date; tmp->next = top ; top = tmp; } print_date_list(top); free_date_list(top); return 0; } int main() { Date *top = NULL, *tmp = NULL ; intday,year ; char month[4]; while (scanf("%d %s %d", &day ,month , &year) != EOF){ tmp = (Date*) malloc(sizeof(Date)); if (tmp == NULL) { free_date_list(top); exit(0); } tmp->day = day ; tmp->year = year ; strcpy(tmp->month ,month); tmp->next = top ; top = tmp; } print_date_list(top); return 0; }

  20. דוגמה נוספת לשימוש במבנים ומצביעים • הבעיה: תוכנית שמדפיסה אירועים היסטוריים שקרו בתאריך מסוים. • התוכנית • תקרא קובץ שמכיל אירועים היסטוריים • תקלוט תאריכים • עבור כל תאריך תדפיס אילו אירועים היסטוריים קרו באותו התאריך דוגמא לקובץ האירועים events: 1 JAN 404 Last gladiator competition 6 MAY 1889 Eiffel tower opens 21 NOV 1794 Honolulu harbor discovered 1 JAN 1852 First US public bath opens 2 MAR 1969 First takeoff of the Concorde 6 MAY 1915 Orson Welles is born 6 MAY 1626 Manhattan purchased for 1000$ 2 MAR 1969 First landing of the Concorde

  21. דוגמה נוספת לשימוש במבנים ומצביעים • הפעלת התוכנית והפלט המתקבל: > important_dates events enter date: 2 MAR 1969 First takeoff of the concorde First landing of the concorde enter date: 23 NOV 1999 Nothing special

  22. next next next next description description description description Historical Date date events list next פתרון אפשרי historicalDateList • נשמור רשימה מקושרת של תאריכים • לכל תאריך נשמור רשימה של אירועים שקרו בתאריך זה event date events list next date events list next

  23. מבנים • הגדרות #define MAX_LINE_LENGTH 100 • מבנה תאריך typedefstructDate_t { int day; char month[4]; int year; } Date;

  24. מבנים • מבנה אירוע typedef struct Event_t { char* description ; struct Event_t *next; } Event; שימו לב לשמות המלאים של השדות. description ברור יותר מאשר d,des,desc,dsc לא צריך להתאמץ להמציא/להבין קיצורים : dscrptn,descript

  25. מבנים • מבנה תאריך היסטורי typedef struct HistoricalDate_t { Date date ; Event *eventList; struct HistoricalDate_t *next ; } HistoricalDate;

  26. פונקציות עזר – טיפול בקלט • קריאת תאריך מקובץ קלט נתון (הקצאת התאריך באחריות הקורא לפונקציה !) intreadDate(FILE* inputFile, Date* date) { if (inputFile == NULL || date == NULL) { return 0; } if (fscanf (inputFile, “%d %s %d ”, &(date->day),date->month, &(date->year))==3) { return 1; } return 0; }

  27. פונקציות עזר – טיפול בקלט • קריאת שורת אירוע היסטורי מקובץ קלט נתון. (הקצאת התאריך, והמחרוזת לתיאור האירוע באחריות הקורא לפונקציה !) intreadEvent(FILE* inputFile, Date* date, char* buffer) { if (inputFile == NULL || date == NULL || buffer == NULL) { return 0 ; } if (readDate(inputFile,date) ==0) { return 0; } if(fgets(buffer, MAX_LINE_LENGTH, inputFile)==NULL) { return 0; } return 1; }

  28. פונקציות עזר – הקצאת צומת אירוע היסטורי Event* allocateEvent(char* description) { Event *newEvent; if (description == NULL) return NULL; newEvent=(Event*)malloc(sizeof(Event)); if (newEvent == NULL) return NULL; newEvent->description=(char*)malloc(strlen(description)+1); if (newEvent->description == NULL) { free (newEvent) ; return NULL; } strcpy(newEvent->description, description); newEvent->next = NULL; returnnewEvent; }

  29. פונקציות עזר – הקצאת צומת תאריך היסטורי HistoricalDate* allocateHistoricalDate(Date date) { HistoricalDate *newHistoricalDate = (HistoricalDate*) malloc(sizeof(HistoricalDate)); if (newHistoricalDate == NULL){ return NULL; } newHistoricalDate ->date = date; newHistoricalDate ->eventList = NULL; newHistoricalDate ->next = NULL; returnnewHistoricalDate; }

  30. פונקציות עזר – מציאת תאריך היסטורי ברשימת תאריכים HistoricalDate* find(HistoricalDate* first, Date date){ if (first == NULL) { return NULL; } if (first->date.day == date.day && strcmp(first->date.month,date.month) == 0 && first->date.year == date.year) { return first; } return find(first->next,date); }

  31. פונקציות עזר – הדפסת רשימת אירועים היסטוריים void printEvents(Event* event) { if (event == NULL ) { return; } printf (“%s\n”, event->description); printEvents(event->next); }

  32. פונקציות עזר – בניית מבנה הנתונים HistoricalDate* readEvents(FILE* inputFIle) { char buffer[MAX_LINE_LENGTH]; Date date ; HistoricalDate *firstHistoricalDate =NULL , *currentHistoricalDate = NULL; Event *event = NULL; while (readEvent(inputFIle,&date,buffer)) { currentHistoricalDate = find(firstHistoricalDate ,date) ; if (currentHistoricalDate == NULL) { /* in case the date doesn't exist – add this date to be the first in the list */ currentHistoricalDate = allocateHistoricalDate(date); if (currentHistoricalDate == NULL) { return NULL ; } currentHistoricalDate->next = firstHistoricalDate ; firstHistoricalDate = currentHistoricalDate ; } event = allocateEvent(buffer); if (event == NULL) { return NULL; } event->next = currentHistoricalDate->eventList; currentHistoricalDate->eventList = event; }return first ; }

  33. התוכנית הראשית int main(intargc, char* argv[]) { FILE* inputFile = NULL; HistoricalDate* historicalDateList= NULL , *historicalDate = NULL; Date date; if (argc != 2) return 1 ; if ((inputFIle = fopen(argv[1],”r”))==NULL) return 2 ; historicalDateList = readEvents(inputFile); /*creating the list*/ fclose(inputFile); if (historicalDateList == NULL) return 3 ; while (readDate(stdin,&date)) { historicalDate = find(historicalDateList ,date) ; if (historicalDate == NULL) printf (“Nothing special\n”); else printEvents(historicalDate->eventList); } return 0; }

  34. השיפורים הנדרשים • שחרור הזיכרון בסוף התוכנית. • שחרור הזיכרון שכבר הוקצה במידה וקרה כשלון במהלך ריצת הפונקציה readEvents. • הודעות שגיאה ברורות למשתמש. • החלפת מספרי קסם שמציינים ערכי שגיאה בערכי define או enum

  35. טענות נכונות - assert • כלי תכנות מועיל למציאת שגיאות בזמן פיתוח תוכנה • נועדו לביטוי טענות לגבי התוכנה שאמורות להיות נכונות בהיעדר שגיאות בתוכנה • ב-C מתבצע ע''י קריאה למאקרו assertשמקבל ביטוי משוערך ל-int • מוגדר ב-assert.h • נבדקות אוטומטית בזמן ריצת התוכנה

  36. טענות נכונות - assert prog.c: int main(intargc, char *argv[]) { ... if(argc > 3) { ... } else if (argc < 2) { ... } else { /* if we are here argc is 2 */ ... } ... assert(argc == 2);

  37. טענות נכונות - assert > gccprog.c -o prog > prog > prog a > prog a b c > prog a b prog: prog.c:12: main: Assertion `argc == 2' failed. Abort >

  38. טענות נכונות - assert • כשהטענה לא מתקיימת – התוכנית נעצרת ומודפסת הודעת שגיאה שמפרטת את • הטענה שלא התקיימה • מיקום הטענה : • שם הקובץ • מספר שורה • שם הפונקציה

  39. טענות נכונות - assert • ניתן לבטל את מנגנון assertsע''יקימפול עם דגל-DNDEBUG : > gcc –DNDEBUG prog.c -o prog > prog a b > • נהוג להפעיל את הדגל הזה בגרסת ייצור (production version) • asserts מופעלים רק בגרסת פיתוח (development version) • בבדיקה האוטומטית של תרגילי הבית התוכניות ייקומפלו עם הדגל הזה • מומלץ להשתמש ב-asserts בזמן פיתוח של תרגילי הבית

  40. טענות נכונות – שימוש לא נכון • שימוש ב-assert כדי לזהות שגיאות אפשריות של משתמש או של מערכת הפעלה : int main(int argc, char *argv[]) { assert(argc == 2); /* the program must be called with only one parameter */ ... if(argc != 2) { fprintf(stderr, “the program must be called with” ); fprintf(stderr,” only one parameter\n”); exit(0); }

  41. טענות נכונות – שימוש לא נכון FILE* fd = fopen(“input.txt" , "r"); assert(fd != NULL); if(fd == NULL) { error(“Failed to open input.txt!\n”); }

  42. טענות נכונות – שימוש לא נכון • הכנסת חישובים, הנחוצים לביצוע התוכנית לתוך הassert- assert((result = calculate()) >= MIN_RESULT); /* working with result */ ... • ב-production version הassert- לא יופעל והחישוב הנחוץ לא יתבצע !

  43. טענות נכונות – שימוש נכון result = calculate(); assert(result >= MIN_RESULT); /* working with result */ ...

More Related