תרגול 3 מבנים טענות נכונות (assert) לי-טל משיח...

Post on 19-Dec-2015

234 Views

Category:

Documents

7 Downloads

Preview:

Click to see full reader

TRANSCRIPT

3תרגול

מבנים

(assert)טענות נכונות

litalma@cs.technion.ac.ilלי-טל משיח

נערך ע''י ודים אייזנברג

Structuresמבנים - struct <struct name> {struct <struct name> {

<type name1> <field1>;<type name1> <field1>;<type name2> <field2>;<type name2> <field2>;....<type namen> <fieldn>;<type namen> <fieldn>;

};};טיפוסים שוניםטיפוסים שוניםושמות שונים שמות שונים עם אוסף נתונים אוסף נתונים אבסטרקציה על •מטיפוסים קיימיםמטיפוסים קיימיםע"י הרכבה טיפוסים חדשים טיפוסים חדשים דרך ליצור •:ממערךממערךהבדלים •

אותו טיפוסאותו טיפוסבמערך כל האיברים מ–לאיברים - מתייחסים לאיברים על פי מספרם אין שמות אין שמות במערך –

הסידורי

2

Structuresמבנים - הגדרת משתנה:•

struct <struct name> <variable name>;struct <struct name> <variable name>;דוגמא - מבנה המגדיר טיפוס תאריך. חודשים •

שמורים בפורמט של שלושה תווים.struct Date_t { int day; char month[4]; int year;};

:struct Date_tהגדרת משתנה מסוג •struct Date_t d;3

Why 4 chars ?

תכנות בלי מבנים

int start_day, start_year;int end_day, end_year;char start_month[4], end_month[4];int compare_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);

4

תכנות עם מבנים

struct Date_t start;struct Date_t end;int compare_dates(struct Date_t date1,

struct Date_t date2);...compare_dates(start,end);

יותרקצרקצרהקוד • יותרקריאקריאהקוד •לפונקציות קטן יותרמספר הפרמטרים מספר הפרמטרים •

פחות סיכוי להתבלבל בין הפרמטרים–

5

Structuresמבנים - נעשית ע"י:structבתוך משתנה מסוג פנייה לשדה פנייה לשדה •

<variable name>.<field name>;<variable name>.<field name>;, ניתן לגשת לשדה בצורה -structכאשר נתון מצביע ל•

ישירה:(*<pointer name>).<field name> ;(*<pointer name>).<field name> ;

מכיוון שדרך זו מסורבלת, קיימת דרך נוספת ופשוטה •יותר לגשת לשדה:

<pointer name> -> <field name> ;<pointer name> -> <field name> ;דוגמא:•

struct Date_t d;struct Date_t* p = &d;d.day = 7;p->day=12; /* or (*p).day=12; */6

Structuresמבנים - תוכנית דוגמא:•

int main() { struct Date_t date , *pdate; date.day = 21; strcpy(date.month , "NOV"); pdate = &date ; pdate->year = 1971 ; printf ("The year is %d\n",date.year);}

7

שם נוסף לטיפוס

struct Date_tבדוגמת התאריך נוצר טיפוס בשם • כל פעם, structבכדי להימנע מהצורך ברישום •

המורכב ממלה שם נוסף שם נוסף ניתן לתת לטיפוס הזה Dateאחת בלבד, למשל

להתייחס לטיפוס, באותה צורה כמו דרך טבעית יותר דרך טבעית יותר –לטיפוסים הפרימיטיביים של השפה:

int i;char c;Date date;

typedef נותנים לטיפוס שם נוסף ע''י Cב-•

8

Date הוא שם נוסף struct Date_tל-

typedeftypedefשם נוסף לטיפוס באמצעות

typedef <old type name> <new type name> ;typedef <old type name> <new type name> ;לשם קיים של טיפוסשם נוסף שם נוסף הפקודה נותנת •אחרי הפקודה שני השמות מתייחסים לאותו •

טיפוסלדוגמא:•

typedef int ID;typedef int * Element;...ID i; Element element;

9

:-זהה לint i;int * element;

typedeftypedefשם נוסף למבנה באמצעות typedef struct Date_t Date ;

:מצביע למבנהמצביע למבנהניתן גם להגדיר טיפוס של •typedef struct Date_t * pDate;

