הרצאה 04 הקצאות דינאמיות קרן כליף.

Slides:



Advertisements
Similar presentations
קורס תכנות שיעור שנים-עשר: ניהול זיכרון 1. הקצאת זיכרון דינאמית עד עכשיו עשינו "הקצאה סטטית" הגדרנו את משתני התוכנית כבר כשכתבנו אותה הקומפיילר הקצה עבורם.
Advertisements

הקצאות דינאמיות קרן כליף.
Pointers הרצאה קריטית. השאלות הפתוחות מה זה ה- & שמופיע ב scanf מדוע כשמעבירים מחרוזת ל scanf אין צורך ב & האם ניתן להכריז על מערך שגדלו אינו ידוע בתחילת.
1 מבוא למדעי המחשב הקצאה דינאמית. 2 הקצאת זיכרון דינאמית  כאשר אנו משתמשים במערכים, אנו מקצים אוטומטית את הזיכרון המקסימלי שנצטרך.  בפועל, אנו משתמשים.
רקורסיות נושאי השיעור פתרון משוואות רקורסיביות שיטת ההצבה
תכנות תרגול 11 שבוע : מבנים מטרת המבנים היא לאפשר למתכנת להגדיר טיפוסי משתנים חדשים אשר מתאימים ספציפית לבעיה שאותה התוכנית פותרת. מטרת המבנים.
תכנות תרגול 7 שבוע : מערכים עד היום התוכניות שלנו לא ידעו לשמור כמות גדולה של מידע ללא הגדרת כמות גדולה של משתנים. עד היום התוכניות שלנו לא.
תכנות תרגול 6 שבוע : חישוב e זוהי הנוסחא לחישוב e נראה כיצד לתרגם אותה לפונקציה n n.
תרגול 5 רקורסיות. רקורסיה קריאה של פונקציה לעצמה –באופן ישיר או באופן עקיף היתרון : תכנות של דברים מסובכים נעשה ברור ונוח יותר, מכיוון שזו למעשה צורת.
תכנות תרגול 4 שבוע : לולאות while לולאות while while (condition) { loop body } במקרה של קיום התנאי מתבצע גוף הלולאה ברגע שהתנאי לא מתקיים נצא.
מבוא לשפת C חידות ונקודות חשובות נכתב על-ידי יורי פקלני. © כל הזכויות שמורות לטכניון – מכון טכנולוגי לישראל.
מבוא למדעי המחשב תרגול 8 - מחרוזות שעת קבלה : יום שני 11:00-12:00 דוא " ל :
מבוא כללי למדעי המחשב תרגול. הבית האדום כתובת : רחוב קוקוריקו 2 הבית הירוק כתובת : רחוב קוקוריקו 4 הבית הצהוב כתובת : רחוב קוקוריקו 1 הבית הורוד כתובת.
תכנות תרגול 9 שבוע : מערכים int a; a=5; int a[10]; a[2] = 5; 5 a a[0] a[1] a[2] a[9]  5 משתנה בודד מערך גישה למשתנה השלישי במערך.
11 Introduction to Programming in C - Fall 2010 – Erez Sharvit, Amir Menczel 1 Introduction to Programming in C תרגול
מבוא למדעי המחשב © אריק פרידמן 1 מצביעים כמערכים דוגמה.
תכנות תרגול 6 שבוע : תרגיל שורש של מספר מחושב לפי הסדרה הבאה : root 0 = 1 root n = root n-1 + a / root n-1 2 כאשר האיבר ה n של הסדרה הוא קירוב.
11 Introduction to Programming in C - Fall 2010 – Erez Sharvit, Amir Menczel 1 Introduction to Programming in C תרגול
תכנות תרגול 6 שבוע : הגדרת פונקציות return-value-type function-name(parameter1, parameter2, …) הגדרת סוג הערכים שהפונקציה מחזירה שם הפונקציהרשימת.
תכנות תרגול 14 שבוע:
תכנות תרגול 10 שבוע : הקשר בין מערכים למצביעים נרצה לעמוד על הקשר בין מערך למצביע מאחר ומערכים הם הכללה של משתנים הרי שברור שלמערך ולכל אחד מאיבריו.
מבוא כללי למדעי המחשב תרגול 3. לולאות while לולאות while while (condition) { loop body } במקרה של קיום התנאי מתבצע גוף הלולאה ברגע שהתנאי לא מתקיים נצא.
קורס תכנות – סימסטר ב ' תשס " ח שיעור שישי: מערכים
תכנות תרגול 12 שבוע : הקצאת זיכרון דינאמית הזיכרון המקסימאלי ששימש את התוכנית שלנו עד היום היה קבוע מראש. לפני הרצת התוכנית, לאחר שהתוכנית עברה.
מבוא כללי למדעי המחשב רשימות מקושרות
מבוא למדעי המחשב תרגול 6 - מערכים שעת קבלה : יום שני 11:00-12:00 דוא " ל :
תכנות תרגול 7 שבוע : מערכים עד היום התוכניות שלנו לא ידעו לשמור כמות גדולה של מידע ללא הגדרת כמות גדולה של משתנים. עד היום התוכניות שלנו לא ידעו.
תכנות תרגול 5 שבוע : הגדרת פונקציות return-value-type function-name(parameter1, parameter2, …) הגדרת סוג הערכים שהפונקציה מחזירה שם הפונקציהרשימת.
מערכים עד היום כדי לייצג 20 סטודנטים נאלצנו להגדיר עד היום כדי לייצג 20 סטודנטים נאלצנו להגדיר int grade1, grade2, …, grade20; int grade1, grade2, …, grade20;
תכנות מונחה עצמים Object Oriented Programming (OOP) אתגר מחזור ב' Templates תבניות.
תוכנה 1 - תרגול שיעור 10 Pointers (2) שולי לב יהודי
מבוא כללי למדעי המחשב הקצאת זיכרון דינאמית
1 מבוא למדעי המחשב סיבוכיות. 2 סיבוכיות - מוטיבציה סידרת פיבונאצ'י: long fibonacci (int n) { if (n == 1 || n == 2) return 1; else return (fibonacci(n-1)
מבוא למדעי המחשב תרגול 12 – הקצאת זיכרון דינאמית שעת קבלה : יום שני 11:00-12:00 דוא " ל :
תכנות תרגול 8 שבוע : מערכים עד היום התוכניות שלנו לא ידעו לשמור כמות גדולה של מידע ללא הגדרת כמות גדולה של משתנים. עד היום התוכניות שלנו לא.
Structure. מה לומדים היום ? דרך לבנות מבנה נתונים בסיסי – Structure מייצר " טיפוס " חדש מתאים כאשר רוצים לאגד כמה משתנים יחד דוגמאות : עובד : שם, טלפון,
הגדרת משתנים יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר.
קורס תכנות שיעור שמיני: הקצאת זיכרון דינאמית, הצצה לייצוג ועיבוד תמונות 1.
Void*, pointer to functions, variadic functions קרן כליף.
קורס תכנות שיעור עשירי: מיונים, חיפושים, וקצת סיבוכיות חישוב.
תכנות מכוון עצמים ושפת ++C וויסאם חלילי. TODAY TOPICS: 1. Function Overloading & Default Parameters 2. Arguments By Reference 3. Multiple #include’s 4.
מבנים קרן כליף. ביחידה זו נלמד :  מהו מבנה (struct)  איתחול מבנה  השמת מבנים  השוואת מבנים  העברת מבנה לפונקציה  מבנה בתוך מבנה  מערך של מבנים.
Programming Pointers. נדגים היום בעזרת מצביעים העברת משתנים לפונקציה שמשנה אותם  פונקציה שמקבלת מצביעים לסמן תא בזיכרון  פונקציה שמחזירה מצביע מערך.
Programming Arrays.
מבני נתונים רשימה מקושרת, מחסנית ותור
הרצאה 10 פונקציות עם מספר משתנה של פרמטרים
מבוא למדעי המחשב סיבוכיות.
הקצאות דינאמיות בשילוב מבנים
מ- JAVA ל- C קרן כליף.
מערכים ומצביעים הקצאה דינאמית של מערכים דו-מימדיים
מיונים וחיפושים קרן כליף.
מבוא למדעי המחשב – סמסטר א' תשע"ב
הקצאות דינאמיות בשילוב מבנים
הקצאות דינאמיות בשילוב מבנים
מצביעים קרן כליף.
הרצאה 06 רשימות מקושרות קרן כליף.
הקצאות דינאמיות קרן כליף.
מבנים קרן כליף.
הקצאות דינאמיות קרן כליף.
Introduction to Programming in C
מצביעים קרן כליף.
ניתוח זמן ריצה (על קצה המזלג)
הקצאת זיכרון דינאמית מבוא כללי למדעי המחשב
שאלה 1.
מבוא כללי למדעי המחשב תרגול 4
מבוא כללי למדעי המחשב תרגול 6
מחרוזות קרן כליף.
מערכים של מצביעים הקצאה דינאמית
Programming in C תרגול Introduction to C - Fall Amir Menczel.
תירגול 8:מצביעים והקצאה דינאמית
Presentation transcript:

