230 likes | 383 Views
תרגול 11. מערכים של מצביעים הקצאה דינאמית. מערך של מצביעים. כמו שניתן להגדיר מערך לכל טיפוס בשפת C , ניתן להגדיר גם מערך של מצביעים לדוגמא: אם נרצה לכתוב ברצף 4 משפטים קיימות בפנינו מספר אפשרויות (לפי מה שראינו עד כה): החזקת פוינטר נפרד לכל משפט char *s1 = “HELLO”;
E N D
תרגול 11 מערכים של מצביעים הקצאה דינאמית
מערך של מצביעים • כמו שניתן להגדיר מערך לכל טיפוס בשפת C, ניתן להגדיר גם מערך של מצביעים • לדוגמא: אם נרצה לכתוב ברצף 4 משפטים קיימות בפנינו מספר אפשרויות (לפי מה שראינו עד כה): • החזקת פוינטר נפרד לכל משפט char *s1 = “HELLO”; char *s2 = “How are you?”; char *s3 = “I am the longest line here!!!”; char *s4 = “THE END”; המשפטים לא יהיו ברצף!
מערך של מצביעים - המשך • מערך דו מימדי של תווים: • במערך תהינה 10 שורות, וכמה עמודות ? כמספר התווים במשפט הארוך ביותר פתרון סביר אך בזבזני במקום (בהנחה שיש משפטים הרבה יותר קצרים מהמשפט הארוך ביותר!)
מערך של מצביעים - המשך • הפתרון: מערך של מצביעים • מערך של 4 משפטים ייראה כך: char* message[] = { “HELLO!”, “How are you?”, “I am the longest line here!!”, “THE END”} “HELLO” “How are you?” “I am the longest line here!!” “THE END”
מערך של מצביעים - המשך תוכנית לדוגמא תוכנית המקבלת מערך של שמות (שם פרטי+שם משפחה) ויוצרת ממנו מערך של שמות המשפחה בלבד. חישבו מה הייתם משנים אם הייתם צריכים לקרוא את השמות מהמשתמש לתוך המערך names?
הקצאת זיכרון דינאמית • לעיתים קרובות אנחנו לא נוכל להניח הנחות מראש על גודל הזיכרון הדרוש לנו, לדוגמא- גודל מערך. • עד כה הגבלנו גודל זה והגדרנו מערכים בגודל קבוע, אך הגבלה שכזאת אינה נוחה (כיוון שאינה מאפשרת גמישות בתוכנית) ומובילה לבזבוז זיכרון. • לדוגמא, אם נרצה לקלוט מהמשתמש מספר כלשהו של נתונים, נוכל להניח שלא יהיו יותר מ-20 נתונים, אך אז נגביל את המשתמש (אם רצה לכתוב 32 נתונים,לדוגמא), או לחילופין- נבזבז זיכרון (אם יכתוב בסך הכל 2 נתונים).
הקצאת זיכרון דינאמית - המשך • לשם כך קיימות פונקציות להקצאה דינאמית של זיכרון, בזמן ריצה. • כדי להשתמש בפונקציות אלו יש לכלול את הקובץ: stdlib.h
הקצאת זיכרון דינאמית - המשך • פונקציה להקצאת זיכרון: void* malloc(unsigned size) • הפונקציה malloc( ), מקצה רצף של size בתים ,ומחזירה מצביע לכתובת הבית הראשון, או NULL אם ההקצאה לא הצליחה. • המצביע שהפונקציה מחזירה הוא מטיפוס void*, כלומר מצביע מטיפוס לא ידוע, ולכן צריך לבצע המרה של המצביע לטיפוס הרצוי.
הקצאת זיכרון דינאמית - המשך • חשוב לזכור : לאחר כל הקצאת זיכרון צריך לבדוק שההקצאה הצליחה. • לא תמיד הקצאות זיכרון מצליחות, מכיוון שזה תלוי במקום הרצוף הפנוי שמערכת ההפעלה יכולה לתת לנו- אם מערכת ההפעלה לא תוכל להקצות את המקום הנחוץ, ואנו ננסה להשתמש בו זו תהיה גישה לכתובת לא חוקית והתוכנית "תעוף" בזמן ריצה • כדי לבדוק אם ההקצאה הצליחה בודקים אם המצביע שונה מ-NULL
הקצאת זיכרון דינאמית - המשך • חשוב לזכור : לאחר כל הקצאת זיכרון צריך לבדוק שההקצאה הצליחה. • לא תמיד הקצאות זיכרון מצליחות, מכיוון שזה תלוי במקום הרצוף הפנוי שמערכת ההפעלה יכולה לתת לנו- אם מערכת ההפעלה לא תוכל להקצות את המקום הנחוץ, ואנו ננסה להשתמש בו זו תהיה גישה לכתובת לא חוקית והתוכנית "תעוף" בזמן ריצה • כדי לבדוק אם ההקצאה הצליחה בודקים אם המצביע שונה מ-NULL
הקצאת זיכרון דינאמית - המשך נניח שרוצים להקצות מערך של 50 מספרים שלמים: int *p; p = malloc(50*sizeof(int)); (int*) המרה מ(void*) מספר בתים
הקצאת זיכרון דינאמית - המשך נניח שרוצים להקצות מערך של 50 מספרים שלמים: int *p; if ( p = (int*)malloc(50*sizeof(int)) == NULL) { printf(“Allocation failed\n”); return 1; /* exit(1) */ }
הקצאת זיכרון דינאמית - המשך • אחרי שמקצים זכרון צריך לשחרר אותו void free(void* ptr) • הפונקציה free מקבלת מצביע לכתובת בזכרון ומשחררת את הזכרון שהוקצה מכתובת זו. • למשל: free(p);
דוגמא – הקצאת מערך חד מימדי בפונקציה #include <stdio.h> #include <stdlib.h> #define INPUT 0 #define MEMORY 1 int get_array_size(void); /* get the array size from the user */ void create_array(int**, int) /* create the array */ void read_array(int* , int); /* read numbers into the array */ void print_array(int* , int); /* print the array */ void error(int); /* print an error message */
דוגמא – הקצאת מערך חד מימדי int main( ) { int *arr, size; size = get_array_size( ); create_array(&arr,size); read_array(arr,size); print_array(arr,size); free (arr); return 0; }
דוגמא – הקצאת מערך חד מימדי void create_array(int** a, int size) { if((*a = (int*)malloc(size*sizeof(int))) == NULL) error(MEMORY); return; }
דוגמא – הקצאת מערך חד מימדי void print_array (int a[],int size) { int i; printf(“\nThe array is : \n”); for (i=0;i<size-1;i++) printf(“%d ,”,a[i]); printf(“%d\n”,a[size-1]); return; } void error (int type) { switch(type) { case INPUT: printf("Input error!!!! "); break; case MEMORY: printf("memory allocation failed"); break; } exit (1); return; } int get_array_size() { int size; printf("\nHow many numbers do you want to write ? \n"); if (scanf("%d",&size)!=1) error(INPUT); return size; } void read_array(int a[], int size) { int i; for (i=0 ; i<size ; i++) if (scanf("%d",&a[i])<1) error(INPUT); return; }
הקצאה דינאמית של מערך דו מימדי • הקצאה כמערך חד-מימדי – כמו שראיתם בהרצאה • הקצאה כמערך של מערכים
הקצאה דינאמית של מערך דו מימדי ייצוג מערך דו-מימדי כמערך של מערכים: Matrix : מערכים המייצגים כל אחד שורה מערך של מצביעים – כל מצביע מצביע לשורה המתאימה
הקצאה דינאמית של מערך דו מימדי כדי להקצות דינאמית מערך דו-מימדי (למשל של מס' שלמים) כמערך של מערכים צריך: • להקצות מערך חד מימדי של מצביעים (המערך המאונך בציור) • להקצות מערך חד-מימדי עבור כל שורה, כך שעל כל שורה יצביע המצביע המתאים מהמערך שהקצנו בסעיף 1. וזה הקוד שעושה זאת: למערך של המצביעים נקרא matrix, והוא יהיה מסוג int** (מצביע למצביע למס' שלם) ונניח כי במשתנים row ו-col יש לנו את מימדי המטריצה אותה אנו רוצים להקצות. int row,col,i; int** matrix; … matrix = (int**)malloc(row*sizeof(int*)); if(matrix==NULL) { printf(“Allocation failed\n”); return 1; } for(i=0;i<row;i++) { matrix[i] = (int*)malloc(col*sizeof(int)); if(matrix[i]==NULL) { printf(“Allocation failed\n”); return 1; } }
הקצאה דינאמית של מערך דו מימדי בפונקציה • אם נרצה לעשות את ההקצאה בפונקציה נפרדת, זה אומר שאנחנו צריכים להציב ערך במשתנה matrix לא באותה פונקציה בה הוא מוגדר, אלא בפונקציה שונה. • הצבת ערך בתוך משתנה שהוגדק בפונקציה אחרת יכולה להתבצע בשתי דרכים (בדיוק כמו שלמדנו כשהתחלנו לדבר על מצביעים): • בעזרת מצביע לאותו משתנה (ושליחת הכתובת שלו לפונקציה) • להחזיר את הערך שרוצים להציב כערך מוחזר מהפונקציה בעזרת מצביע: המשתנה שבו אנחנו רוצים להציב ערך בפונקציה הוא matrix (הערך שאנו רוצים להציב הוא הכתובת ש-malloc מחזיר לנו). מאחר ו-matrix הוא מטיפוס int** אז מצביע אליו יהיה מטיפוס int*** והפונקציה תיראה כך: void mymalloc(int*** m, int row, int col) { int i; (*m) = (int**)malloc(row*sizeof(int*)); for(i=0;i<row;i++) (*m)[i] = (int*)malloc(col*sizeof(int)); return; } הקריאה לפונקציה תהיה: mymalloc(&matrix,row,col); וכמובן להוסיף בדיקות שההקצאה הצליחה
הקצאה דינאמית של מערך דו מימדי בפונקציה בעזרת ערך מוחזר: הפונקציה תיראה כך: int** mymalloc(int row, int col) { int**m; int i; m = (int**)malloc(row*sizeof(int*)); for(i=0;i<row;i++) m[i] = (int*)malloc(col*sizeof(int)); return m; } הקריאה לפונקציה תהיה: matrix = mymalloc(row,col); [זה יעבוד בגלל שהזיכרון שקיבלנו מ-malloc נשאר עד סוף התוכנית. צריך לזכור לשחרר אותו כשלא צריך אותו יותר.]
הקצאה דינאמית של מערך דו מימדי בפונקציה • לאחר הקצאה כמערך של מערכים השימוש במערך הדו-מימדי יהיה בדיוק באותו אופן בו השתמשנו כשהמערך הוגדר סטטית. כלומר: matrix[i][j]