אוtypedef Date* pDate;

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

10

שימוש במבנה בתוך מבנהניתן להגדיר בתוך מבנה שדה מטיפוס מבנה אחר•

typedef struct Date_t { int day; char month[4]; int year;} Date ;typedef struct Person_t { char* name;

int height; Date birth;} Person ;

גישה לשדה של המבנה הפנימי:•Person p ;Person* pp=&p;p.birth.year = 1994pp->birth.year = 1994;

? pp->birth->year מדוע לא •

11

שימוש במצביע למבנה בתוך מבנה ניתן היה להגדירו כמצביע למבנה Date כשדה מטיפוסbirthבמקום להגדיר את •

Dateמטיפוס typedef struct Person_t { char* name;

int height; Date* birth; } Person ;

גישה לשדה של המבנה הפנימי:•Person p ;Person* pp=&p;p.birth->year = 1994pp->birth->year = 1994;

כחלק ממנו מקום למבנה מטיפוס לא הקצינו לא הקצינו p Personשמו לב: כאשר הקצינו את •Dateאלא למצביע אליו

לכן קטע הקוד הנתון אינו תקין !•כיצד ניתן לתקנו?•

12

מבנים המתייחסים לעצמם:מצביע למבנה עצמומצביע למבנה עצמובהגדרת מבנה ניתן להשתמש במצביעים מטיפוס •

typedef struct Date_t { int day; char month[4]; int year; struct Date_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);}13

תמונת הזיכרון לפני ביצוע פקודות ההדפסה

ניתן לשנות את ערכו של • של המשתנה yearהשדה

dt2:בכמה אופנים dt2.year = 1994;pdtyear = 1994;(dt1.next) year = 1994;

רשימה מקושרת רשימה מקושרת נוצרה כאן •בת שני איברים

מה צריך להיות ערכו של • כדי שנדע dt2 ב-nextהשדה

שאין איבר נוסף?

14

pdt

31DEC1992

1JAN1993

?

dt1

dt2

רשימה מקושרת של מבנים המוקצים דינמית

הבעיה: •מספר לא ידוע מספר לא ידוע נרצה לקרוא מהקלט הסטנדרטי

לסדר ולהדפיסם בסדר הפוך ולהדפיסם בסדר הפוך , של תאריכיםשל תאריכיםהקריאה

פתרון:•של תאריכיםרשימה מקושרת רשימה מקושרת

15

רשימה מקושרת של מבנים המוקצים דינמית

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;}16

האם הבדיקה ? מספיקה

מה חסר בסוף

? הפונקציה

רשימה מקושרת של מבנים המוקצים באופן דינמי

void free_date_list(Date *top){Date *current=top, *tmp;while (current != NULL) {

tmp=current;current=current->next;free(tmp);

} } 17

רשימה מקושרת של מבנים המוקצים באופן דינמי

void print_date_list(Date *top){Date * current = top;

while (current != NULL) { printf("%d %s %d\n", current ->day,

current ->month, current ->year); current = current->next ; }}18

שיפורים לקוד הקודם

19

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;}