הרצאה 04 הקצאות דינאמיות קרן כליף

ביחידה זו נלמד: פקודות ניהול זיכרון ב- C לעומת C++ malloc calloc free realloc מוטיבציה להקצאות דינאמיות מערך של מצביעים מערך של מצביעים שחוזר כפרמטר מפונקציה מערך של מצביעים למבנים © Keren Kalif

מוטיבציה להקצאה דינאמית בעזרת הקצאה דינאמית נוכל: להקצות מערך בזמן ריצה, בלי לדעת את גודלו בזמן קומפילציה נוכל להחזיר מערך שהוגדר בתוך מפונקציה © Keren Kalif

מהי הקצאה דינאמית? הקצאה דינאמית היא הקצאת שטח זיכרון בגודל מבוקש בזמן ריצת התוכנית בניגוד להקצאה סטטית שמוקצית בתחילת התוכנית וגודלה ידוע כבר בזמן קומפילציה הקצאה דינאמית מוקצית על שטח הזיכרון heap בניגוד להקצאה סטטית שמוקצית על ה- stack של הפונקציה ה- heap הוא שטח זיכרון המשותף לכל הפונקציות, בניגוד ל- stack שייחודי לכל פונקציה בהקצאת זיכרון דינאמית המתכנת מבקש ממערכת ההפעלה זיכרון בגודל מסוים המוגדר בבתים ומקבל את כתובת הבית הראשון בקטע הזיכרון שקיבל © Keren Kalif

הגדרה נוספת ל- <type>* אנו יודעים שמשתנה מטיפוס *<type>מכיל כתובת של משתנה מטיפוס <type> כלשהו כתובת התחלה של מערך מטיפוס <type>היא גם כתובתו של האיבר הראשון במערך, שהוא משתנה מטיפוס <type> לכן משתנה מטיפוס <type>* יכול להכיל גם כתובת התחלה של מערך לכן נאמר ש- <type>* הוא פוטנציאל למערך כלומר, יכול להכיל כתובת התחלה של מערך (ולא רק כתובת של משתנה יחיד מטיפוס <type>) © Keren Kalif

פונקציות לשימוש בהקצאות דינאמיות בשפת C++ עבדנו עם הפונקציה new להקצאת זיכרון כדי לעבוד עם הקצאות דינאמיות בשפת C יש להכליל את הספריה stdlib.h לצורך שימוש בפונקציות הבאות: void* malloc(size_t size) הפונקציה מקבלת את גודל הזיכרון המבוקש בבתים ומחזירה כתובת לתחילת שטח הזיכרון שהוקצה, ו-NULL במקרה שלא היה מספיק זיכרון פנוי להקצאה נשים לב ש- size_t הוא typedef ל- int, כלומר הפונקציה מקבלת את מספר הבתים שעליה להקצות הפונקציה מחזירה void* שזה הכללה למצביע מכל טיפוס שטח הזיכרון המתקבל מכיל זבל © Keren Kalif

פונקציות לשימוש בהקצאות דינאמיות (2) void* calloc(size_t n,size_t size_el) כמו malloc אבל מקבלת את כמות האיברים שרוצה להקצות, ומה הגודל של כל איבר. מקצה את השטח ומאפסת אותו. מקצה מערך של n איברים כל איבר בגודל size_el בתים, כל ביט מאותחל לאפס. קריאה מוצלחת תחזיר את כתובת ההתחלה של הזיכרון המוקצה , ו-NULL במקרה שלא היה מספיק זיכרון פנוי להקצאה © Keren Kalif

malloc – הקצאת מערך בגודל שאינו ידוע מראש #include <stdio.h> #include <stdlib.h> void main() { int size, *arr, i; printf("Please enter the size of the array: "); scanf("%d", &size); arr = (int*)malloc(size*sizeof(int)); if (!arr) // (arr == NULL) --> allocaton didn't succeed printf("ERROR! Out of memory!\n"); return; } printf("Values in the array: "); for (i=0 ; i < size ; i++) printf("%d ", *(arr+i)); //*(arr+i) == arr[i] printf("\nPlease enter %d numbers: ", size); scanf("%d", &arr[i]); printf("%d ", arr[i]); printf("\n"); malloc – הקצאת מערך בגודל שאינו ידוע מראש לצורך שימוש בהקצאות דנאמיות נשים לב שלמשתנה על ה- heap אין שם, אלא רק יש אליו הצבעה מאחת הפונקציות! int ??? 3000 3004 3008 int 1 3000 5 3004 8 3008 זיכרון ה- heap int:size 3 1000 int*: arr ??? 1004 int: i 1008 int:size 3 1000 int*: arr 3000 1004 int: i ??? 1008 int:size ??? 1000 int*: arr 1004 int: i 1008 הזיכרון של ה- main © Keren Kalif

calloc – הקצאת מערך בגודל שאינו ידוע מראש #include <stdio.h> #include <stdlib.h> void main() { int size, *arr, i; printf("Please enter the size of the array: "); scanf("%d", &size); arr = (int*)calloc(size, sizeof(int)); if (!arr) // (arr == NULL) --> allocaton didn't succeed printf("ERROR! Out of memory!\n"); return; } printf("Values in the array: "); for (i=0 ; i < size ; i++) printf("%d ", *(arr+i)); //*(arr+i) == arr[i] printf("\nPlease enter %d numbers: ", size); scanf("%d", &arr[i]); printf("%d ", arr[i]); printf("\n"); calloc – הקצאת מערך בגודל שאינו ידוע מראש © Keren Kalif

