Download - תרגול מס' 2
![Page 1: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/1.jpg)
2תרגול מס' מצביעיםהקצאת זיכרון דינאמית - מבניםStructures - טיפוסי נתוניםData types-העברת פרמטרים לmainטענות נכונות
![Page 2: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/2.jpg)
2מבוא לתכנות מערכות - 234122
מצביעיםשימוש בסיסיאריתמטיקת מצביעיםvoid*מצביע למצביע
![Page 3: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/3.jpg)
3מבוא לתכנות מערכות - 234122
מבנה הזיכרון - תזכורת:הזיכרון מורכב מתאים הקרויים בתים
מספריכל בית מכיל ערך –פירוש הערכים כערך לא מספרי הוא ע"י •
התכניתלכל בית יש שם – כתובת–
הכתובת היא מיקומו בזיכרון•בד"כ כתובות בזיכרון נרשמות בבסיס •
(16הקסדצימלי )
:משתנים מאוחסנים בבתיםטיפוסים שונים דורשים מספר שונה של בתים, –
למשל:•int בתים8 או 4 צורך •char.צורך בית יחיד
0x0200
0x0201
0x0202
0x0203
0x0204
0x0205
0x0206
0x0207
0x0208
0x0209
0x020A
0x020B
0x020C
0x020D
700000001041011081081110
int התופס בתים4
” hello“מחרוזת המורכבת
ממספר תווים
ערך הבית כתובת
![Page 4: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/4.jpg)
4מבוא לתכנות מערכות - 234122
מצביעים - תזכורת עבור טיפוסT-נקרא ל T *-מצביע לT
int* הוא מצביע ל-intלמשל –
מצביע מסוגT של משתנה כתובת * הוא משתנה אשר שומר.Tמטיפוס
באופרטור &ניתן לקבל את כתובתו של משתנה ע"י שימושלא ניתן להשתמש ב-& על ביטויים או קבועים )מדוע?(–
אופרטור *ניתן לקרוא את ערכו של המצביע ע"יdereferencingפעולה זו קרויה –
int n = 5;int* ptr = &n; // ptr now points to nprintf("%d",*ptr); // dereferencing ptr
0x20834370820119
0x0204
0x0205
0x0206
0x0207
0x0208
0x020A
0x020B
0x020C
![Page 5: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/5.jpg)
5מבוא לתכנות מערכות - 234122
0x0 - NULLהכתובת הינה כתובת לא חוקית:0הכתובת
אף עצם אינו יכול להיות בעל כתובת זו–ניתן לעשות שימוש בכתובת זו כדי לציין שמצביע מסוים אינו מצביע לאף עצם כרגע-השתמשו בNULL 0 כאשר אתם מתייחסים לכתובת ולא בקבוע
שימוש בקבועים כאלו משפר את קריאות הקוד– נסיון לקרוא מצביע המכיל את הכתובתNULL לקריסת התוכנה יגרום.
תתקבל ההודעה:UNIXב-– “segmentation fault”
גישה למצביע המכיל "זבל" תגרום לתכניתלהתנהג בצורה לא צפויה
בקוד!מצביעים לא מאותחלים אסור להשאיר –(C99ניתן להכריז על המשתנה מאוחר יותר )•NULLבמקרה ולא ניתן יש לאתחל אותו ל-•
![Page 6: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/6.jpg)
6מבוא לתכנות מערכות - 234122
אריתמטיקת מצביעים על מצביעיםפעולות חשבוניות ניתן בנוסף לבצע
:חיבור מספרים שלמים–int n = ...;int* ptr2 = ptr + n;
תאים קדימה/אחורהnהתוצאה היא כתובתו של המשתנה מטיפוס מתאים •:חיסור שני מצביעים–
int diff = ptr2 - ptr;( התוצאה היא מספר שלםint)
פעולות אלו מאפשרות להסתכל על המשתנה הבא/הקודם בזיכרוןהכרחי לשימוש במערכים ומחרוזות– – טעויות חשבוניות עלולות לגרום לקריאת "זבל" מהזיכרוןמסוכן–
:מדוע לא ניתן לחבר שני מצביעים?שאלה
![Page 7: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/7.jpg)
7מבוא לתכנות מערכות - 234122
מצביעים – גישה למערך ,ניתן להשתמש במצביע כדי לגשת למשתנים הנמצאים בהמשך בזיכרון
למשל כך:int* ptr;... =
int n = *(ptr + 5);
:האופרטור ] [ משמש כקיצור לפעולה זוint n = ptr[5];
:כלומר הפעולות הבאות שקולות*(ptr + n) ≡ ptr[n]
![Page 8: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/8.jpg)
8מבוא לתכנות מערכות - 234122
מצביעים ומערכיםמערכים ומצביעים מתנהגים בצורה דומה
ניתן להשתמש בשם המערך כמצביע לאיבר הראשון בו–כאשר שולחים מערך לפונקציה ניתן לשלוח אותו כמצביע:–
void sort(int* array, int size);עבור מערךאיטרטורמצביע יכול לשמש כ
int array[N];
...//
for(int* ptr = array; ptr < array+N; ptr++){
printf("%d ",*ptr);
}
:הבדליםהכרזה על מצביע אינה מקצה זיכרון הכרזה על מערך מקצה זיכרון כגודל המערך, –
!לאחסון המשתניםניתן לשנות את ערכו של מצביע, אך לא ניתן לשנות את "ערכו" של תחילת המערך–
ניתן C99ב-להכריז על
משתנה בתוך forלולאת
![Page 9: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/9.jpg)
9מבוא לתכנות מערכות - 234122
void* ניתן להגדיר מצביעים מטיפוס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 1double* dptr = ptr; // Implicit cast from void* to double*double d4 = *dptr; // O.K. – option 2
![Page 10: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/10.jpg)
10מבוא לתכנות מערכות - 234122
מצביע למצביע ניתן ליצור מצביע לכל טיפוס, בפרט עבור טיפוס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;
}מדוע יש כאן צורך במצביע למצביע?•
![Page 11: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/11.jpg)
11מבוא לתכנות מערכות - 234122
מצביעים - סיכוםמצביעים משמשים להתייחסות לתאי זיכרון ניתן לקבל את כתובתו של משתנה ע"י אופרטור& ניתן לקרוא ממצביע ולקבל את הערך המוצבע ע"י* הערךNULL מציין שאין עצם מוצבע ואסור לקרוא אותו על מצביעיםפעולות חשבוניות ניתן לבצע
מאפשר התייחסות למצביעים בדומה למערכים– מצביעיםלאתחל חשוב
הרצת קוד הניגש למצביעים המכילים ערך לא תקין תגרום להתנהגות לא –מוגדרת
הכרזה על מצביע אינה מאתחלת זיכרון עבור המשתנה המוצבע!– מצביע מטיפוסvoid * יכול להצביע לעצם מכל סוג ומשמש לכתיבת קוד
גנרי
![Page 12: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/12.jpg)
12מבוא לתכנות מערכות - 234122
הקצאת זיכרון דינאמיתסוגי משתניםהקצאת זיכרוןשחרור זיכרוןנזילות זיכרון
![Page 13: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/13.jpg)
13מבוא לתכנות מערכות - 234122
סוגי משתנים:את המשתנים השונים בקוד ניתן לסווג לפי טווח ההכרה ואורך חייהם
: משתנים פנימיים של פונקציות. נגישים רק בבלוק בו הם משתנים מקומיים–הוגדרו. משתנים אלו מוקצים בכל פעם שהבלוק מורץ ומשוחררים בסופו.
: משתנים אשר מוגדרים לכל אורך התכנית וניתן לגשת משתנים גלובליים–אליהם מכל מקום. המשתנים מוקצים כאשר התכנית מתחילה ונשמרים לכל
אורך זמן הריצה: משתנים פנימיים של פונקציה. משתנים אלו משתנים סטטיים של פונקציה–
שומרים על ערכם בין הקריאות השונות לפונקציה. מאותחלים בריצה הראשונה של הפונקציה, משוחררים בסוף ריצת התכנית
: מוקצים ומשוחררים ע"י קריאה מפורשת לפונקציהמשתנים דינאמיים–
![Page 14: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/14.jpg)
14מבוא לתכנות מערכות - 234122
הרוע של משתנים גלובליים משתנים גלובליים, משתנים סטטיים של קובץ ומשתנים סטטיים של
תכנות רעפונקציה נחשבים ל הסיבה העיקרית לכך - שימוש במשתנים אלו מקשה על הבנת ודיבוג
הקוד:כדי להבין פונקציה המשתמשת במשתנה גלובלי יש להסתכל בקוד נוסף–קשה לצפות את תוצאת הפונקציה כי היא אינה תלויה רק בפרמטרים שלה–קשה לצפות השלכות של שינויים על ערך המשתנה–בשימוש במשתנה סטטי של פונקציה - בשביל לצפות את תוצאת הפונקציה –
צריך לדעת מה קרה בהרצות קודמותאין להשתמש במשתנים גלובליים במת"מ
בקורסים מתקדמים בהמשך התואר תראו מקרים בהם חובה או מומלץ –להשתמש במשתנים כאלו
![Page 15: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/15.jpg)
15מבוא לתכנות מערכות - 234122
משתנים דינאמיים הם משתנים שזמן החיים שלהם הוא בשליטת משתנים דינאמיים
המתכנתקוד מפורש מקצה אותם וקוד מפורש דרוש לשחרורם–heapהמשתנים מוקצים באזור זיכרון שקרוי ה-–
stackבניגוד למשתנים מקומיים המוקצים על מחסנית הקריאות, ה-•
:משתמשים בהם כאשרצריך ליצור מערך שגודלו אינו ידוע מראש–יש צורך לשמור נתונים בזיכרון גם לאחר יציאה מהפונקציה–
הגישה למשתנים אלו נעשית תמיד בעזרת מצביעים
![Page 16: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/16.jpg)
16מבוא לתכנות מערכות - 234122
mallocהקצאת זיכרון באמצעות כדי להקצות זיכרון נשתמש בפונקציה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;}
![Page 17: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/17.jpg)
17מבוא לתכנות מערכות - 234122
קביעת הגודל אותו מקצים כיצר נדע כמה בתים עלינו להקצות עבור משתנה מסוגint? :נסיון ראשון
int* ptr = malloc(4);4 1 או 0- מספר לא ברור המופיע בקוד שאינו מספר קסם הוא הרגל תכנותי רעמספרי קסם הם:
פוגעים בקריאות הקוד•מקשים על שינויים עתידיים בקוד•
8 הוא intלמשל מעבר לסביבה בה גודלו של –ככל שצריך יותר שינויים הסיכוי לפספס אחד מהם גדל–
:יש להימנע ממספרי קסם שיקלו על שינויים ועל קריאת הקודdefine בעזרת #קבועיםע"י הגדרת –ע"י שמירת ערכם במשתנה קבוע בעל שם ברור–
![Page 18: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/18.jpg)
18מבוא לתכנות מערכות - 234122
קביעת הגודל - נסיון שני נסיון שני - נגדיר את הגודל שלint:כקבוע
#define SIZE_OF_INT 4
int* ptr = malloc(SIZE_OF_INT);
עכשיו הקוד קריא וקל לשנות את הערךאבל אם נעביר את הקוד לסביבה אחרת עדיין נצטרך לעדכן את הערך
non-portableקוד שדורש שינויים במעבר בין סביבות שונות נקרא –
![Page 19: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/19.jpg)
19מבוא לתכנות מערכות - 234122
sizeofאופרטור נשתמש באופרטור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));
למה השיטה הזו עדיפה?
, sizeof(char)אפשר להוריד את 1מובטח שהוא תמיד
למה צריך 1?+
![Page 20: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/20.jpg)
20מבוא לתכנות מערכות - 234122
בדיקת ערכי חזרהmalloc עלולה להיכשל בהקצאת הזיכרון - במקרה זה מוחזר NULL מה קורה במקרה זה אם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();}
בהמשך נראה מקרים נוספים של שגיאות יותר שכיחות ופשוטות להתמודדות
![Page 21: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/21.jpg)
21מבוא לתכנות מערכות - 234122
freeשחרור זיכרון באמצעות הפונקציהfree משמשת לשחרור גוש זיכרון שהוקצה ע"י malloc
void free(void* ptr); חייב להצביע לתחילת גוש הזיכרון )אותו ערך שהתקבל freeהמצביע שנשלח ל-–
(mallocמ-לאחר שחרור הזיכרון אסור לגשת יותר לערכים בזיכרון ששוחרר– לא מתבצע כלוםfree ל-NULLאם שולחים –
NULLכלומר אין צורך לבדוק את הפרמטר הנשלח ולוודא שאינו •למה זה טוב?•
מצביע שאינו מצביע freeאסור לשחרר את אותו זיכרון פעמיים או לשלוח ל-–(NULLלתחילת גוש זיכרון שהוקצה דינאמית )או
int* my_array = malloc(sizeof(int) * n);// ... using my_array ...free(my_array);
![Page 22: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/22.jpg)
22מבוא לתכנות מערכות - 234122
מקרי קצה במקרה ונשלחNULL-ל freeלא מתבצע כלום
if (ptr != NULL) {free(ptr);
}:ניתן להחליף את הקוד הקודם בזה
free(ptr);NULL עבור מקרה קצה הואfree
לא היתה מתמודדת עם מקרה הקצה הזה?freeמה היה קורה אם –
לטפל במקרי קצה בתוך הפונקציהעדיףבאגים ושכפולי קודמונע מהמשתמש בה ליצור –
![Page 23: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/23.jpg)
23מבוא לתכנות מערכות - 234122
גישה לזיכרון אחרי ששוחרר )אינה מוגדרתגישה לכתובת זיכרון שאינה מוקצה )או הוקצתה ושוחררה אינו מוגדרשחרור כפול של כתובת זיכרון קוד שתוצאתו אינה מוגדרת הוא קוד שמתקמפל ורץ אך אינו מחשב את
הערכים הצפויים.נותן תוצאות שאינן צפויות–
בחלק מהמקרים התוצאה שתוחזר אכן מתאימה לציפיות•
קוד שאינו מוגדר הוא באג קשה לטיפולקשה לצפות את התנהגותו והשלכותיו –יכול להשפיע על משתנים באזור אחד בקוד–
חשוב להקפיד על שימוש נכון בשפה כדי להימנע ממקרים אלו
![Page 24: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/24.jpg)
24מבוא לתכנות מערכות - 234122
התנהגות לא מוגדרת - דוגמה?האם שתי התכניות הבאות מתנהגות בצורה זהה
#include <stdio.h>#define N 7 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;
}
#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;
}
![Page 25: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/25.jpg)
25מבוא לתכנות מערכות - 234122
דליפות זיכרון מתרחשת כאשר שוכחים לשחרר זיכרון שהוקצה:דליפת זיכרון
void sort(int* array, int n) {int* copy = malloc(sizeof(int) * n);// ... some code without free(copy)return;
}דליפת זיכרון אינה גורמת ישירות לשגיאות בהתנהגות התוכנה
ולהאטת דליפת זיכרון יגרמו לצריכת זיכרון גדלה של התוכנה ככל שזמן ריצתה גדל ומערכת ההפעלה כולההתוכנה
תחתUNIX ניתן להשתמש בכלי valgrind לאיתור דליפות זיכרון–valgrindמריץ את התכנית שלכם ומחפש גושי זיכרון שהוקצו אך לא שוחררו 3 בתרגול עזר valgrindניתן למצוא מידע נוסף על השימוש ב-–
![Page 26: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/26.jpg)
26מבוא לתכנות מערכות - 234122
איך מתמודדים עם כל הקשיים? כדי להימנע מכל הבעיות שתוארו כאשר עובדים עם הקצאות דינאמיות
עבודה מסודרתקיים רק פתרון אחד יעיל -
בעזרת עבודה מסודרת ניתן לשמור על הקוד פשוט יותר
קוד מסובך מקל על הכנסת באגים בטעות
הטיפול בבאגים קשה יותר אם הקוד מסובך
![Page 27: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/27.jpg)
27מבוא לתכנות מערכות - 234122
הקצאת זיכרון דינאמית - סיכוםמומלץ לא להשתמש במשתנים גלובליים וסטטיים-ניתן להשתמש בmalloc-ו free כדי להקצות ולשחרר זיכרון בצורה
מפורשתעבור יצירת מערכים בגודל לא ידוע–עבור שמירת ערכים לאורך התכנית–
ניהול הזיכרון מתבצע ע"י מצביעים לתחילת גוש הזיכרון שהוקצהיש לבדוק הצלחת הקצאת זיכרוןיש לזכור לשחרר את הזיכרון המוקצה כאשר אין בו צורך יותר-ניתן להשתמש בvalgrindכדי למצוא בקלות גישות לא מוגדרות לזיכרון
![Page 28: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/28.jpg)
28מבוא לתכנות מערכות - 234122
מבניםהגדרת מבנהפעולות על מבניםtypedef
![Page 29: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/29.jpg)
29מבוא לתכנות מערכות - 234122
הטיפוסים הקיימים אינם מספיקים נניח שברצוננו לכתוב תוכנה לניהול אנשי קשר, לכל איש קשר נשמור: שם
וכתובת מגורים.e-mailפרטי, שם משפחה, מספר טלפון, כתובת מערכים שונים5 לשם כך נצטרך לשמור ! 5כל פונקציה שתצטרך לקבל את פרטיו של איש קשר כלשהו תצטרך לקבל
!פרמטרים שונים לפחות
void someFunction(char* firstname, char* lastname, char* address, char* email, int number, ... more?);
המהווים הרכבה של טיפוסים חדשים כדי להימנע מריבוי משתנים ניתן להגדירמספר טיפוסים קיימים
void someFunction(Contact contact, ...);
![Page 30: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/30.jpg)
30מבוא לתכנות מערכות - 234122
Structuresמבנים - ניתן להגדיר טיפוסים חדשים המהווים הרכבה של מספר טיפוסים קיימים בעזרת
:structהמילה השמורה struct <name> {
<typename 1> <field name 1>;<typename 2> <field name 2>;...<typename n> <field name n>;
} <declarations>;שדותהטיפוס החדש מורכב מ:
לכל שדה יש שם–טיפוס השדה נקבע לפי הגדרת המבנה–
המבנים נשמרים בזיכרון ברצףניתן להשתמש במערכים בעלי גודל קבוע כשדות - כל המערך נשמר במבנהניתן להשתמש במצביעים כשדות - במקרה זה הערך המוצבע אינו חלק מהמבנה
![Page 31: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/31.jpg)
31מבוא לתכנות מערכות - 234122
מבנים - דוגמאותstruct point {
double x;double y;
}; struct date {
int day;char month[4];int year;
}; struct person {
char* name;struct Date birth;
};
x=3.0
y=2.5
point
day=31month="NOV
"
date
year=1971
name=0x0ffef6
birth
person
"Ehud Banai"day=31
month="MAR"
year=1953
?4למה
כל המערך נשמר בתוך
המבנה
המחרוזת נשמרת מחוץ
למבנה
![Page 32: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/32.jpg)
32מבוא לתכנות מערכות - 234122
שימוש במבנים הטיפוס החדש מוגדר בשםstruct <name> נקודה(.כדי לגשת לשדות של משתנה מטיפוס המבנה נשתמש באופרטור(
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, annoyingp->y = 2.5; // Same thing, only clearerdouble distance = sqrt(p->x * p->x + p->y * p->y);
מה חסר?
![Page 33: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/33.jpg)
33מבוא לתכנות מערכות - 234122
פעולות על מבנים:ניתן לאתחל מבנים בעזרת התחביר הבא
struct date d = { 31, "NOV", 1970 };
:ניתן לבצע השמה בין מבנים מאותו הטיפוסstruct date d1,d2;// ...d1 = d2;
במקרה זה מתבצעת השמה בין כל שני שדות תואמים– מבנים מועברים ומוחזרים מפונקציותby valueכלומר מועתקים –
גם במקרה זה מתבצעת ההעתקה שדה-שדה– הפעולות האלו אינן מתאימות למבנים מסובכים יותר )בד"כ בגלל
מצביעים(
![Page 34: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/34.jpg)
34מבוא לתכנות מערכות - 234122
מבנים עם מצביעיםמבנים המכילים מצביעים אינם מתאימים בדרך כלל לביצוע השמות והעתקות?מה יקרה אם נבצע השמה בין שני המבנים בדוגמה זו
נשתמש מסיבה זו וכדי למנוע העתקות כבדות ומיותרות של מבנים בדרך כללבמבנים ע"י מצביעים
נשלח לפונקציות )ונקבל כערכי חזרה( מצביעים למבנה–pointיוצא הדופן הוא מבנים קטנים ופשוטים כגון –
name=0x0ffef6
birth
person1"Ehud Banai"
day=31
month="MAR"
year=1953
name=0x0ffed0
birth
person2"Yuval Banai"
day=9
month="JUN"
year=1962
![Page 35: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/35.jpg)
35מבוא לתכנות מערכות - 234122
typedefהגדרת טיפוסים בעזרת המילה השמורהtypedef משמשת להגדרת טיפוסים חדשים ע"י נתינת שם חדש
לטיפוס קייםtypedef int length;
פקודתtypedef עובדת על שורת הכרזה של משתנה – אך מגדירה טיפוס חדש במקום משתנה.
נשתמש בפקודתtypedef:כדי לתת שמות נוחים לטיפוסים typedef struct point Point;
(struct )ללא המילה השמורה Pointבמקרה זה נוכל להתייחס למבנה מעכשיו כ-–נוח לתת שם גם לטיפוס המצביע למבנה:–
typedef struct date Date, *pDate;עבור מבנים מסובכים נשתמש תמיד במצביעים ולכן במקרים האלו נשמור את השם –
ה"נוח" לטיפוס המצביע:typedef struct person *Person;
![Page 36: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/36.jpg)
36מבוא לתכנות מערכות - 234122
typedefהגדרת טיפוסים בעזרת ניתן להוסיףtypedef:ישירות על הגדרת המבנה
typedef struct point {double x;double y;
} Point;
:ניתן להשמיט את שם הטיפוס בהגדרה ולהשאיר רק את השם החדשtypedef enum { RED, GREEN, BLUE } Color;typedef struct {
double x;double y;
} Point;
![Page 37: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/37.jpg)
37מבוא לתכנות מערכות - 234122
מבנים - סיכום מבנים מאפשרים הרכבה של מספר טיפוסים קיימים כדי להקל על
קריאות הקודמבנה מורכב משדות בעלי שם
-<.ניתן לגשת לשדות ע"י האופרטורים . ו- –העתקה והשמה של מבנים בטוחה כל עוד אין בהם מצביעים-מומלץ להשתמש בtypedefכדי לתת שם נוח לטיפוס החדש
![Page 38: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/38.jpg)
38מבוא לתכנות מערכות - 234122
טיפוסי נתונים
![Page 39: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/39.jpg)
39מבוא לתכנות מערכות - 234122
Data typesטיפוסי נתונים – 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-vuif (d1.day == d2.day && strcmp(d1.month,d2.month) == 0 &&d1.year == d2.year) {printf("The dates are equal\n");}return 0;
}
אלו בעיות יש בקוד הזה?
![Page 40: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/40.jpg)
40מבוא לתכנות מערכות - 234122
Data typesטיפוסי נתונים - מהרכבה של שני מספרים שלמים וארבעה תווים יותרתאריך הוא
לא כל צירוף של ערכים עבור המבנהDate תאריך חוקי הוא אכן–5 BLA 2010“-אין חודש מתאים ל - BLA”–31 SEP 1978 ימים30 - ב-ספטמבר יש רק –29 FEB 2010 ימים28 יש רק 2010 - בפברואר
בצורות מסוימותמי שמשתמש במבנה התאריך צפוי להשתמש בוהדפסת תאריך–מציאת התאריך המוקדם יותר מבין שני תאריכים–מציאת מספר הימים בין שני תאריכים–
![Page 41: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/41.jpg)
41מבוא לתכנות מערכות - 234122
Data typesטיפוסי נתונים - כדי לוודא את נכונות השימוש בתאריכים ולמנוע את שכפולי הקוד
בשימוש בתאריכים עלינו לכתוב פונקציות מתאימות לטיפול בתאריכים
טיפוס נתונים -לצירוף של טיפוס והפעולות האפשריות עליו קוראיםData type
טיפוסי הנתונים המובנים בשפה נקראים טיפוסי נתונים פרימטיביים– ומצביעים )לכל אחד מהם פעולות שונות אפשריות(int, floatלמשל •
יצירת טיפוסי נתונים מהווה את הבסיס לכתיבת תוכנה גדולה בצורה מסודרת –ופשוטה
![Page 42: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/42.jpg)
42מבוא לתכנות מערכות - 234122
טיפוס נתונים לתאריך#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;
const char* const months[] = { "JAN", "FEB", "MAR", " APR", "MAY", "JUN","JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
הגדרת קבועים
רק לקבצים includeמבצעים שהכרחיים לקמפול הקוד:
stdio.h עבור - printf-ו scanfstring.h עבור - strcmp
stdbool.h עבור הגדרת הטיפוס - bool
![Page 43: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/43.jpg)
43מבוא לתכנות מערכות - 234122
טיפוס נתונים לתאריך/** 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);
מומלץ לתעד לפחות בקצרה את משמעות
הפונקציות מעל הכרזתן
תיעוד צריך להופיע מעל הפונקציה ולא בתוכה
הערות באמצע הקוד בד"כ מיותרות או מסבירות קוד שהיה צריך להיכתב ברור
יותר
![Page 44: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/44.jpg)
44מבוא לתכנות מערכות - 234122
טיפוס נתונים לתאריך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;
}
![Page 45: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/45.jpg)
45מבוא לתכנות מערכות - 234122
טיפוס נתונים לתאריך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);
}
יש לבדוק את תקינות הקלט בכניסה לפונקציה
במיוחד מצביעים!
, אם קוד כלשהו משכפול קודהמנעו לקרוא לפונקציה כבר נכתב הקפידו
לכתוב אותו ולא המבצעת אותומחדש!
וקוד חוזר על אם אין פונקציה מתאימה ולקרוא לכתוב פונקצית עזר - יש עצמו
לה!
![Page 46: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/46.jpg)
46מבוא לתכנות מערכות - 234122
טיפוס נתונים לתאריך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;
}
![Page 47: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/47.jpg)
47מבוא לתכנות מערכות - 234122
המעודכנתmainפונקצית ה-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;
}
![Page 48: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/48.jpg)
48מבוא לתכנות מערכות - 234122
טיפוסי נתונים - סיכוםכאשר מגדירים טיפוס חדש יש להגדיר גם פונקציות מתאימות עבורויש להגדיר פונקציות עבור הפעולות הבסיסיות שיצטרך המשתמש בטיפוס יש להגדיר פונקציות כך שתשמורנה על ערכים חוקיים של הטיפוס
ותמנענה באגים יצירת טיפוסי נתונים מאפשרת דרך נוחה לחלוקת תוכנה גדולה לחלקים
נפרדים
![Page 49: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/49.jpg)
49מבוא לתכנות מערכות - 234122
mainהעברת פרמטרים ל- הפרמטריםargc-ו argvתכנית לדוגמה
![Page 50: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/50.jpg)
50מבוא לתכנות מערכות - 234122
mainהעברת פרמטרים ל- את הפונקציהmain:המתחילה את ריצת התכנית ניתן להגדיר גם כך
int main(int argc, char** argv)
במקרה זה יילקחו הארגומנטים משורת ההרצה של התכנית ויושמו לתוךע"י מערכת ההפעלה argv ו-argcהמשתנים
–argc )יאותחל למספר הארגומנטים בשורת הפקודה )כולל שם הפקודה–argv -הוא מערך של מחרוזות כאשר התא ה- בו יכיל את הארגומנט ה
בשורת הפקודהNULLבנוסף, קיים איבר אחרון נוסף במערך המאותחל ל-–
![Page 51: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/51.jpg)
51מבוא לתכנות מערכות - 234122
echoדוגמה - תכנית #include <stdio.h> int main(int argc, char** argv) {
for(int i = 1; i < argc; i++) {printf("%s ", argv[i]);}return 0;
}
> ./echo Hello worldHello world> ./echo Hello > world> cat worldHello
כיצד ניתן לכתוב את הקוד הזה ללא שימוש במשתנה
argc?
לאן נעלמה ?worldהמילה
argv[0] /."echo"
"Hello"
"world"
argv[1]argv[2]argv[3]
argv3
argc
![Page 52: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/52.jpg)
52מבוא לתכנות מערכות - 234122
טענות נכונותהערות התוך הקוד שימוש במאקרוassertכיבוי המאקרו-מתי משתמשים בassert
![Page 53: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/53.jpg)
53מבוא לתכנות מערכות - 234122
הערות בתוך הקוד?מה הבעיה בקוד הזה
int main(int argc, char** argv) {if (argc > 3) {
...} else if ( argc < 2) {
...} else {
// if we are here argc is 2...
}}
![Page 54: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/54.jpg)
54מבוא לתכנות מערכות - 234122
assertהמאקרו המאקרוassert:משמש לוידוא טענות
assert(<expression>); המאקרו מוגדר בקובץ המנשקassert.h ועל מנת להשתמש בו יש לעשות #include.בזמן ריצת הקוד הביטוי מוערך ונבדק
- לא קורה כלום והקוד ממשיךאם הוא נכון –- התכנית נעצרת ומודפסת הודעה המפרטת מיקום הטענה הלא נכונה בקוד.אם הוא אינו נכון –
-נשתמש בassert.כדי להגן על הקוד מפני הכנסת באגים // if we are here argc is 2 assert(argc == 2);
התראות מוקדמותשינויים עתידיים המפרים הנחות קיימות יגרמו ל–הנחות לא נכונות לגבי הקוד יימצאו כבר בפעם הראשונה שהן אינן מתקיימות–
> ./prog a> ./prog a bprog: main.c:12: main: Assertion `n==3' failed.Abort
![Page 55: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/55.jpg)
55מבוא לתכנות מערכות - 234122
כיבוי המאקרו ניתן לכבות את המאקרוassert ע"י הגדרת הקבוע NDEBUG
#define NDEBUG מוגדר המאקרו יוחלף בקוד שאינו עושה כלוםNDEBUGאם –כך ניתן לשחרר גרסה סופית של הקוד שאינה מואטת ע"י הבדיקות ללא הסרתן –
ידניתDNDEBUG- ישירות משורת ההידור ע"י הוספת הדגל NDEBUGניתן להגדיר את –
< מוסיף בתחילת כל קובץ הגדרה של המאקרו בשם D<stringהדגל -•<string>
:קוד שבתוך המאקרו לא יורץ כלל אם המאקרו כבוישימו לב.assertאסור לשים חישוביים הכרחיים לקוד בתוך –
?מה הבעיה כאן? מה הפתרוןassert(doSomethingImportant() != FAILED);
![Page 56: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/56.jpg)
56מבוא לתכנות מערכות - 234122
assertמתי משתמשים ב--בassert הנעשות בקודנכונות של הנחות משתמשים לוידוא
אם ההנחות שגויות ייתכן וקיימים באגים– של טיפוסי אינווריאנטותוערכי החזרה , ארגומנטים את נכונות הassertנוח לבדוק עם –
נתונים
ב-לא משתמשיםassertכדי לבדוק קלט מהמשתמש ב-לא משתמשיםassertכאשר אסור לעצור את התכנית בגלל השגיאה
int getInput() {int input;printf("Enter a positive number:");scanf("%d",&input);assert(input > 0);return input;
}
כאן?assertמה הבעיה ב-מה צריך לעשות במקום?
![Page 57: תרגול מס' 2](https://reader031.vdocuments.net/reader031/viewer/2022020713/56815ab7550346895dc86f55/html5/thumbnails/57.jpg)
57מבוא לתכנות מערכות - 234122
טענות נכונות - סיכום ניתן להשתמש במאקרוassertכדי לוודא קיום תנאים בתכנית -מומלץ להשתמש בassertכדי להקל על דיבוג התכנית ניתן לכבות בקלות את התנהגות המאקרו בגרסאות סופיות בעזרת
NDEBUGהגדרת אסור לשים חישובים הכרחיים בתוךassert-השימוש בassert מתאים רק עבור מציאת באגים של המתכנת ואינו
מתאים עבור שגיאות אחרות