PowerPoint Presentation

מסמכים קשורים
PowerPoint Presentation

PowerPoint Presentation

Slide 1

תכנות מונחה עצמים א' – תש"ע

Microsoft PowerPoint - rec1.ppt

Slide 1

Microsoft Word B

סדנת תכנות ב C/C++

Microsoft Word - c_SimA_MoedB2005.doc

הגשה תוך שבוע בשעת התרגול

פתרון מוצע לבחינת מה"ט ב_שפת c מועד ב אביב תשע"ט, אפריל 2019 מחברת: גב' זהבה לביא, מכללת אורט רחובות שאלה מספר 1 מוגדרת מחרוזת המורכבת מהספרות 0 עד 9.

PRESENTATION NAME

PowerPoint Presentation

PowerPoint Presentation

שאלהIgal : מערכים דו מימדיים רקורסיה:

Slide 1

Slide 1

שאלהIgal : מערכים דו מימדיים רקורסיה:

Microsoft PowerPoint - rec3.ppt

Slide 1

מספר מחברת: עמוד 1 מתוך 11 ת"ז: תשע"א מועד ב סמסטר א' תאריך: 00:11 שעה: 0 שעות הבחינה: משך כל חומר עזר אסור בשימוש בחינה בקורס: מבוא למדעי ה

Slide 1

מבוא למדעי המחשב

שאלהIgal : מערכים דו מימדיים רקורסיה:

Slide 1

Slide 1

PowerPoint Presentation

PowerPoint Presentation

הגשה תוך שבוע בשעת התרגול

PowerPoint Presentation

Microsoft PowerPoint - lec2.ppt

אוניברסיטת חיפה החוג למדעי המחשב מרצה: שולי וינטנר מתרגלים: נעמה טוויטו, מחמוד שריף מבוא למדעי המחשב סמסטר א' תשע"ב בחינת סיום, מועד א', הנחי

אוניברסיטת חיפה החוג למדעי המחשב מרצה: שולי וינטנר מתרגלים: נעמה טוויטו, מחמוד שריף מבוא למדעי המחשב סמסטר א' תשע"ב בחינת סיום, מועד א', הנחי

אוניברסיטת חיפה החוג למדעי המחשב.5.6 מבוא למדעי המחשב סמסטר א' תשע"ז בחינה סופית מועד א', מרצה: שולי וינטנר מתרגלים: סמאח אידריס, ראמי עילבו

מבוא למדעי המחשב

מבוא למדעי המחשב

מבחן סוף סמסטר מועד א 15/02/08 מרצה אחראית: דר שירלי הלוי גינסברג מתרגלים: גלעד קותיאל, דניאל גנקין הוראות: א. בטופס המבחן 7 עמודים ו 4 דפי נוסחאות. ב

תרגול 1

מבוא לתכנות ב- JAVA תרגול 7

מספר זהות: סמסטר ב' מועד א' תאריך: 11102/4// שעה: 9:22 משך הבחינה: 3 שעות חומר עזר: אין מותר השימוש במחשבון פשוט בחינה בקורס: מבני נתונים מרצה: הדר בי

מבוא למדעי המחשב - חובלים

מבוא למדעי המחשב - חובלים

úåëðä 1 - çæøä

שבוע 4 סינטקס של HACK ASSEMBLY ניתן להשתמש בשלושה אוגרים בלבד:,A,D,M כולם בעלי 16 ביטים. M אינו אוגר ישיר- הוא מסמן את האוגר של ה RAM שאנחנו מצביעים ע

מבוא למדעי המחשב

Slide 1

אוניברסיטת חיפה החוג למדעי המחשב מבוא למדעי המחשב מועד א' סמסטר ב', תשע"ג, משך המבחן: שעתיים וחצי חומר עזר: אסור הנחיות: וודאו כי יש בידיכם

Tutorial 11

אוניברסיטת בן גוריון בנגב תאריך המבחן: שקולניק אלכסנדר שם המרצה: מר בשפת JAVA מבוא לתכנות מבחן ב: מס' הקורס : הנדסת תעשיה וניהול מ

שקופית 1

Slide 1

מהוא לתכנות ב- JAVA מעבדה 3

Slide 1

מבוא לתכנות ב- JAVA תרגול 11