שחרור הזיכרון שהוקצה אחריות המתכנת לשחרר את כל זיכרון שהוקצה דינאמית במילים אחרות, המתכנת אחראי להחזיר למערכת ההפעלה כל שטח זיכרון שביקש ממנה בזמן ריצה הסיבה: שטח ה- heap עליו מוקצות ההקצאות הדינאמיות מוגבל בשטחו ומשותף לכל התוכניות, ולכן התוכנית הבאה שתבקש זיכרון עלולה לקבל NULL, כי אנחנו לא החזרנו את הזיכרון שביקשנו בסיום העבודה... צריך לזכור: כאשר יש סביבת עבודה משותפת, צריך להתחשב גם באחרים (וזה שיעור חשוב בכלל לחיים!) הקומפיילר לא מתריע על אי-שחרור הזיכרון ואין שום אינדיקציה לדעת זאת, לכן חייבים לשים לב!! © Keren Kalif

פונקציה לשחרור הקצאות דינאמיות void free(void* ptr) הפונקציה מקבלת את כתובת ההתחלה של הזיכרון שברצוננו לשחרר הפונקציה מקבלת void* שזה הכללה למצביע מכל טיפוס גם פונקציה זו נמצאת ב- stdlib.h © Keren Kalif

free – שחרור זיכרון ??? 1 5 8 זיכרון ה- heap 3 ??? 3 3000 ??? ??? #include <stdio.h> #include <stdlib.h> void main() { int size, *arr, i; printf("Please enter the size of the array: "); scanf("%d", &size); arr = (int*)malloc(size*sizeof(int)); if (!arr) // (arr == NULL) --> allocaton didn't succeed printf("ERROR! Out of memory!\n"); return; } printf("Values in the array: "); for (i=0 ; i < size ; i++) printf("%d ", *(arr+i)); //*(arr+i) == arr[i] printf("\nPlease enter %d numbers: ", size); scanf("%d", &arr[i]); printf("%d ", arr[i]); printf("\n"); free(arr); free – שחרור זיכרון int ??? 3000 3004 3008 int 1 3000 5 3004 8 3008 זיכרון ה- heap int:size 3 1000 int*: arr ??? 1004 int: i 1008 int:size 3 1000 int*: arr 3000 1004 int: i ??? 1008 int:size ??? 1000 int*: arr 1004 int: i 1008 הזיכרון של ה- main © Keren Kalif

תזכורת: החזרת מערך מפונקציה כאשר מעבירים מערך לפונקציה, מעבירים רק את כתובת ההתחלה שלו, ולא עותק של כל המערך ובאופן דומה, כאשר מחזירים מערך שהוגדר בפונקציה, חוזרת כתובת ההתחלה שלו, ולא עותק של כל המערך הבעייתיות: כאשר יוצאים מהפונקציה שטח הזיכרון שלה משתחרר ויש לנו מצביע לזיכרון שנמחק... הפתרון: הקצאה דינאמית © Keren Kalif

תזכורת: הבעייתיות בהחזרת מערך מפונקציה - דוגמא #define SIZE 3 int* readArray() { int arr[SIZE], i; printf("Please enter %d numbers: ", SIZE); for (i=0 ; i < SIZE ; i++) scanf("%d", &arr[i]); return arr; } void main() int* arr, i; arr = printf("The array is: \n"); printf("%d ", arr[i]); printf("\n"); int[]: arr 3 2000 5 2004 6 2008 int: i 2012 int[]: arr ??? 2000 2004 2008 int: i 2012 הזיכרון של readArray הקומפיילר נותן warning: returning address of local variable or temporary שפירושה שאנחנו מחזירים כתובת למשתנה בזיכרון שישתחרר readArray(); int*: arr ??? 1000 int: i 1004 int*: arr 2000 1000 int: i ??? 1004 הזיכרון של ה- main לעולם לא נחזיר מפונקציה כתובת של משתנה שהוגדר מקומית בפונקציה! © Keren Kalif

הפתרון: הקצאת המערך דינאמית #define SIZE 3 int* buildArray() { int i; int* arr = (int*)malloc(SIZE*sizeof(int)); if (!arr) printf("ERROR! Not enough memory!\n"); exit(1); // to exit the program immedietly. // use only when memory allocation fails } for (i=0 ; i < SIZE; i++) arr[i] = i+1; return arr; void main() int *arr, i; arr = printf("Values in the array: "); for (i=0 ; i < SIZE ; i++) printf("%d ", arr[i]); printf("\n"); free(arr); הפתרון: הקצאת המערך דינאמית אם אנחנו יוצאים מפונקציה שהקצתה דינאמית ולא שחררה, חובה להחזיר את כתובת ההתחלה של ההקצאה, כדי שנוכל לשחרר אותה בהמשך! אחרת כאשר נצא מהפונקציה כבר לא יהיה משתנה שיכיל את מיקום ההקצאה! int:i ??? 2000 int*: arr 3000 2004 int:i ??? 2000 int*: arr 2004 הזיכרון של buildArray int ??? 3000 3004 3008 int 1 3000 2 3004 3 3008 int*: arr ??? 1000 int: i 1004 int*: arr 3000 1000 int: i ??? 1004 buildArray(); הזיכרון של ה- main זיכרון ה- heap © Keren Kalif

הקצאה בתוך פונקציה אחריות שלנו כמתכנתים לשחרר את כל הזיכרון שהקצינו יש לשים לב בייחוד במקרים בהם ההקצאה מתבצעת בפונקציה אחת, והשחרור צריך להיות בפונקציה אחרת! © Keren Kalif

החזרת מערך מפונקציה by pointer למדנו שפונקציה יכולה להחזיר ערכים ע"י קבלת כתובת לעדכון התשובה למשל: int getSum(int arr[], int size); לעומת: void getSum(int arr[], int size, int* sum); באותו אופן ניתן גם להחזיר מערך שייוצר בתוך הפונקציה לא לשכוח להעביר את המערך כ- ** © Keren Kalif

החזרת מערך מפונקציה by pointer - דוגמא void buildArray(int* arr, int size) { int i; arr = (int*)malloc(size*sizeof(int)); if (!arr) } printf("ERROR! Not enough memory!\n"); exit(1); for (i=0 ; i < size ; i++) arr[i] = i+1; void main() int size, *arr=NULL, i; printf("Please enter the size of the array: "); scanf("%d", &size); buildArray(arr, size); printf("Values in the array: "); printf("%d ", arr[i]); printf("\n"); free(arr); int 1 3000 2 3004 3 3008 int ??? 3000 3004 3008 זיכרון ה- heap int:size 3 2000 int: i ??? 2004 int*: arr 3000 2008 int:size 3 2000 int: i ??? 2004 int*: arr NULL 2008 הזיכרון של buildArray int:size 3 1000 int*: arr NULL 1004 int: i ??? 1008 int:size ??? 1000 int*: arr 1004 int: i 1008 int:size ??? 1000 int*: arr NULL 1004 int: i 1008 פה התוכנית תעוף... הזיכרון של ה- main © Keren Kalif