int main() { Date *top = NULL, *tmp = NULL ; Date date;

while (scanf("%d %s %d", &date.day ,date.month , &date.year) == 3){ 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;

}

דוגמה נוספת לשימוש במבנים ומצביעים

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

התוכנית•אירועים היסטורייםאירועים היסטורייםשמכיל תקלוט תאריכים מקובץ תקלוט תאריכים מקובץ –קרו באותו התאריךאירועים היסטוריים אירועים היסטוריים אילו עבור כל תאריך תדפיסעבור כל תאריך תדפיס–

:eventsדוגמא לקובץ האירועים 1 1 JAN 404 Last gladiator competition JAN 404 Last gladiator competition 6 6 MAYMAY 1889 Eiffel tower opens1889 Eiffel tower opens21 NOV 1794 Honolulu harbor discovered21 NOV 1794 Honolulu harbor discovered1 1 JAN 1852 First US public bath opensJAN 1852 First US public bath opens2 MAR 1969 First takeoff of the Concorde2 MAR 1969 First takeoff of the Concorde6 6 MAYMAY 1915 Orson Welles is born1915 Orson Welles is born6 6 MAY 1626 Manhattan purchased for 1000$MAY 1626 Manhattan purchased for 1000$2 2 MAR 1969 First landing of the ConcordeMAR 1969 First landing of the Concorde

20

דוגמה נוספת לשימוש במבנים ומצביעים

הפעלת התוכנית והפלט המתקבל:–

> important_dates eventsenter date: 2 MAR 1969 First takeoff of the concordeFirst landing of the concordeenter date: 23 NOV 1999Nothing special

21

פתרון אפשרי

רשימה רשימה נשמור •של תאריכיםמקושרת מקושרת

לכל תאריך נשמור •רשימה של אירועים רשימה של אירועים

שקרו בתאריך זה

22

date

events list

next

date

events list

next

Historical Date

date

events list

next

next

next

next

next

event

description

description

description

description

historicalDateList

מבנים

הגדרות •#define MAX_LINE_LENGTH 100

מבנה תאריך•typedef struct Date_t { int day; char month[4]; int year;} Date;

23

מבנים

מבנה אירוע•

typedef struct Event_t { char* description ;

struct Event_t *next;} Event;

24

שימו לב לשמות המלאים של .השדות

description ברור יותר מאשרd,des,desc,dsc

לא צריך להתאמץ להמציא/להבין קיצורים :

dscrptn,descript

מבנים

מבנה תאריך היסטורי •

typedef struct HistoricalDate_t { Date date ;

Event *eventList;struct HistoricalDate_t *next ;

} HistoricalDate;

25

פונקציות עזר – טיפול בקלטקריאת תאריך מקובץ קלט נתון )הקצאת התאריך באחריות הקורא •

לפונקציה !( int readDate(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; }

26

פונקציות עזר – טיפול בקלטקריאת שורת אירוע היסטורי מקובץ קלט נתון. )הקצאת התאריך, והמחרוזת •

לפונקציה !( לתיאור האירוע באחריות הקורא

int readEvent(FILE* inputFile, Date* date, char* description) { if (inputFile == NULL || date == NULL || description == NULL) { return 0 ; } if (readDate(inputFile,date) ==0) { return 0; } if(fgets(description, MAX_LINE_LENGTH, inputFile)==NULL) {

return 0; } return 1;}

27

פונקציות עזר – הקצאת צומת אירוע היסטורי

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; return newEvent;}28

פונקציות עזר – הקצאת צומת תאריך היסטורי

HistoricalDate* allocateHistoricalDate(Date date) { HistoricalDate *newHistoricalDate =

(HistoricalDate*) malloc(sizeof(HistoricalDate)); if (newHistoricalDate == NULL){ return NULL; } newHistoricalDate->date = date; newHistoricalDate->eventList = NULL; newHistoricalDate->next = NULL; return newHistoricalDate;}

29

פונקציות עזר – מציאת תאריך היסטורי ברשימת תאריכים

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); }

30

פונקציות עזר – הדפסת רשימת אירועים היסטוריים

void printEvents(Event* event) { if (event == NULL ) {

return; } printf (“%s\n”, event->description); printEvents(event->next);}

31

פונקציות עזר – בניית מבנה הנתוניםHistoricalDate* readEvents(FILE* inputFIle) {

char description[MAX_LINE_LENGTH]; Date date ;HistoricalDate *firstHistoricalDate =NULL , *currentHistoricalDate = NULL; Event *event = NULL;while (readEvent(inputFIle,&date, description)) {

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(description);if (event == NULL) { return NULL;}event->next = currentHistoricalDate->eventList;currentHistoricalDate->eventList = event;

}return firstHistoricalDate ;

}32

התוכנית הראשיתint main(int argc, 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 ;printf(“enter date:”);while (readDate(stdin,&date)) {

historicalDate = find(historicalDateList ,date) ;if (historicalDate == NULL) printf (“Nothing special\n”);else printEvents(historicalDate->eventList); printf(“enter date:”);

}return 0;

}33

השיפורים הנדרשים

בסוף התוכנית.שחרור הזיכרון שחרור הזיכרון •

במידה וקרה שחרור הזיכרון שכבר הוקצה שחרור הזיכרון שכבר הוקצה •.readEvents במהלך ריצת הפונקציה כשלוןכשלון

למשתמש.הודעות שגיאה ברורות הודעות שגיאה ברורות •