מבחן 7002 פרטים כלליים מועד הבחינה: בכל זמן מספר השאלון: 1 משך הבחינה: 3 שעות חומר עזר בשימוש: הכל )ספרים ומחברות( המלצות: קרא המלצות לפני הבחינה ובדי

PowerPoint Presentation

משימה תכנית המתרגמת קטעי טקסט לשפה אחרת הקלט: קובץ המכיל את קטעי הטקסט וכן את השפה אליה רוצים לתרגם תרגול מס' 4: המתרגם שימוש במחלקות קיימות תכנות מתק

תוכן העניינים

Microsoft PowerPoint - T-10.ppt [Compatibility Mode]

תוכן העניינים

תרגול מס' 4: המתרגם שימוש במחלקות קיימות מחרוזות, קבצים, וקבלת קלט מהמשתמש

מצגת של PowerPoint

Microsoft Word - Ass1Bgu2019b_java docx

מבחן סוף סמסטר מועד ב 28/10/08 מרצה אחראית: דר שירלי הלוי גינסברג מתרגלים: גלעד קותיאל, גדי אלכסנדרוביץ הוראות: א. בטופס המבחן 6 עמודים (כולל דף זה) ו

תשע"דד אביב תוכנה 1 תרגיל מספר 4 עיבוד מחרוזות וקריאה מקבצים הנחיות כלליות: קראו בעיון את קובץ נהלי הגשת התרגילים אשר נמצא באתר הקורס..(

Microsoft Word - דוגמאות ב

PowerPoint Presentation

תרגיל בית מספר 1#

מבוא למדעי המחשב, סמסטר א', תשס"ח תרגול מס' 2

תאריך פרסום: תאריך הגשה: מבנה נתונים תרגיל 5 )תיאורטי( מרצה ומתרגל אחראים: צחי רוזן, דינה סבטליצקי נהלי הגשת עבודה: -את העבודה יש לה

2013/14 אוניברסיטת חיפה מבוא למדעי מחשב, מעבדה מטרת המעבדה: לתרגל את המעבר מאלגוריתם לקוד C כמה שיותר. הוראות:.1.2 ניתן לעבוד ביחידים או בזוגות. (יש מ

תורת החישוביות תרגול הכנה לוגיקה ותורת הקבוצות מה יש כאן? בקורס תורת החישוביות נניח ידע בסיסי בתורת הקבוצות ובלוגיקה, והכרות עם מושגים בסיסיים כמו א"ב

בס"ד תרגיל 3 מועד אחרון ל כללי בתרגיל זה עליכם לכתוב תוכנה שמדמה מאגר נתונים של חנות. את מוצרי החנות תייצגו באמצעות עצים ורשימות מקושרות יהיה עליכם לנ

מבוא לאסמבלי

אוניברסיטת בן גוריון בנגב תאריך המבחן: שם המרצה: מר אלכסנדר שקולניק, בשפת JAVA מבחן ב: מבוא לתכנות מס' הקורס : מיועד לתלמידי : הנד

Homework Dry 3

מקביליות

PowerPoint Presentation

1 תבניות טקסט מהי תבנית טקסט? שימוש ב- Characters Meta שימוש ב- Expression Grouping שימוש ב- Quantifiers תת תבניות הפונקציה preg_match הפונקציה preg_m

שעור 6

מבוא למדעי המחשב

234114

מקביליות

HTML - Hipper Text Makeup Language

מבוא למדעי המחשב

Slide 1

Microsoft PowerPoint - lec9.ppt

פייתון

יצוא לחשבשבת תוכן עיניינים הגדרות - חשבונות בנק...2 הגדרות - הגדרות חשבשבת... 3 הגדרות - כרטיסי אשראי... 4 הגדרות - סוגי הכנסה... 5 יצוא לחשבשבת...6 י

Microsoft Word - c_SimA_MoedA2006.doc

תרגול מס' 1

Microsoft PowerPoint - lec10.ppt

מערכות הפעלה

Microsoft Word - pitaron222Java_2007.doc

Programming

תמליל:

מצביעים הקצאת זיכרון דינאמית מבנים - Structures טיפוסי נתונים - types Data העברת פרמטרים ל- main טענות נכונות

שימוש בסיסי אריתמטיקת מצביעים void* מצביע למצביע 2

כתובת ערך הבית int התופס 4 בתים 7 0 0 0 0 0 0 0 104 101 108 108 111 0 0x0200 0x0201 0x0202 0x0203 0x0204 0x0205 0x0206 0x0207 0x0208 0x0209 0x020A 0x020B 0x020C 0x020D הזיכרון מורכב מתאים הקרויים בתים: כל לכל בית מכיל ערך מספרי פירוש הערכים כערך לא מספרי הוא ע"י התכנית בית יש שם כתובת הכתובת היא מיקומו בזיכרון בד"כ כתובות בזיכרון נרשמות בבסיס הקסדצימלי )16( משתנים מאוחסנים בבתים: טיפוסים שונים דורשים מספר שונה של בתים, למשל: int צורך 4 או 8 char בתים צורך בית יחיד. מחרוזת hello המורכבת ממספר תווים 3

עבור טיפוס T נקרא ל-* T מצביע ל- T למשל int* הוא מצביע ל- int 0x208 34 37 0 8 20 11 9 0x0204 0x0205 0x0206 0x0207 0x0208 0x020A 0x020B 0x020C מצביע מסוג *T הוא משתנה אשר שומר כתובת של משתנה מטיפוס T. ניתן לקבל את כתובתו של משתנה ע"י שימוש באופרטור & לא ניתן להשתמש ב-& על ביטויים או קבועים )מדוע?( ניתן לקרוא את ערכו של המצביע ע"י אופרטור * פעולה זו קרויה dereferencing int n = 5; int* ptr = &n; // ptr now points to n printf("%d",*ptr); // dereferencing ptr 4

הכתובת 0 הינה כתובת לא חוקית: אף עצם אינו יכול להיות בעל כתובת זו ניתן לעשות שימוש בכתובת זו כדי לציין שמצביע מסוים אינו מצביע לאף עצם כרגע השתמשו ב- NULL כאשר אתם מתייחסים לכתובת ולא בקבוע 0 שימוש בקבועים כאלו משפר את קריאות הקוד נסיון לקרוא מצביע המכיל את הכתובת NULL יגרום לקריסת התוכנה. ב- UNIX תתקבל ההודעה: segmentation fault גישה למצביע המכיל "זבל" תגרום לתכנית להתנהג בצורה לא צפויה אסור להשאיר מצביעים לא מאותחלים בקוד! ניתן להכריז על המשתנה מאוחר יותר (C99) במקרה ולא ניתן יש לאתחל אותו ל- NULL 5

ניתן בנוסף לבצע פעולות חשבוניות חיבור מספרים שלמים: על מצביעים int n =...; int* ptr2 = ptr + n; התוצאה היא כתובתו של המשתנה מטיפוס מתאים n תאים חיסור שני מצביעים: קדימה/אחורה int diff = ptr2 - ptr; התוצאה היא מספר שלם (int) פעולות אלו מאפשרות להסתכל על המשתנה הבא/הקודם בזיכרון הכרחי לשימוש במערכים ומחרוזות מסוכן טעויות חשבוניות עלולות לגרום לקריאת "זבל" מהזיכרון שאלה: מדוע לא ניתן לחבר שני מצביעים? 6

ניתן להשתמש במצביע כדי לגשת למשתנים הנמצאים בהמשך בזיכרון, למשל כך: int* ptr =...; int n = *(ptr + 5); int n = ptr[5]; האופרטור [ ] משמש כקיצור לפעולה זו: כלומר הפעולות הבאות שקולות: *(ptr + n) ptr[n] 7

מערכים ומצביעים מתנהגים בצורה דומה ניתן להשתמש בשם המערך כמצביע לאיבר הראשון בו כאשר שולחים מערך לפונקציה ניתן לשלוח אותו כמצביע: void sort(int* array, int size); מצביע יכול לשמש כאיטרטור עבור מערך int array[n]; //... ב- C99 ניתן for(int* ptr = array; ptr < array+n; ptr++) { להכריז על משתנה בתוך printf("%d ",*ptr); לולאת for הבדלים: הכרזה על מערך מקצה זיכרון כגודל המערך, הכרזה על מצביע אינה מקצה זיכרון לאחסון המשתנים! ניתן לשנות את ערכו של מצביע, אך לא ניתן לשנות את "ערכו" של תחילת המערך 8

ניתן להגדיר מצביעים מטיפוס.void* מצביעים אלו יכולים לקבל את כתובתו של כל משתנה לא ניתן לקרוא מצביע מטיפוס,void* יש להמירו קודם לכן int n = 5; double d = 3.14; void* ptr = &n; ptr = &d; double d2 = *ptr; // Error: cannot dereference void* double d3 = *(double*)ptr; // O.K. option 1 double* dptr = ptr; // Implicit cast from void* to double* double d4 = *dptr; // O.K. option 2 9

T** ניתן ליצור מצביע לכל טיפוס, בפרט עבור טיפוס *T מתקבל מצביע למצביע של T אפשר להמשיך לכל מספר של * ניתן ליצור מצביע מטיפוס דוגמאות: שליחת מערך של מצביעים לפונקציה: void sort_pointers(int** array, int size); כתיבת פונקצית swap עבור מחרוזות: void swap_strings(char** str1, char** str2) { char* temp = *str1; *str1 = *str2; *str2 = temp; מדוע יש כאן צורך במצביע למצביע? 10

מצביעים משמשים להתייחסות לתאי זיכרון ניתן לקבל את כתובתו של משתנה ע"י אופרטור & ניתן לקרוא ממצביע ולקבל את הערך המוצבע ע"י * הערך NULL מציין שאין עצם מוצבע ואסור לקרוא אותו ניתן לבצע פעולות חשבוניות על מצביעים מאפשר התייחסות למצביעים בדומה למערכים חשוב לאתחל מצביעים הרצת קוד הניגש למצביעים המכילים ערך לא תקין תגרום להתנהגות לא מוגדרת הכרזה על מצביע אינה מאתחלת זיכרון עבור המשתנה המוצבע! מצביע מטיפוס void* יכול להצביע לעצם מכל סוג ומשמש לכתיבת קוד גנרי 11

סוגי משתנים הקצאת זיכרון שחרור זיכרון נזילות זיכרון 12

את המשתנים השונים בקוד ניתן לסווג לפי טווח ההכרה ואורך חייהם: משתנים מקומיים: משתנים פנימיים של פונקציות. נגישים רק בבלוק בו הם הוגדרו. משתנים אלו מוקצים בכל פעם שהבלוק מורץ ומשוחררים בסופו. משתנים גלובליים: משתנים אשר מוגדרים לכל אורך התכנית וניתן לגשת אליהם מכל מקום. המשתנים מוקצים כאשר התכנית מתחילה ונשמרים לכל אורך זמן הריצה משתנים סטטיים של פונקציה: משתנים פנימיים של פונקציה. משתנים אלו שומרים על ערכם בין הקריאות השונות לפונקציה. מאותחלים בריצה הראשונה של הפונקציה, משוחררים בסוף ריצת התכנית משתנים דינאמיים: מוקצים ומשוחררים ע"י קריאה מפורשת לפונקציה 13

משתנים גלובליים, משתנים סטטיים של קובץ ומשתנים סטטיים של פונקציה נחשבים לתכנות רע הסיבה העיקרית לכך - שימוש במשתנים אלו מקשה על הבנת ודיבוג הקוד: כדי להבין פונקציה המשתמשת במשתנה גלובלי יש להסתכל בקוד נוסף קשה לצפות את תוצאת הפונקציה כי היא אינה תלויה רק בפרמטרים שלה קשה לצפות השלכות של שינויים על ערך המשתנה בשימוש במשתנה סטטי של פונקציה - בשביל לצפות את תוצאת הפונקציה צריך לדעת מה קרה בהרצות קודמות אין להשתמש במשתנים גלובליים במת"מ בקורסים מתקדמים בהמשך התואר תראו מקרים בהם חובה או מומלץ להשתמש במשתנים כאלו 14

משתנים דינאמיים הם משתנים שזמן החיים שלהם הוא בשליטת המתכנת קוד מפורש מקצה אותם וקוד מפורש דרוש לשחרורם המשתנים מוקצים באזור זיכרון שקרוי ה- heap בניגוד למשתנים מקומיים המוקצים על מחסנית הקריאות, ה- stack משתמשים בהם כאשר: צריך ליצור מערך שגודלו אינו ידוע מראש יש צורך לשמור נתונים בזיכרון גם לאחר יציאה מהפונקציה הגישה למשתנים אלו נעשית תמיד בעזרת מצביעים 15

כדי להקצות זיכרון נשתמש בפונקציה :malloc void* malloc(size_t bytes); malloc מקבלת גודל בבתים של זיכרון אותו עליה להקצות ערך החזרה מכיל מצביע לתחילת גוש הזיכרון שהוקצה התוצאה היא תמיד גוש זיכרון רציף במקרה של כשלון מוחזר NULL לאחר מכן ניתן להתייחס לשטח המוצבע כאל משתנה או מערך: int* my_array = malloc(sizeof(int) * n); for (int i=0; i<n; i++) { my_array[i] = i; 16

כיצר נדע כמה בתים עלינו להקצות עבור משתנה מסוג נסיון ראשון: 4 הוא מספר קסם מספרי קסם הם הרגל תכנותי רע:?int int* ptr = malloc(4); פוגעים בקריאות הקוד - מספר לא ברור המופיע בקוד שאינו 0 או 1 מקשים על שינויים עתידיים בקוד למשל מעבר לסביבה בה גודלו של int הוא 8 ככל שצריך יותר שינויים הסיכוי לפספס אחד מהם גדל יש להימנע ממספרי קסם: ע"י הגדרת קבועים בעזרת ע"י שמירת ערכם במשתנה קבוע בעל שם ברור #define שיקלו על שינויים ועל קריאת הקוד 17

int נסיון שני - נגדיר את הגודל של כקבוע: #define SIZE_OF_INT 4 int* ptr = malloc(size_of_int); עכשיו הקוד קריא וקל לשנות את הערך אבל אם נעביר את הקוד לסביבה אחרת עדיין נצטרך לעדכן את הערך קוד שדורש שינויים במעבר בין סביבות שונות נקרא non-portable 18

sizeof אשר מחזיר את הגודל המתאים: int* ptr = malloc(sizeof(int)); נשתמש באופרטור ניתן להפעיל את sizeof על שמות טיפוסים או על משתנים עבור שם טיפוס יוחזר הגודל בבתים של הטיפוס: int* ptr = malloc(sizeof(int)); עבור הפעלה על משתנה יוחזר הגודל של הטיפוס של המשתנה בבתים: int* ptr = malloc(sizeof(*ptr)); // = sizeof(int) למה השיטה שימו לב להבדל בין גודל של מצביע לגודל העצם המוצבע הזו עדיפה? מה נעשה אם ברצוננו להקצות זיכרון לעותק של מחרוזת? char* str = "This is a string"; char* copy = malloc(sizeof(char)*(strlen(str)+1)); למה צריך 1+? אפשר להוריד את,sizeof(char) מובטח שהוא תמיד 1 19

NULL עלולה להיכשל בהקצאת הזיכרון - במקרה זה מוחזר malloc מה קורה במקרה זה אם malloc נכשלת? int* my_array = malloc (sizeof(int) * n); for (int i=0; i<n; i++) { my_array[i] = i; הפתרון: בדיקת ערך ההחזרה של פונקציות העלולות להיכשל וטיפול בו הטיפול צריך להופיע מיד לאחר ההקצאה ולפני השימוש הראשון int* my_array = malloc(sizeof(int) * n); if (my_array == NULL) { // or!my_array handle_memory_error(); בהמשך נראה מקרים נוספים של שגיאות יותר שכיחות ופשוטות להתמודדות 20

הפונקציה free משמשת לשחרור גוש זיכרון שהוקצה ע"י malloc void free(void* ptr); המצביע שנשלח ל- free חייב להצביע לתחילת גוש הזיכרון )אותו ערך שהתקבל מ- )malloc לאחר שחרור הזיכרון אסור לגשת יותר לערכים בזיכרון ששוחרר אם שולחים NULL ל- free לא מתבצע כלום כלומר אין צורך לבדוק את הפרמטר הנשלח ולוודא שאינו NULL למה זה טוב? אסור לשחרר את אותו זיכרון פעמיים או לשלוח ל- free מצביע שאינו מצביע לתחילת גוש זיכרון שהוקצה דינאמית )או )NULL int* my_array = malloc(sizeof(int) * n); //... using my_array... free(my_array); 21

במקרה ונשלח NULL ל- free לא מתבצע כלום ניתן להחליף את הקוד הקודם בזה: free הוא מקרה קצה עבור NULL if (ptr!= NULL) { free(ptr); free(ptr); מה היה קורה אם free לא היתה מתמודדת עם מקרה הקצה הזה? עדיף לטפל במקרי קצה בתוך הפונקציה מונע מהמשתמש בה ליצור באגים ושכפולי קוד 22

גישה לכתובת זיכרון שאינה מוקצה שחרור כפול של כתובת זיכרון אינו מוגדר )או הוקצתה ושוחררה( אינה מוגדרת קוד שתוצאתו אינה מוגדרת הוא קוד שמתקמפל ורץ אך אינו מחשב את הערכים הצפויים. נותן תוצאות שאינן צפויות בחלק מהמקרים התוצאה שתוחזר אכן מתאימה לציפיות קוד שאינו מוגדר הוא באג קשה לטיפול קשה לצפות את התנהגותו והשלכותיו יכול להשפיע על משתנים באזור אחד בקוד חשוב להקפיד על שימוש נכון בשפה כדי להימנע ממקרים אלו 23

האם שתי התכניות הבאות מתנהגות בצורה זהה? #include <stdio.h> #define N 7 #include <stdio.h> #define N 7 int main() { int a[n] = {0; int i; for (i=0; i < N; i++) { printf("%d\n", i); a[n-1-(i+1)] = a[i]; return 0; int main() { int i; int a[n] = {0; for (i=0; i < N; i++) { printf("%d\n", i); a[n-1-(i+1)] = a[i]; return 0; 24

דליפת זיכרון מתרחשת כאשר שוכחים לשחרר זיכרון שהוקצה: void sort(int* array, int n) { int* copy = malloc(sizeof(int) * n); //... some code without free(copy) return; דליפת זיכרון אינה גורמת ישירות לשגיאות בהתנהגות התוכנה דליפת זיכרון יגרמו לצריכת זיכרון גדלה של התוכנה ככל שזמן ריצתה גדל ולהאטת התוכנה ומערכת ההפעלה כולה תחת UNIX ניתן להשתמש בכלי valgrind לאיתור דליפות זיכרון valgrind מריץ את התכנית שלכם ומחפש גושי זיכרון שהוקצו אך לא שוחררו ניתן למצוא מידע נוסף על השימוש ב- valgrind בתרגול עזר 3 25

כדי להימנע מכל הבעיות שתוארו כאשר עובדים עם הקצאות דינאמיות קיים רק פתרון אחד יעיל - עבודה מסודרת בעזרת עבודה מסודרת ניתן לשמור על הקוד פשוט יותר קוד מסובך מקל על הכנסת באגים בטעות הטיפול בבאגים קשה יותר אם הקוד מסובך 26

מומלץ לא להשתמש במשתנים גלובליים וסטטיים ניתן להשתמש ב- malloc ו- free כדי להקצות ולשחרר זיכרון בצורה מפורשת עבור יצירת מערכים בגודל לא ידוע עבור שמירת ערכים לאורך התכנית ניהול הזיכרון מתבצע ע"י מצביעים לתחילת גוש הזיכרון שהוקצה יש לבדוק הצלחת הקצאת זיכרון יש לזכור לשחרר את הזיכרון המוקצה כאשר אין בו צורך יותר ניתן להשתמש ב- valgrind כדי למצוא בקלות גישות לא מוגדרות לזיכרון 27

הגדרת מבנה פעולות על מבנים typedef 28

נניח שברצוננו לכתוב תוכנה לניהול אנשי קשר, לכל איש קשר נשמור: שם פרטי, שם משפחה, מספר טלפון, כתובת e-mail וכתובת מגורים. לשם כך נצטרך לשמור 5 מערכים שונים! כל פונקציה שתצטרך לקבל את פרטיו של איש קשר כלשהו תצטרך לקבל 5 פרמטרים שונים לפחות! void somefunction(char* firstname, char* lastname, char* address, char* email, int number,... more?); כדי להימנע מריבוי משתנים ניתן להגדיר טיפוסים חדשים המהווים הרכבה של מספר טיפוסים קיימים void somefunction(contact contact,...); 29

ניתן להגדיר טיפוסים חדשים המהווים הרכבה של מספר טיפוסים קיימים בעזרת המילה השמורה :struct struct <name> { <typename 1> <field name 1>; <typename 2> <field name 2>;... <typename n> <field name n>; <declarations>; הטיפוס החדש מורכב משדות: לכל שדה יש שם טיפוס השדה נקבע לפי הגדרת המבנה המבנים נשמרים בזיכרון ברצף ניתן להשתמש במערכים בעלי גודל קבוע כשדות - כל המערך נשמר במבנה ניתן להשתמש במצביעים כשדות - במקרה זה הערך המוצבע אינו חלק מהמבנה 30

birth struct point { double x; double y; ; struct date { int day; char month[4]; int year; ; struct person { char* name; struct Date birth; ; 31 למה 4? point x=3.0 y=2.5 person name=0x0ffef6 day=31 month="mar" year=1953 date day=31 month="nov" year=1971 "Ehud Banai" כל המערך נשמר בתוך המבנה המחרוזת נשמרת מחוץ למבנה

הטיפוס החדש מוגדר בשם <name> struct כדי לגשת לשדות של משתנה מטיפוס המבנה נשתמש באופרטור. )נקודה( struct point p; p.x = 3.0; p.y = 2.5; double distance = sqrt(p.x * p.x + p.y * p.y); עבור מצביע למבנה ניתן להשתמש באופרטור החץ >- struct point* p = malloc(sizeof(*p)); מה חסר? (*p).x = 3.0; // Must use parentheses, annoying p->y = 2.5; // Same thing, only clearer double distance = sqrt(p->x * p->x + p->y * p->y); 32

ניתן לאתחל מבנים בעזרת התחביר הבא: struct date d = { 31, "NOV", 1970 ; ניתן לבצע השמה בין מבנים מאותו הטיפוס: struct date d1,d2; //... d1 = d2; במקרה זה מתבצעת השמה בין כל שני שדות תואמים מבנים מועברים ומוחזרים מפונקציות by value כלומר מועתקים גם במקרה זה מתבצעת ההעתקה שדה-שדה הפעולות האלו אינן מתאימות למבנים מסובכים יותר )בד"כ בגלל מצביעים( 33

birth birth מבנים המכילים מצביעים אינם מתאימים בדרך כלל לביצוע השמות והעתקות מה יקרה אם נבצע השמה בין שני המבנים בדוגמה זו? person1 person2 name=0x0ffef6 "Ehud Banai" name=0x0ffed0 "Yuval Banai" day=31 day=9 month="mar" month="jun" year=1953 year=1962 מסיבה זו וכדי למנוע העתקות כבדות ומיותרות של מבנים בדרך כלל במבנים ע"י מצביעים נשתמש נשלח לפונקציות )ונקבל כערכי חזרה( מצביעים למבנה יוצא הדופן הוא מבנים קטנים ופשוטים כגון point 34

המילה השמורה typedef משמשת להגדרת טיפוסים חדשים ע"י נתינת שם חדש לטיפוס קיים typedef int length; פקודת typedef עובדת על שורת הכרזה של משתנה אך מגדירה טיפוס חדש במקום משתנה. נשתמש בפקודת typedef כדי לתת שמות נוחים לטיפוסים: typedef struct point Point; במקרה זה נוכל להתייחס למבנה מעכשיו כ- Point )ללא המילה השמורה )struct נוח לתת שם גם לטיפוס המצביע למבנה: typedef struct date Date, *pdate; עבור מבנים מסובכים נשתמש תמיד במצביעים ולכן במקרים האלו נשמור את השם ה"נוח" לטיפוס המצביע: typedef struct person *Person; 35

ניתן להוסיף typedef ישירות על הגדרת המבנה: typedef struct point { double x; double y; Point; ניתן להשמיט את שם הטיפוס בהגדרה ולהשאיר רק את השם החדש: typedef enum { RED, GREEN, BLUE Color; typedef struct { double x; double y; Point; 36

מבנים מאפשרים הרכבה של מספר טיפוסים קיימים כדי להקל על קריאות הקוד מבנה מורכב משדות בעלי שם ניתן לגשת לשדות ע"י האופרטורים.-> ו-. העתקה והשמה של מבנים בטוחה כל עוד אין בהם מצביעים מומלץ להשתמש ב- typedef כדי לתת שם נוח לטיפוס החדש 37

38

typedef struct date_t { int day; char month[4]; int year; Date; אלו בעיות יש בקוד הזה? int main() { Date d1 = {21, "NOV", 1970; Date d2; scanf("%d %3s %d", &d2.day, d2.month, &d2.year); printf("%d %s %d\n", d1.day, d1.month, d1.year); printf("%d %s %d\n", d2.day, d2.month, d2.year); // deja-vu if (d1.day == d2.day && d1.year == d2.year) { printf("the dates are equal\n"); return 0; strcmp(d1.month,d2.month) == 0 && 39

תאריך הוא יותר מהרכבה של שני מספרים שלמים וארבעה תווים לא כל צירוף של ערכים עבור המבנה Date הוא אכן תאריך חוקי - אין חודש מתאים ל- BLA 5 BLA 2010-31 SEP 1978 ב-ספטמבר יש רק 30 ימים - 29 FEB 2010 בפברואר 2010 יש רק 28 ימים מי שמשתמש במבנה התאריך צפוי להשתמש בו בצורות מסוימות הדפסת תאריך מציאת התאריך המוקדם יותר מבין שני תאריכים מציאת מספר הימים בין שני תאריכים 40

כדי לוודא את נכונות השימוש בתאריכים ולמנוע את שכפולי הקוד בשימוש בתאריכים עלינו לכתוב פונקציות מתאימות לטיפול בתאריכים לצירוף של טיפוס והפעולות האפשריות עליו קוראים טיפוס נתונים - Data type טיפוסי הנתונים המובנים בשפה נקראים טיפוסי נתונים פרימטיביים למשל float,int ומצביעים )לכל אחד מהם פעולות שונות אפשריות( יצירת טיפוסי נתונים מהווה את הבסיס לכתיבת תוכנה גדולה בצורה מסודרת ופשוטה 41

#include <stdio.h> #include <string.h> #include <stdbool.h> typedef struct Date_t { int day; char month[4]; int year; Date; const int MIN_DAY = 1; const int MAX_DAY = 31; const int INVALID_MONTH = 0; const int MIN_MONTH = 1; const int MAX_MONTH = 12; const int DAYS_IN_YEAR = 365; מבצעים include רק לקבצים שהכרחיים לקמפול הקוד: ו- scanf printf עבור - stdio.h strcmp עבור - string.h bool עבור הגדרת הטיפוס - stdbool.h הגדרת קבועים const char* const months[] = { "JAN", "FEB", "MAR", " APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" ; 42

/** writes the date to the standard output */ void dateprint(date date); /** Reads a date from the standard input. * Returns true if valid, false otherwise */ bool dateread(date* date); /** Returns true if both dates are identical */ bool dateequals(date date1, Date date2); /** Returns the number of days between the dates */ int datedifference(date date1, Date date2); /** Translates a month string to an integer */ int monthtoint(char* month); /** Calculates the number of days since 01/01/0000 */ int datetodays(date date); מומלץ לתעד לפחות בקצרה את משמעות הפונקציות מעל הכרזתן תיעוד צריך להופיע מעל הפונקציה ולא בתוכה הערות באמצע הקוד בד"כ מיותרות או מסבירות קוד שהיה צריך להיכתב ברור יותר /** Checks if the date has valid values */ bool dateisvalid(date date); 43

int monthtoint(char* month) { for (int i = MIN_MONTH; i <= MAX_MONTH; i++) { if (strcmp(month, months[i - 1]) == 0) { return i; return INVALID_MONTH; int datetodays(date date) { int month = monthtoint(date.month); return date.day + month*(max_day - MIN_DAY + 1) + DAYS_IN_YEAR * date.year; bool dateisvalid(date date) { return date.day >= MIN_DAY && date.day <= MAX_DAY && monthtoint(date.month)!= INVALID_MONTH; 44

void dateprint(date date) { printf("%d %s %d\n", date.day, date.month, date.year); יש לבדוק את תקינות הקלט בכניסה לפונקציה במיוחד מצביעים! bool dateread(date* date) { if (date == NULL) { return false; if (scanf("%d %s %d", &(date->day), date->month, &(date->year))!= 3) { המנעו משכפול קוד, אם קוד כלשהו כבר נכתב הקפידו לקרוא לפונקציה המבצעת אותו ולא לכתוב אותו מחדש! אם אין פונקציה מתאימה וקוד חוזר על עצמו - יש לכתוב פונקצית עזר ולקרוא לה! return false; return dateisvalid(*date); 45

bool dateequals(date date1, Date date2) { return date1.day == date2.day && strcmp(date1.month,date2.month) == 0 && date1.year == date2.year; int datedifference(date date1, Date date2) { int days1 = datetodays(date1); int days2 = datetodays(date2); return days1 - days2; 46

int main() { Date date1 = { 21, "NOV", 1970 ; Date date2; if(!dateread(&date2)) { printf("invalid date\n"); return 0; dateprint(date1); dateprint(date2); if (dateequals(date1,date2)) { printf("the dates are equal\n"); else { int diff = datedifference(date1,date2); printf("the dates are %d days apart\n", abs(diff)); return 0; 47

כאשר מגדירים טיפוס חדש יש להגדיר גם פונקציות מתאימות עבורו יש להגדיר פונקציות עבור הפעולות הבסיסיות שיצטרך המשתמש בטיפוס יש להגדיר פונקציות כך שתשמורנה על ערכים חוקיים של הטיפוס ותמנענה באגים יצירת טיפוסי נתונים מאפשרת דרך נוחה לחלוקת תוכנה גדולה לחלקים נפרדים 48

הפרמטרים ו- argv argc תכנית לדוגמה 49

את הפונקציה main המתחילה את ריצת התכנית ניתן להגדיר גם כך: int main(int argc, char** argv) במקרה זה יילקחו הארגומנטים משורת ההרצה של התכנית ויושמו לתוך המשתנים argc ו- argv ע"י מערכת ההפעלה argc יאותחל למספר הארגומנטים בשורת הפקודה )כולל שם הפקודה( argv הוא מערך של מחרוזות כאשר התא ה- n בו יכיל את הארגומנט ה- n בשורת הפקודה בנוסף, קיים איבר אחרון נוסף במערך המאותחל ל- NULL 50

#include <stdio.h> int main(int argc, char** argv) { for(int i = 1; i < argc; i++) { printf("%s ", argv[i]); return 0; >./echo Hello world Hello world >./echo Hello > world > cat world Hello לאן נעלמה המילה?world argc 3 כיצד ניתן לכתוב את הקוד הזה ללא שימוש במשתנה?argc argv argv[0] argv[1] argv[2] argv[3] "./echo" "Hello" "world" 51

הערות התוך הקוד שימוש במאקרו assert כיבוי המאקרו מתי משתמשים ב- assert 52

int main(int argc, char** argv) { if (argc > 3) {... else if ( argc < 2) {... else { // if we are here argc is 2... מה הבעיה בקוד הזה? 53

המאקרו assert משמש לוידוא טענות: assert(<expression>);.#include המאקרו מוגדר בקובץ המנשק assert.h ועל מנת להשתמש בו יש לעשות בזמן ריצת הקוד הביטוי מוערך ונבדק אם הוא נכון - לא קורה כלום והקוד ממשיך אם הוא אינו נכון - התכנית נעצרת ומודפסת הודעה המפרטת מיקום הטענה הלא נכונה בקוד. נשתמש ב- assert כדי להגן על הקוד מפני הכנסת באגים. // if we are here argc is 2 assert(argc == 2); שינויים עתידיים המפרים הנחות קיימות יגרמו להתראות מוקדמות הנחות לא נכונות לגבי הקוד יימצאו כבר בפעם הראשונה שהן אינן מתקיימות >./prog a >./prog a b prog: main.c:12: main: Assertion `n==3' failed. Abort 54

ניתן לכבות את המאקרו assert ע"י הגדרת הקבוע NDEBUG #define NDEBUG אם NDEBUG מוגדר המאקרו יוחלף בקוד שאינו עושה כלום כך ניתן לשחרר גרסה סופית של הקוד שאינה מואטת ע"י הבדיקות ללא הסרתן ידנית ניתן להגדיר את NDEBUG הדגל ישירות משורת ההידור ע"י הוספת הדגל -DNDEBUG -D<string> מוסיף בתחילת כל קובץ הגדרה של המאקרו בשם <string> שימו לב: קוד שבתוך המאקרו לא יורץ כלל אם המאקרו כבוי אסור לשים חישוביים הכרחיים לקוד בתוך.assert מה הבעיה כאן? מה הפתרון? assert(dosomethingimportant()!= FAILED); 55

ב- assert משתמשים לוידוא נכונות של הנחות הנעשות בקוד אם ההנחות שגויות ייתכן וקיימים באגים נוח לבדוק עם assert את נכונות הארגומנטים, ערכי החזרה ואינווריאנטות של טיפוסי נתונים לא משתמשים ב- assert כדי לבדוק קלט מהמשתמש לא משתמשים ב- assert כאשר אסור לעצור את התכנית בגלל השגיאה int getinput() { int input; printf("enter a positive number:"); scanf("%d",&input); assert(input > 0); return input; מה הבעיה ב- assert כאן? מה צריך לעשות במקום? 56

ניתן להשתמש במאקרו מומלץ להשתמש ב- assert assert כדי לוודא קיום תנאים בתכנית כדי להקל על דיבוג התכנית ניתן לכבות בקלות את התנהגות המאקרו בגרסאות סופיות בעזרת הגדרת NDEBUG אסור לשים חישובים הכרחיים בתוך assert השימוש ב- assert מתאים רק עבור מציאת באגים של המתכנת ואינו מתאים עבור שגיאות אחרות 57