החזרת מערך מפונקציה by pointer - התיקון void buildArray(int** arr, int size) } int i; *arr = (int*)malloc(size*sizeof(int)); if (!*arr) printf("ERROR! Not enough memory!\n"); exit(1); { for (i=0 ; i < size ; i++) (*arr)[i] = i+1; void main() int size, *arr=NULL, i; printf("Please enter the size of the array: "); scanf("%d", &size); buildArray(&arr, size); printf("Values in the array: "); printf("%d ", arr[i]); printf("\n"); free(arr); int ??? 3000 3004 3008 int 1 3000 2 3004 3 3008 זיכרון ה- heap int:size 3 2000 int: i ??? 2004 int**: arr 1004 2008 הזיכרון של buildArray int:size 3 1000 int*: arr 3000 1004 int: i ??? 1008 int:size 3 1000 int*: arr NULL 1004 int: i ??? 1008 int:size ??? 1000 int*: arr NULL 1004 int: i 1008 int:size ??? 1000 int*: arr 1004 int: i 1008 הזיכרון של ה- main © Keren Kalif

שאלה לדוגמא © Keren Kalif

הגדלת מערך בדוגמא הבאה אנו קולטים מהמשתמש מספרים לתוך מערך, לא ידוע כמה איברים המשתמש יכניס כל פעם כאשר כבר אין מקום במערך צריך להגדיל אותו פי 2 האלגוריתם: קרא את האיבר החדש, אם 1- צא אם אין מקום במערך, הקצה מערך גדול פי 2 העתק למערך החדש את האיברים מהמערך הישן שחרר את המערך המקורי שנה את מצביע המערך להצביע למערך החדש © Keren Kalif

הגדלת מערך - הפלט © Keren Kalif

הגדלת מערך - הקוד זיכרון ה- heap -1 ??? 4 3 4300 5 ??? 4 3 4300 5 ??? void main() { int num, i, physSize = 2, logicSize = 0; int *temp, * arr = (int*)calloc(physSize, sizeof(int)); printf("Please enter numbers, -1 to stop:\n"); while (1) scanf("%d", &num); if (num == -1) break; if (physSize == logicSize) physSize *= 2; tmp = (int*)calloc(physSize, sizeof(int)); for (i=0 ; i < logicSize ; i++) tmp[i] = arr[i]; free(arr); arr = tmp; printf("Doubled the array size to %d\n", physSize); } printf("Read number is %d\n", num); arr[logicSize] = num; logicSize++; printf("The array has %d elements (physSize=%d):\n", logicSize, physSize); printf("%d ", arr[i]); printf("\n"); הגדלת מערך - הקוד int 7 2200 int 2200 int 7 2200 4 int 4300 4304 4308 4312 int 7 4300 4 4304 4308 4312 int 7 4300 4 4304 5 4308 4312 זיכרון ה- heap int: num -1 1000 int: i ??? 1004 int: physSize 4 1008 int: logicSize 3 1012 int*: arr 4300 1016 int*: tmp 1020 int: num 5 1000 int: i ??? 1004 int: physSize 4 1008 int: logicSize 3 1012 int*: arr 4300 1016 int*: tmp 1020 int: num 5 1000 int: i ??? 1004 int: physSize 4 1008 int: logicSize 2 1012 int*: arr 2200 1016 int*: tmp 4300 1020 int: num 5 1000 int: i ??? 1004 int: physSize 4 1008 int: logicSize 2 1012 int*: arr 2200 1016 int*: tmp 1020 int: num 5 1000 int: i ??? 1004 int: physSize 4 1008 int: logicSize 2 1012 int*: arr 4300 1016 int*: tmp 1020 int: num 4 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1 1012 int*: arr 2200 1016 int*: tmp 1020 int: num ??? 1000 int: i 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 1016 int*: tmp 1020 int: num ??? 1000 int: i 1004 int: physSize 1008 int: logicSize 1012 int*: arr 1016 int*: tmp 1020 int: num 5 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 2200 1016 int*: tmp 1020 int: num ??? 1000 int: i 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 2200 1016 int*: tmp 1020 int: num 7 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 2200 1016 int*: tmp 1020 int: num 4 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 2200 1016 int*: tmp 1020 int: num 7 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1 1012 int*: arr 2200 1016 int*: tmp 1020 הזיכרון של ה- main © Keren Kalif

הפונקציה realloc שפת C נותנת לנו פונקציה שיודעת להגדיל מערך: הפונקציה יודעת להקצות מקום חדש בגודל המבוקש ולהעתיק אליו את האיברים מהמערך הישן, ובסוף גם לשחרר את הזיכרון של המערך המקורי void* realloc(void* ptr, size_t size; ( הפונקציה מקבלת כפרמטר את כתובת ההתחלה של המערך שאותו ברצונה להגדיל וכן את גודל שטח הזיכרון החדש שרוצים הפונקציה מחזירה את כתובת ההתחלה של השטח החדש שהוקצה יתכן וזו תהיה הכתובת המקורית, במידה והיה מספיק מקום להגדלה במקום המקורי בו המערך הוקצה © Keren Kalif

הגדלת מערך – הקוד בשימוש realloc void main() { int num, i, physSize = 2, logicSize = 0; int* arr = (int*)calloc(physSize, sizeof(int)); printf("Please enter numbers, -1 to stop:\n"); while (1) scanf("%d", &num); if (num == -1) break; if (physSize == logicSize) physSize *= 2; arr = (int*)realloc(arr, physSize*sizeof(int)); printf("Doubled the array size to %d\n", physSize); } printf("Read number is %d\n", num); arr[logicSize] = num; logicSize++; printf("The array has %d elements (physSize=%d):\n", logicSize, physSize); for (i=0 ; i < logicSize ; i++) printf("%d ", arr[i]); printf("\n"); free(arr); הגדלת מערך – הקוד בשימוש realloc int 7 2200 4 int 2200 int 7 2200 int 7 4300 4 4304 4308 4312 int 7 4300 4 4304 5 4308 4312 זיכרון ה- heap int: num 5 1000 int: i ??? 1004 int: physSize 4 1008 int: logicSize 2 1012 int*: arr 2200 1016 int: num 5 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 2200 1016 int: num 5 1000 int: i ??? 1004 int: physSize 4 1008 int: logicSize 2 1012 int*: arr 4300 1016 int: num -1 1000 int: i ??? 1004 int: physSize 4 1008 int: logicSize 3 1012 int*: arr 4300 1016 int: num 4 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 2200 1016 int: num 5 1000 int: i ??? 1004 int: physSize 4 1008 int: logicSize 3 1012 int*: arr 4300 1016 int: num ??? 1000 int: i 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 1016 int: num ??? 1000 int: i 1004 int: physSize 1008 int: logicSize 1012 int*: arr 1016 int: num 4 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1 1012 int*: arr 2200 1016 int: num ??? 1000 int: i 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 2200 1016 int: num 7 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1012 int*: arr 2200 1016 int: num 7 1000 int: i ??? 1004 int: physSize 2 1008 int: logicSize 1 1012 int*: arr 2200 1016 הזיכרון של ה- main © Keren Kalif

מה יהיה פלט התוכנית הבאה? #include <stdio.h> #include <stdlib.h> void main() { char* c = (char*)malloc (sizeof(char)*5); char** s = (char**)malloc (sizeof(char*)*3); int i; for( ; ; ) printf("enter word : "); gets(c); s[i] = c; for(i=0; i<3; i++) printf("strings : %s \n",s[i]); free(c); free(s); } char ‘b’ 4300 ‘y’ 4301 ‘e’ 4302 4303 ??? 4304 char ‘h’ 4300 ‘i’ 4301 4302 ??? 4303 4304 char ??? 4300 4301 4302 4303 4304 char ‘g’ 4300 ‘o’ 4301 4302 ‘d’ 4303 4304 char* 4300 5300 5301 5302 char* 4300 5300 5301 ??? 5302 char* 4300 5300 ??? 5301 5302 char* ??? 5300 5301 5302 i=0 i < 3 i ++ זיכרון ה- heap char*: c 4300 1000 char**: s 5300 1004 int: i 2 1008 char*: c 4300 1000 char**: s 5300 1004 int: i 3 1008 char*: c 4300 1000 char**: s 5300 1004 int: i 1008 char*: c ??? 1000 char**: s 1004 int: i 1008 char*: c 4300 1000 char**: s ??? 1004 int: i 1008 char*: c 4300 1000 char**: s 5300 1004 int: i 1 1008 char*: c 4300 1000 char**: s 5300 1004 int: i ??? 1008 הזיכרון של ה- main © Keren Kalif

התיקון לתוכנית הקודמת זיכרון ה- heap 4300 5300 1 6300 5300 1 6300 5300 void main() { char* c; char** s = (char**)malloc (sizeof(char*)*2); int i; for( ; ; ) c = (char*)malloc (sizeof(char)*5); printf("enter word : "); gets(c); s[i] = c; for(i=0; i<2; i++) printf("strings : %s \n",s[i]); for (i=0 ; i < 2 ; i++) free(s[i]); free(s); } char ??? 4300 4301 4302 4303 4304 char ‘h’ 4300 ‘i’ 4301 4302 ??? 4303 4304 char ‘b’ 6300 ‘y’ 6301 ‘e’ 6302 6303 ??? 6304 char ??? 6300 6301 6302 6303 6304 i=0 i < 2 i ++ char* 4300 5300 ??? 5301 char* 4300 5300 6300 5301 char* ??? 5300 5301 זיכרון ה- heap char*: c 4300 1000 char**: s 5300 1004 int: i 1 1008 char*: c 6300 1000 char**: s 5300 1004 int: i 1 1008 char*: c 6300 1000 char**: s 5300 1004 int: i 2 1008 char*: c ??? 1000 char**: s 1004 int: i 1008 char*: c 4300 1000 char**: s 5300 1004 int: i 1008 char*: c ??? 1000 char**: s 5300 1004 int: i 1008 char*: c ??? 1000 char**: s 5300 1004 int: i 1008 הזיכרון של ה- main © Keren Kalif

הקצאת מערך של מערכים (1) כעת אנחנו יכולים לייצר מטריצה שבכל שורה יש מספר שונה של איברים © Keren Kalif

void main() { int rows,. matrix, i, j, void main() { int rows, **matrix, i, j, *sizes; printf("Enter number of rows in the matrix: "); scanf("%d", &rows); matrix = (int**)malloc(rows*sizeof(int*)); sizes = (int*)malloc(rows*sizeof(int)); for ( ; ; ) printf("Enter size of row #%d: ", i+1); scanf("%d", &sizes[i]); matrix[i] = (int*)calloc(sizes[i], sizeof(int)); for (j=0 ; j < sizes[i] ; j++) matrix[i][j] = (i+1)*10+j+1; } printf("The matrix is:\n"); for (i=0 ; i < rows ; i++) printf("%d ", matrix[i][j]); printf("\n"); free(sizes); for ( ; ; ) free(matrix[i]); free(matrix); הקצאת מערך של מערכים (2) int:rows 3 1000 int**: matrix 3000 1004 int: i 1008 int: j ??? 1012 int*: sizes 4000 1016 int:rows 3 1000 int**: matrix 3000 1004 int: i 2 1008 int: j ??? 1012 int*: sizes 4000 1016 int:rows 3 1000 int**: matrix 3000 1004 int: i 1008 int: j ??? 1012 int*: sizes 4000 1016 int:rows 3 1000 int**: matrix 3000 1004 int: i 1 1008 int: j ??? 1012 int*: sizes 4000 1016 int:rows 3 1000 int**: matrix 3000 1004 int: i 1008 int: j ??? 1012 int*: sizes 4000 1016 int:rows 3 1000 int**: matrix 3000 1004 int: i 2 1008 int: j ??? 1012 int*: sizes 4000 1016 int:rows 3 1000 int**: matrix 3000 1004 int: i 1008 int: j ??? 1012 int*: sizes 4000 1016 int:rows 3 1000 int**: matrix 3000 1004 int: i 1 1008 int: j ??? 1012 int*: sizes 4000 1016 int:rows 3 1000 int**: matrix ??? 1004 int: i 1008 int: j 1012 int*: sizes 1016 int:rows ??? 1000 int**: matrix 1004 int: i 1008 int: j 1012 int*: sizes 1016 int:rows 3 1000 int**: matrix 3000 1004 int: i ??? 1008 int: j 1012 int*: sizes 4000 1016 int:rows 3 1000 int**: matrix 3000 1004 int: i ??? 1008 int: j 1012 int*: sizes 1016 i=0 i < rows i++ הזיכרון של ה- main int 11 4050 12 4054 int 4050 4054 int* 4050 3000 ??? 3004 3008 int* 4050 3000 5000 3004 2200 3008 int* ??? 3000 3004 3008 int* 4050 3000 5000 3004 ??? 3008 int 5000 5004 5008 int 21 5000 22 5004 23 5008 int 31 2200 32 2224 33 2228 34 2232 int 2200 2224 2228 2232 int 2 4000 3 4004 ??? 4008 int ??? 4000 4004 4008 int 2 4000 ??? 4004 4008 int 2 4000 3 4004 4 4008 i=0 i < rows i++ זיכרון ה- heap © Keren Kalif

char* strdup(const char *str) מקבלת מחרוזת ומחזירה העתק שלה: מקצה דינאמית על ה- heap מערך של תווים בגודל המחרוזת המקורית, מעתיקה אליו את התוכן ומחזירה את כתובת ההתחלה שלו תחזיר NULL במידה וההקצאה נכשלה אחריות המתכנת לשחרר את המחרוזת שחזרה!! © Keren Kalif

זיכרון ה- static storage strdup – דוגמא char ‘b’ 5300 ‘y’ 5301 ‘e’ 5302 ‘\0’ 5303 #include <stdio.h> #include <string.h> #include <stdlib.h> void main() } char str1[] = "hi"; char* str2 = "bye"; char* newStr1 = strdup(str1); char* newStr2 = strdup(str2); printf("The first duplicated string: |%s|\n", newStr1); printf("The second duplicated string: |%s|\n", newStr2); free(newStr1); free(newStr2); { זיכרון ה- static storage char ‘b’ 6300 ‘y’ 6301 ‘e’ 6302 ‘\0’ 6303 char ‘h’ 4300 ‘i’ 4301 ‘\0’ 4302 זיכרון ה- heap char: str1[] ‘h’ 1000 ‘i’ 1001 ‘\0’ 1002 char*: str2 5300 1003 char*: newStr1 4300 1007 char*: newStr2 6300 1011 char: str1[] ??? 1000 1001 1002 char*: str2 1003 char*: newStr1 1007 char*: newStr2 1011 char: str1[] ‘h’ 1000 ‘i’ 1001 ‘\0’ 1002 char*: str2 5300 1003 char*: newStr1 4300 1007 char*: newStr2 ??? 1011 char: str1[] ‘h’ 1000 ‘i’ 1001 ‘\0’ 1002 char*: str2 5300 1003 char*: newStr1 ??? 1007 char*: newStr2 1011 char: str1[] ‘h’ 1000 ‘i’ 1001 ‘\0’ 1002 char*: str2 ??? 1003 char*: newStr1 1007 char*: newStr2 1011 הזיכרון של ה- main © Keren Kalif

קליטת טקסט כתוב תוכנית הקולטת שורות טקסט מהמשתמש עד ENTER. ניתן להניח כי בכל שורה יש מקסימום 128 תווים. לא ניתן להניח דבר על כמות השורות שיקלטו ניתן להיעזר בפונקציה strdup © Keren Kalif

קליטת טקסט - הקוד הזיכרון של ה- main #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LINE_LEN 128 int readText (char*** text); void printText(char** text, int numOfLines); void freeText (char** text, int numOfLines); void main() { char** text; int numOfLines; numOfLines = readText(&text); printText(text, numOfLines); freeText(text, numOfLines); } void printText(char** text, int numOfLines) int i; printf("There are %d lines:\n", numOfLines); for (i=0 ; i < numOfLines ; i++) puts(text[i]); קליטת טקסט - הקוד char**: text ??? 1000 int: numOfLines 1001 הזיכרון של ה- main void freeText (char** text, int numOfLines) { int i; for (i=0 ; i < numOfLines ; i++) free(text[i]); free(text); } © Keren Kalif

קליטת טקסט - הקוד (2) הזיכרון של ה- main זיכרון ה- heap strdup(temp); int readText (char*** text) { char temp[MAX_LINE_LEN]; int phisicalSize=2, logicalSize=0; int fContinue = 1; *text = (char**)malloc(phisicalSize*sizeof(char*)); printf("Enter strings, ENTER to stop:\n"); while (fContinue) gets(temp); if (temp[0] == '\0') fContinue = 0; else if (logicalSize == phisicalSize) phisicalSize *= 2; *text = (char**)realloc (*text, phisicalSize*sizeof(char*)); } (*text)[logicalSize++] = return logicalSize; קליטת טקסט - הקוד (2) char**: text ??? 1000 int: numOfLines 1001 char**: text 7300 1000 int: numOfLines ??? 1001 char**: text 4300 1000 int: numOfLines ??? 1001 הזיכרון של ה- main char* 5300 7300 6300 7304 5800 7308 ??? 7312 char* 5300 4300 6300 4304 char* ??? 4300 4304 char* 5300 4300 ??? 4304 char ‘h’ 5300 ‘i’ 5301 ‘\0’ 5302 char ‘m’ 5800 ‘e’ 5801 ‘\0’ 5802 char ‘b’ 6300 ‘l’ 6301 ‘a’ 6302 ‘\0’ 6303 זיכרון ה- heap char***: text 1000 2000 char: temp[128] “me” 2004 … ??? 2131 int: phisicalSize 4 2132 int: logicalSize 2 2136 int: fContinue 1 2140 char***: text 1000 2000 char: temp[128] “me” 2004 … ??? 2131 int: phisicalSize 4 2132 int: logicalSize 3 2136 int: fContinue 1 2140 char***: text 1000 2000 char: temp[128] “” 2004 … ??? 2131 int: phisicalSize 4 2132 int: logicalSize 3 2136 int: fContinue 2140 char***: text 1000 2000 char: temp[128] “” 2004 … ??? 2131 int: phisicalSize 4 2132 int: logicalSize 3 2136 int: fContinue 1 2140 char***: text 1000 2000 char: temp[128] “me” 2004 … ??? 2131 int: phisicalSize 2 2132 int: logicalSize 2136 int: fContinue 1 2140 char***: text 1000 2000 char: temp[128] “bla” 2004 … ??? 2131 int: phisicalSize 2 2132 int: logicalSize 2136 int: fContinue 1 2140 char***: text 1000 2000 char: temp[128] “hi” 2004 … ??? 2131 int: phisicalSize 2 2132 int: logicalSize 1 2136 int: fContinue 2140 char***: text 1000 2000 char: temp[128] “hi” 2004 … ??? 2131 int: phisicalSize 2 2132 int: logicalSize 2136 int: fContinue 1 2140 char***: text 1000 2000 char: temp[128] ??? 2004 … 2131 int: phisicalSize 2 2132 int: logicalSize 2136 int: fContinue 1 2140 char***: text 1000 2000 char: temp[128] “bla” 2004 … ??? 2131 int: phisicalSize 2 2132 int: logicalSize 1 2136 int: fContinue 2140 strdup(temp); הזיכרון של ה- main © Keren Kalif

קליטת טקסט – המשך הרצה הזיכרון של ה- main #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LINE_LEN 128 int readText (char*** text); void printText(char** text, int numOfLines); void freeText (char** text, int numOfLines); void main() { char** text; int numOfLines; numOfLines = readText(&text); printText(text, numOfLines); freeText(text, numOfLines); } void printText(char** text, int numOfLines) int i; printf("There are %d lines:\n", numOfLines); for (i=0 ; i < numOfLines ; i++) puts(text[i]); קליטת טקסט – המשך הרצה char**: text 7300 1000 int: numOfLines ??? 1001 char**: text 7300 1000 int: numOfLines 3 1001 הזיכרון של ה- main char* 5300 7300 6300 7304 5800 7308 ??? 7312 char ‘h’ 5300 ‘i’ 5301 ‘\0’ 5302 char ‘m’ 5800 ‘e’ 5801 ‘\0’ 5802 char ‘b’ 6300 ‘l’ 6301 ‘a’ 6302 ‘\0’ 6303 void freeText (char** text, int numOfLines) { int i; for (i=0 ; i < numOfLines ; i++) free(text[i]); free(text); } © Keren Kalif

אין שום שינוי פרט לטיפוס ב- calloc/malloc struct Point { int x, y; } typedef point_t; void main() int size, i; point_t* points; printf("How many points? "); scanf("%d", &size); points = (point_t*)calloc(size, sizeof(point_t)); if (!points) printf("ERROR! Out of memory!\n"); return; } printf("Points are: "); for (i=0 ; i < size ; i++) printf("(%d, %d) ", points[i].x, points[i].y); printf("\nPlease enter %d points: \n", size); printf("Point #%d: ", i+1); scanf("%d %d", &points[i].x, &points[i].y); printf("\n"); free(points) הקצאת מערך של מבנים אין שום שינוי פרט לטיפוס ב- calloc/malloc © Keren Kalif

סוגי מערכים כאשר מקצים מערך של מבנים, ישנן כמה אופציות (למשל עבור המבנה Student): מערך של Student בגודל הידוע בזמן קומפילציה student_t arr[3]; מערך של Student בגודל שאינו ידוע בזמן קומפילציה student_t* arr; מערך של מצביעים ל- Student בגודל הידוע בזמן קומפילציה student_t* arr[3]; מערך של מצביעים ל- Student בגודל שאינו ידוע בזמן קומפילציה student_t** arr; © Keren Kalif

סוגי המערכים (1) מערך של Student בגודל הידוע בזמן קומפילציה student_t arr[3]; במקרה זה כל איברי המערך נמצאים על ה- stack מימוש זה בזבזני במידה ולא נשתמש בכל איברי המערך יעיל מבחינת ביצועים (אין הקצאות דינאמיות) student[]: arr[0]: name arr[0]: id arr[1]: name arr[1]: id arr[2]: name arr[2]: id © Keren Kalif

סוגי המערכים (2) מערך של Student בגודל שאינו ידוע בזמן קומפילציה student_t* arr; במקרה זה רק כתובת ההתחלה של המערך נמצאת על ה- stack, בעוד המבנים עצמם נמצאים על ה- heap גם מימוש זה בזבזני במידה ולא נשתמש בכל איברי המערך יעיל מבחינת ביצועים (יש רק הקצאה אחת) student_t: name “yoyo” 2200 id 1111 2230 “gogo” 2236 2222 2246 student_t*: arr 2200 1000 © Keren Kalif

סוגי המערכים (3) מערך של מצביעים ל- Student בגודל הידוע בזמן קומפילציה student_t* arr[3]; במקרה זה יש מערך של 3 כתובות על ה- stack, והמבנים עצמם יוקצו דינאמית על ה- heap בעת הצורך מימוש זה אופטימלי מבחינת מקום, כלומר נקצה מקום לנתוני המבנה רק בעת הצורך. פחות יעיל מבחינת ביצועים (כל אחד מהאיברים מוקצה דינאמית) student_t: name “yoyo” 2200 id 1111 student_t*[]: arr 2200 1000 5400 NULL student_t: name “yoyo” 5400 id 1111 © Keren Kalif

students*[]: students סוגי המערכים (4) מערך של מצביעים ל- Student בגודל שאינו ידוע בזמן קומפילציה student_t** arr; על ה- stack תהיה רק כתובת ההתחלה של מערך של כתובות מערך הכתובות יוקצה על ה- heap שכן גודלו אינו ידוע בזמן קומפילציה student_t: name “yoyo” 2200 id 1111 student_t: name “yoyo” 5400 id 1111 student_t**: arr 4400 1000 students*[]: students 2200 4400 5400 © Keren Kalif

הקצאת מערך של מבנים בתוך מבנה בדוגמא הבאה יש לנו את המבנה "כיתה" שיכול להכיל מערך של תלמידים מספר התלמידים אינו ידוע מראש וניתן ע"י המשתמש בזמן ריצה מערך התלמידים שבתוך המבנה "כיתה" מוקצה בזמן ריצה בכל איבר במערך יהיו נתונים של סטודנט דוגמא, המשתמש בחר מערך בגודל 4, בכל איבר יהיו נתוני סטודנט © Keren Kalif

דוגמא – הקצאת סטודנטים בכיתה (הקוד בלבד, כך שאפשר לראות אותו ) struct Student { char name[10]; int id; } typedef student_t; struct Class char teacherName[10]; int numOfStudents; student_t* students; }typedef class_t; void main() int i; class_t c = {"Keren"}; printf("How many students? "); scanf("%d", &c.numOfStudents); c.students = (student_t*)calloc(c.numOfStudents , sizeof(student_t)); // check if allocation succeeded.. printf("Enter name and id for each student:\n"); for (i=0 ; i < c.numOfStudents ; i++) printf("Student #%d: ", i+1); scanf("%s %d", c.students[i].name, &c.students[i].id); } printClass(c); free(c.students); void printClass(class_t c) { int i; printf("The teacher is %s and the %d students are:\n", c.teacherName, c.numOfStudents); for (i=0 ; i < c.numOfStudents ; i++) printf(" %d- Name: %s\tId: %d\n", i+1, c.students[i].name, c.students[i].id); } © Keren Kalif

דוגמא – הקצאת סטודנטים בכיתה #include <stdio.h> #include <stdlib.h> struct Student { char name[10]; int id; } typedef student_t; struct Class char teacherName[10]; int numOfStudents; student_t* students; }typedef class_t; void printClass(class_t c) { int i; printf("The teacher is %s and the %d students are:\n", c.teacherName, c.numOfStudents); for (i=0 ; i < c.numOfStudents ; i++) printf(" %d- Name: %s\tId: %d\n", i+1, c.students[i].name, c.students[i].id); } void main() class_t c = {"Keren"}; printf("How many students? "); scanf("%d", &c.numOfStudents); c.students = (student_t*)calloc(c.numOfStudents , sizeof(student_t)); // check if allocation succeeded.. printf("Enter name and id for each student:\n"); printf("Student #%d: ", i+1); scanf("%s %d", c.students[i].name, &c.students[i].id); printClass(c); free(c.students); int:i ??? 3000 class_t: char[10]:c.teacherName “keren” 3004 int: c.numOfStudents 2 student_t*:c.students 2200 הזיכרון של printClass student_t: name “yoyo” 2200 id 1111 2230 “gogo” 2236 2222 2246 student_t: name 2200 id 2230 2236 2246 זיכרון ה- heap int:i ??? 1000 class_t: char[10]:c.teacherName 1004 int: c.numOfStudents student_t*:c.students int:i ??? 1000 class_t: char[10]:c.teacherName “keren” 1004 int: c.numOfStudents 2 student_t*:c.students int:i ??? 1000 class_t: char[10]:c.teacherName “keren” 1004 int: c.numOfStudents student_t*:c.students int:i ??? 1000 class_t: char[10]:c.teacherName “keren” 1004 int: c.numOfStudents 2 student_t*:c.students 2200 © Keren Kalif הזיכרון של ה- main

אבל מה אם נרצה להקצות מערך ולהשתמש רק בחלק מהאיברים? במקרה זה נגדיר עוד שדה שיגיד בכמה מאיברי המערך אנחנו משתמשים, ולא נתייחס לשאר האיברים, מצידנו שיישארו זבל אבל, מבנה יכול לתפוס הרבה מקום בזיכרון וזה יכול להיות בזבוז מקום לכן נקצה מערך של מצביעים למבנים בגודל המקסימלי שאנחנו רוצים, וכל איבר שיהיה בשימוש יצביע למבנה שיוקצה דינאמית © Keren Kalif

הקצאת מערך של מצביעים למבנים בתוך מבנה בדוגמא הבאה יש לנו את המבנה "כיתה" שיכול להכיל אוסף של תלמידים מספר הסטודנטים המקסימלי ידוע מראש ויש מערך של מצביעים ל"סטודנט" בתחילה רשומים לכיתה 0 סטודנטים, וכל פעם נוסיף סטודנט נוסף לכיתה כל איבר יהיה מצביע ל"סטודנט". כל עוד לא נרשם סטודנט המצביע הוא NULL דוגמא: כיתה שיכולים להיות בה מקסימום 4 סטודנטים לאחר רישום סטודנט © Keren Kalif

דוגמא – רישום סטודנטים לכיתה דוגמא – רישום סטודנטים לכיתה void main() { int i, fExit=0; char answer; class_t c = {"Keren"}; do { if (c.registeredStudents == MAX_STUDENTS) printf("Class is full!\n"); break; } printf("Register a student (Y/N)? "); flushall(); scanf("%c", &answer); if (answer == 'N') fExit = 1; else c.students[c.registeredStudents] = (student_t*)calloc(1, sizeof(student_t)); printf("Enter name and id: "); scanf("%s %d", c.students[c.registeredStudents]->name, &c.students[c.registeredStudents]->id); c.registeredStudents++; } while (fExit==0); printClass(c); for (i=0 ; i < c.registeredStudents ; i++) free(c.students[i]); בדיקה אם יש מקום לסטודנט נוסף (הקוד בלבד, כך שאפשר לראות אותו ) #define MAX_STUDENTS 2 struct Student { char name[10]; int id; } typedef student_t; struct Class char teacherName[10]; int registeredStudents; student_t* students[MAX_STUDENTS]; }typedef class_t; void printClass(class_t c) int i; printf("The teacher is %s and the %d students are:\n", c.teacherName, c.registeredStudents); for (i=0 ; i < c.registeredStudents ; i++) printf(" %d- Name: %s\tId: %d\n", i+1, c.students[i]->name, c.students[i]->id); } פניה לאיבר הפנוי הבא בעזרת registeredStudents מערך של מצביעים פניה לשדות איברי המערך בעזרת <- (כי הם מצביעים) © Keren Kalif

דוגמא – רישום סטודנטים לכיתה void main() { int i, fExit=0; char answer; class_t c = {"Keren"}; do { if (c.registeredStudents == MAX_STUDENTS) printf("Class is full!\n"); break; } printf("Register a student (Y/N)? "); flushall(); scanf("%c", &answer); if (answer == 'N') fExit = 1; else c.students[c.registeredStudents] = (student_t*)calloc(1, sizeof(student_t)); printf("Enter name and id: "); scanf("%s %d", c.students[c.registeredStudents]->name, &c.students[c.registeredStudents]->id); c.registeredStudents++; } while (fExit==0); printClass(c); for ( ; ; ) free(c.students[i]); class_t: char[10]:c.teacherName “Keren” 1000 int: c.registeredStudents student_t*[] :c.students 2200 NULL class_t: char[10]:c.teacherName “Keren” 1000 int: c.registeredStudents 1 student_t*[] :c.students 2200 5300 class_t: char[10]:c.teacherName “Keren” 1000 int: c.registeredStudents 2 student_t*[] :c.students 2200 5300 class_t: char[10]:c.teacherName “Keren” 1000 int: c.registeredStudents student_t*[] :c.students NULL class_t: char[10]:c.teacherName “Keren” 1000 int: c.registeredStudents 1 student_t*[] :c.students 2200 NULL class_t: char[10]:c.teacherName ??? 1000 int: c.registeredStudents student_t*[] :c.students הזיכרון החלקי של ה- main #define MAX_STUDENTS 2 struct Student { char name[10]; int id; } typedef student_t; struct Class char teacherName[10]; int registeredStudents; student_t* students[MAX_STUDENTS]; }typedef class_t; void printClass(class_t c) int i; printf("The teacher is %s and the %d students are:\n", c.teacherName, c.registeredStudents); for (i=0 ; i < c.registeredStudents ; i++) printf(" %d- Name: %s\tId: %d\n", i+1, c.students[i]->name, c.students[i]->id); } Y student_t: name 2200 id student_t: name “momo” 2200 id 1111 student_t: name “gogo” 5300 id 2222 student_t: name 5300 id זיכרון ה- heap i=2 i=1 i=0 i=0 i < c.registeredStudents i++ © Keren Kalif

הקצאת מערך של מצביעים למבנים בתוך מבנה בדוגמא הבאה יש לנו את המבנה "כיתה" שיכול להכיל אוסף של תלמידים מספר הסטודנטים המקסימלי אינו ידוע מראש וניתן ע"י המשתמש בזמן ריצה בתחילה רשומים לכיתה 0 סטודנטים, וכל פעם נוסיף סטודנט נוסף לכיתה בכל איבר יהיה מצביע ל"סטודנט". כל עוד לא נרשם סטודנט המצביע הוא NULL דוגמא: כיתה שהמשתמש החליט שיכולים להיות בה מקסימום 4 סטודנטים: לאחר רישום סטודנט © Keren Kalif

דוגמא – רישום סטודנטים לכיתה דינאמית (1) #include <stdio.h> #include <stdlib.h> struct Student { char name[10]; int id; } typedef student_t; struct Class char teacherName[10]; int numOfStudents; int registeredStudents; student_t** students; }typedef class_t; void printClass(class_t c) int i; printf("The teacher is %s and the %d students are:\n", c.teacherName, c.registeredStudents); for (i=0 ; i < c.registeredStudents ; i++) printf(" %d- Name: %s\tId: %d\n", i+1, c.students[i]->name, c.students[i]->id); } מערך בגודל שאינו ידוע עדיין ושכל איבר בו יהיה מצביע © Keren Kalif

דוגמא – רישום סטודנטים לכיתה דינאמית (2) void main() { int i, fExit=0; char answer; class_t c = {"Keren"}; printf("How many max students? "); scanf("%d", &c.numOfStudents); c.students = (student_t**)malloc(sizeof(student_t*)*c.numOfStudents); // check if allocation succeeded do { if (c.registeredStudents == c.numOfStudents) printf("Class is full!\n"); fExit = 1; } printf("Register a student (Y/N)? "); flushall(); scanf("%c", &answer); if (answer == 'N') else c.students[c.registeredStudents] = (student_t*)calloc(1, sizeof(student_t)); printf("Enter name and id: "); scanf("%s %d", c.students[c.registeredStudents]->name, &c.students[c.registeredStudents]->id); c.registeredStudents++; } while (fExit==0); printClass(c); for (i=0 ; i < c.registeredStudents ; i++) free(c.students[i]); free(c.students); דוגמא – רישום סטודנטים לכיתה דינאמית (2) קבלת מספר הסטודנטים המקסימלי מהמשתמש הקצאת מערך של מצביעים לסטודנט שיחרור כל אחד מאיברי המערך שיחרור מערך המצביעים שגם הוקצה דינאמית (כמו בדוגמא "מערך של מערכים") © Keren Kalif

שאלת סיכום © Keren Kalif

ביחידה זו למדנו: פקודות ניהול זיכרון ב- C לעומת C++ malloc calloc free realloc מוטיבציה להקצאות דינאמיות מערך של מצביעים מערך של מצביעים שחוזר כפרמטר מפונקציה מערך של מצביעים למבנים © Keren Kalif