230 likes | 395 Views
תרגול מס' 3 . מבנים בעייה לדוגמה. לי-טל משיח litalma@cs.technion.ac.il. הקצאת זיכרון דינמית - תזכורת. void* malloc(unsigned nbytes) הקצאת קטע זיכרון בגודל nbytes . אם ההקצאה הצליחה, מוחזר מצביע לקטע הזה בזיכרון, אחרת מוחזר הערך NULL שחרור הזכרון שהוקצה באופן דינמי מתבצע ע"י free .
E N D
תרגול מס' 3 מבנים בעייה לדוגמה לי-טל משיח litalma@cs.technion.ac.il
הקצאת זיכרון דינמית - תזכורת void* malloc(unsigned nbytes) • הקצאת קטע זיכרון בגודל nbytes. אם ההקצאה הצליחה, מוחזר מצביע לקטע הזה בזיכרון, אחרת מוחזר הערך NULL • שחרור הזכרון שהוקצה באופן דינמי מתבצע ע"יfree. void handle_error(const char* msg) { fprintf(stderr,"ERROR: %s\n",msg); exit (1); } int main() { int t , *p; p = (int*) malloc(sizeof(int)); if ( p == NULL) handle_error("out of memory") ; *p = 5; t = * p; free (p); return 0; }
Structures מבנים - • דרך ליצור טיפוסים חדשים ע"י הרכבה מטיפוסים קיימים struct <struct name> { <type name1> <field1>; <type name2> <field2>; . . <type namen> <fieldn>; }; • הגדרת משתנה נעשית ע"י: struct <struct name> <variable name>; • דוגמא - מבנה המגדיר טפוס תאריך. חודשים שמורים בפורמט של שלושה תווים struct Date_t { int day; char[4] month; /* why 4 chars? */ int year; }; • הגדרת משתנה מסוג date: struct Date_t d;
Structures מבנים - • פנייה לשדה בתוך משתנה מסוג struct נעשית ע"י: <variable name>.<field name>; • כאשר נתון מצביע לstruct-, ניתן לגשת לשדה בצורה ישירה: (*<pointer name>).<field name> ; • מכיוון שדרך זו מסורבלת, קיימת דרך נוספת ופשוטה יותר לגשת לשדה: <pointer name> -> <field name> ; • דוגמא: struct Date_t* p; p=&d; p->day=12; /* or (*p).day=12; */ • תוכנית דוגמא: int main() { struct Date_t dt , *pdt; dt.day = 21; strcpy(dt.month , "NOV"); pdt = &dt ; pdt->year = 1971 ; printf ("The year is %d\n",dt.year); }
typedefשם נוסף לטיפוס באמצעות • בדוגמת התאריך נוצר טיפוס בשם struct Date_t. • בכדי להמנע מהצורך ברשום struct בכל פעם, ניתן להעזר ב typedef בכדי לתת לטיפוס שנוצר שם קצר יותר: • דרך א': struct Date_t { .... same as before } ; typedef struct Date_t Date ; • מעתה, השמות Date ו struct Date_t הם שמות שונים לאותו טיפוס • ניתן גם להגדיר טפוס של מצביע למבנה: typedef struct Date_t *pDate; • או typedef Date *pDate; • דרך ב': • ניתן לבצע את שני השלבים יחדיו: typedef struct Date_t { int day; char[4] month; int year; } Date; typedef Date *pDate;
שמוש במבנה בתוך מבנה • ניתן להגדיר בתוך struct שדה מטיפוס struct אחר typedef struct Date_t { int day; char month[4]; int year; } Date ; typedef struct Person_t { char* name; int height; Date birth; } Person ; • גישה לשדה של ה-struct הפנימי: Person p ; Person *pp=&p; p.birth.year = 1994 pp->birth.year = 1994; • מדוע לא pp->birth->year ?
שמוש במצביע למבנה בתוך מבנה • במקום להגדיר את birth כשדה מטפוסdate ניתן היה להגדירו כמצביעל-struct מטיפוס Date typedef struct Date_t { int day; char month[4]; int year; } Date ; typedef struct Person_t { char* name; int height; Date* birth; / * or sturct Date_t* birth; */ } Person ; • גישה לשדה של ה-struct הפנימי: Person p ; Person *pp=&p; p.birth->year = 1994 pp->birth->year = 1994; • שמו לב: כאשר הקצאנו את p Personלא הקצנו כחלק ממנו מקום למבנה מטיפוס Date אלא למצביע אליו ! לכן קטע הקוד הנתון אינו תקין! כיצד ניתן לתקנו?
מבנים המתייחסים לעצמם • בהגדרת מבנה ניתן להשתמש במצביעים מטיפוס מצביע למבנה עצמו: 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 ; strcpy(dt1.month,”DEC”); dt1.year = 1992 ; dt2.day = 1 ; strcpy(dt2.month,”JAN”); dt2.year = 1993 ; pdt = &dt2; dt1.next = &dt2; printf ("Year in dt1: %d\n",dt1.year); printf ("Year in dt2: %d\n",(dt1.next)->year); }
dt1 31 DEC 1992 dt2 1 JAN 1993 ? תמונת הזכרון לפני ביצוע פקודות ההדפסה • ניתן לשנות את ערכו של השדה year של המשתנה dt2 בכמה אופנים: dt2.year = 1994; pdtyear = 1994; (dt1.next) year = 1994; • נוצרה כאן רשימה מקושרתבת שני איברים • מה צריך להיות ערכו של השדה next ב dt2 כדי שנדע שאין איבר נוסף ? pdt
רשימה מקושרת של מבנים המוקצים באופן דינמי • הבעיה: נרצה לקרוא מהקלט מספר לא ידוע של תאריכים, ולהדפיסם בסדר הפוך לסדר הקריאה • פתרון: רשימה מקושרת של תאריכים
רשימה מקושרת של מבנים המוקצים באופן דינמי int main() { Date *top = NULL, *tmp = NULL ; int d,y ; char m[4]; while (scanf("%d %s %d", &d ,m , &y) != EOF){ /* is the check good enough ? */ tmp = (Date*) malloc(sizeof(Date)); if (tmp == NULL) free_list(top); tmp->day = d ; strcpy(tmp->month ,m); tmp->year = y ; tmp->next = top ; top = tmp; } while (tmp != NULL) { printf("%d %s %d\n",tmp->day, tmp->mon, tmp->year); tmp = tmp->next ; } /* what should come here? */ }
רשימה מקושרת של מבנים המוקצים באופן דינמי void free_list(Date *top){ Date *tmp; while (top != NULL) { tmp=top; top=top->next; free(tmp); } }
העתקת מבנים בדרך פשוטה יותר int main() { Date *top = NULL, *tmp = NULL ; Date d; while (scanf("%d %s %d", &d.day ,d.mon , &d.year) == 3){ tmp = (Date*) malloc(sizeof(Date)); if (tmp == NULL) free_list(top); *tmp=d ; tmp->next = top ; top = tmp; } while (tmp != NULL) { printf("%d %s %d\n",tmp->day, tmp->mon, tmp->year); tmp = tmp->next ; } free_list(top); return 0; } int main() { Date *top = NULL, *tmp = NULL ; int d,y ; char m[4]; while (scanf("%d %s %d", &d ,m , &y) != EOF){ /* is the check good enough ? */ tmp = (Date*) malloc(sizeof(Date)); if (tmp == NULL) free_list(top); tmp->day = d ; strcpy(tmp->month ,m); tmp->year = y ; tmp->next = top ; top = tmp; } while (tmp != NULL) { printf("%d %s %d\n",tmp->day, tmp->mon, tmp->year); tmp = tmp->next ; } /* what should come here? */ }
דוגמה נוספת לשימוש במבנים ומצביעים • הבעיה: ברצוננו לכתוב תוכנית המחזירה את האירועים ההסטוריים אשר קרו בתאריך מסויים. התוכנית תקרא קובץ המכיל ארועים הסטוריים, לאחר מכן תקלוט תאריכים ועבור כל אחד מהם תגיד אילו אירועים היסטוריים מאלו שבקובץ קרו באותו התאריך • קובץ ארועים events: 1 JAN 404 Last gladiator competition 6 MAY 1889 Eiffel tower completed 21 NOV 1794 Honolulu harbor discovered 1 JAN 1852 First public bath in NY 1 JAN 379 King basilius the great born 21 NOV 1971 First tackoff of theconcorde 6 MAY 1915 Orson Welles born 6 MAY 1626 Manahatan purchused for 24$ 21 NOV 1971 First landing of the concorde • הפעלת התוכנית והפלט המתקבל: % important_dates events enter date: 21 NOV 1971 First takeoff of the concorde First landing of the concorde enter date: 23 NOV 1999 Nothing special
next next next next description description description description Historical Date date events list next פתרון אפשרי: hist נשמור רשימה מקושרת של תאריכים לכל תאריך נשמור רשימה של ארועים שקרו בתאריך זה event date events list next date events list next
מיבנים: הגדרות #define MAX 100 /* Max line lenth*/ מבנה תאריך typedef struct Date_t { int day; char[4] month; int year; } Date; מבנה אירוע typedef struct Event_t { char* desc ; struct Event_t *next; } Event; מבנה תאריך הסטורי typedef struct HistoricalDate_t { Date dt ; Event *events_list; struct HistoricalDate_t *next ; } HistoricalDate;
פונקציות עזר – טיפול בקלט קריאת תאריך מערוץ קלט נתון (הקצאת התאריך באחריות הקורא !) int getDate(FILE* fd, Date* dt) { if (dt == NULL) return 0 ; if (fscanf (fd, “%d %s %d ”, &(dt->day),dt->month, &(dt->year))==3) return 1; return 0; } קריאת שורת ארוע הסטורי מערוץ קלט נתון. (הקצאת התאריך, והמחרוזת לתיאור הארוע באחריות הקורא !) int getEvent(FILE* fd, Date* dat, char* bf) { if (dat == NULL || bf == NULL) return 0 ; if (getDate(fd,dat) ==0 || fgets(bf, MAX, fd)==NULL) return 0; return 1; }
פונקציות עזר – הקצאת צמתים ואתחולם הקצאת צומת ארוע הסטורי Event* allocEvent(char* ev) { Event *new_event; if (ev == NULL) return NULL ; new_event=(Event*)malloc(sizeof(Event)); if (new_event== NULL) return NULL; new_event->desc=(char*)malloc(strlen(ev)+1); if (new_event->desc == NULL) { free (new_event) ; return NULL; } strcpy(new_event->desc,ev); new_event->next = NULL ; return new_event ; }
פונקציות עזר – הקצאת צמתים ואתחולם הקצאת צומת תאריך הסטורי HistoricalDate* allocHistoriaclDate(Date dt) { HistoricalDate *new_hdate; new_hdate=(HistoricalDate*) malloc(sizeof(HistoricalDate)); if (new_hdate == NULL) return NULL ; new_hdate ->dt = dt ; new_hdate ->events_list = NULL; new_hdate ->next = NULL ; return new_hdate ; }
פונקציות עזר – טיפול במבנה הנתונים מציאת תאריך הסטורי ברשימת תאריכים HistoricalDate* find(HistoricalDate* first,Date dt){ if (first == NULL) return NULL ; if (first->dt.day == dt.day && strcmp(first->dt.month,dt.month) == 0 && first->year == dt.year) return first ; return find(first->next,dt); } הדפסת רשימת ארועים הסטוריים: void printEvents(Event* ev) { if ( ev == NULL ) return; printf (“%s\n”, ev->desc); printEvents(ev->next); }
פונקציות עזר – טיפול במבנה הנתונים בניית מבנה הנתונים HistoricalDate* readEvents(FILE* fd) { char buff[MAX]; Date dt ; HistoricalDate *first=NULL , *cur = NULL; Event *ev =NULL; while (getEvent(fd,&dt,buff)) { cur = find(first,dt) ; if (cur == NULL) { /* incase the date doesn't exist, add this date to be the first in the list */ cur = allocHistoricalDate(dt); if (cur == NULL) return NULL ; cur->next = first ; first = cur ; } ev = allocEvent(buff); if (ev == NULL) return NULL ; ev->next = cur->events_list; cur->events_list = ev ; }return first ; }
התוכנית הראשית int main(int argc, char* argv[]) { FILE* ifd = NULL; HistoricalDate* hist = NULL , *cur = NULL; Date dt ; if (argc != 2) return 1 ; if ((ifd = fopen(argv[1],”r”))==NULL) return 2 ; hist = readEvents(ifd); /*creating the database*/ fclose(ifd); if (hist == NULL) return 3 ; while (getDate(stdin,&dt)) { cur = find(hist,dt) ; if (cur == NULL) printf (“Nothing special\n”); else printEvents(cur->events_list); } return 0; }
שיפורים אפשריים • שחרור הזכרון בסוף התוכנית. • שחרור הזכרון שכבר הוקצה במידה וקרה כשלון במהלך ריצת הפונקציה readEvents. • הודעות שגיאה ברורות למשתמש.