שמציינים ערכי שגיאה מספרי קסם מספרי קסם החלפת • enumenum או definedefineבערכי

34

(assert)טענות נכונות

35

assertטענות נכונות -

בזמן פיתוח בזמן פיתוח מציאת שגיאותמציאת שגיאותמנגנון מועיל ל•תוכנה

לגבי התוכנה שאמורות ביטוי טענותביטוי טענותנועדו ל•נכונות בהיעדר באגיםנכונות בהיעדר באגיםלהיות

לתיעוד לתיעודעוזרות–

assertassert מתבצע ע''י קריאה למאקרו Cב-•intמקבל ביטוי משוערך ל-–assert.hמוגדר ב-–

התוכנהנבדקות אוטומטית בזמן ריצת נבדקות אוטומטית בזמן ריצת •36

assertטענות נכונות - prog.c:

int main(int argc, char *argv[]){ ...

if(argc > 3) {...

} else if (argc < 2) {...} else {

/* if we are here argc is 2 */

...}

...

37

assert(argc == 2);

assertטענות נכונות -

> gcc prog.c -o prog> prog> prog a> prog a b c> prog a bprog: prog.c:12: main: Assertion ‘argc == 2’ failed.Abort>

38

assertטענות נכונות - prog.c:

int main(int argc, char *argv[]){ ...

if(argc > 3) {...

} else if (argc < 2) {...} else {

...}

...

39

assert(argc == 2);

argcהמתכנת חשב שבנקודה הזאת • )בלי קשר לאיך 2חייב להיות שווה ל-

( if/elseשהתוכנית נקראת, בגלל המתכנת תיעד את ההנחה שלו ע''י •

assert .ההנחה התבדתה בזמן ריצה.• ifהבאג של המתכנת היה כנראה ב-•

=< if(argcהראשון – הקוד הנכון הוא 3)

assertטענות נכונות -

התוכנית נעצרת התוכנית נעצרת – לא מתקיימת לא מתקיימת כשהטענה •שמפרטת אתהודעת שגיאה הודעת שגיאה ומודפסת

שלא התקיימה הטענההטענה–:מיקום הטענה מיקום הטענה –

שם הקובץ•מספר שורה•שם הפונקציה•

•assert מהדפסות בשביל קומפקטי יותר קומפקטי יותרdebuggingdebugging

40

assertטענות נכונות - ע''י קימפול עם דגלasserts את מנגנון ניתן לבטל ניתן לבטל •

-DNDEBUG: > gcc –DNDEBUG prog.c -o prog> prog a b>

גרסת ייצורגרסת ייצורנהוג להפעיל את הדגל הזה ב•(production version)

–assertsגרסת פיתוחגרסת פיתוח מופעלים רק ב (development version)

של תרגילי הבית התוכניות בבדיקה האוטומטית בבדיקה האוטומטית •הזהעם הדגל עם הדגל ייקומפלו

בזמן פיתוח של תרגילי הביתasserts להשתמש ב-מומלץמומלץ–

41

assertטענות נכונות -

שגיאות שגיאות רק לתפיסת assertsמשתשים ב-•– באגיםמתכנת מתכנת

להשתמש בהם בשביל לתפוסלא נכון לא נכון •משתמשמשתמש שגיאות–מערכת הפעלהמערכת הפעלהשגיאות –

42

טענות נכונות – שימוש לא נכון

int main(int argc, char *argv[]) {assert(argc == 2); /* the program must be called with only one parameter */

...

43

if(argc != 2) { fprintf(stderr, “the program must be called with” ); fprintf(stderr, ” only one parameter\n”); exit(0);}

טענות נכונות – שימוש לא נכון

FILE* fd = fopen(" input.txt" , "r");assert(fd != NULL);

44

if(fd == NULL) { error(“Failed to open input.txt!\n”);}

טענות נכונות – שימוש לא נכון

לביצוע התוכנית הנחוצותהנחוצות, הכנסת פעולותהכנסת פעולות•-assertלתוך ה

assert((result = calculate()) >= MIN_RESULT);/* working with result */...

והחישוב לא יופעל לא יופעל --assertassertהה production versionב-–הנחוץ לא יתבצע !

45

טענות נכונות – שימוש נכון

result = calculate();assert(result >= MIN_RESULT);/* working with result */...

46

top related