Download presentation
Presentation is loading. Please wait.
1
מצביעים קרן כליף
2
ביחידה זו נלמד: מהו מצביע (פוינטר) מוטיבציה למצביעים אופרטורים * ו- &
אתחול מצביע העברת פרמטר לפונקציה by pointer מצביע const הקשר בין מערך לכתובת פעולות חיבור וחיסור עם כתובות מצביע מטייל על מערך העברת מערך לפונקציה הבעייתיות בהחזרת מערך מפונקציה מערך של כתובות העברת מצביע לפונקציה לצורך שינוי הצבעתו
3
מוטיבציה החזרת יותר מערך אחד מפונקציה
שפונקציה תוכל לשנות את הפרמטרים שהיא קיבלה העברת מערכים לפונקציות הקצאת מערכים בגודל לא ידוע בזמן קומפילציה – הקצאה דינאמית (לא נראה ביחידה זו)
4
מהו מצביע (פוינטר) עד כה ראינו טיפוסים שונים:
דוגמאות: int – מכיל מספר שלם double – מכיל מספר עשרוני char – מכיל תו מצביע הוא טיפוס המכיל כתובת של משתנה אחר עבור כל טיפוס שלמדנו עד כה יש מצביע מהטיפוס המתאים (למשל מצביע לתא המכיל int, מצביע לתא המכיל double וכו') גודלו של משתנה מטיפוס מצביע הוא 4 בתים
5
<type>* <var_name>;
הגדרת מצביע כדי להגדיר מצביע: <type>* <var_name>; למשל: הגדרת משתנה המצביע למשתנה אחר מטיפוס int: int* ptr; הגדרת משתנה המצביע למשתנה אחר מטיפוס char: char* ptr;
6
אופרטור & כל משתנה נמצא בזיכרון בכתובת כלשהי כדי לקבל את הכתובת של משתנה כלשהו נשתמש באופרטור & את הכתובת שנקבל נוכל לשים במשתנה מטיפוס מצביע מהטיפוס המתאים
7
אופרטור & - דוגמא void main() { int* pX = &x; printf("x = %d\n", x);
int x = 3; int* pX = &x; printf("x = %d\n", x); printf("address of x = %p\n", &x); printf("pX = %p\n", pX); printf("address of pX = %p\n", &pX); } int: x ??? 1000 int*: pX 1004 1008 int: x 3 1000 int*: pX 1004 1008 int: x 3 1000 int*: pX ??? 1004 1008 כדי להדפיס כתובת משתמשים ב- %p יודפס: x=3 יודפס: address of x = 1000 יודפס: pX = 1000 יודפס: address of pX = 1004 אנו רואים שהכתובות במחשב הן לא מספרים דצימאלים, אלא הקסה-דצימאלים! בדוגמאות שלנו נמשיך להשתמש בכתובות בבסיס דצימאלי
8
נשים לב.. הדרך היחידה לתת ערך למשתנה מטיפוס מצביע היא ע"י מתן כתובת של משתנה אחר ע"י האופרטור &, או השמה ממשתנה המכיל מצביע מאותו טיפוס לא ניתן לבצע השמה עם מספר לא נבצע השמה של כתובת למשתנה מצביע שאינו מאותו טיפוס (למשל מכתובת של double לכתובת של int)
9
דוגמאות void main() { int x = 3; double* pDouble; int* pInt1, *pInt2;
pInt1 = &x; pInt1 = 1000; pDouble = pInt1; pInt2 = pInt1; } כאשר מגדירים כמה מצביעים בשורה אחת, צריך להגדיר * לפני כל אחד // cannot convert from int to int* // cannot convert from int* to double* int: x ??? 1000 double*: pDouble 1004 int*: pInt1 1008 int*: pInt2 1012 int: x 3 1000 double*: pDouble ??? 1004 int*: pInt1 1008 int*: pInt2 1012 int: x 3 1000 double*: pDouble ??? 1004 int*: pInt1 1008 int*: pInt2 1012 int: x 3 1000 double*: pDouble ??? 1004 int*: pInt1 1008 int*: pInt2 1012 כל משתנה מטיפוס מצביע תופס 4 בתים בזיכרון, בלי קשר לגודל הטיפוס אליו הוא מצביע
10
דוגמא להדפסת משתנים בפורמט שונה
void main() { int x = 97; printf("as int: %d\n", x); printf("as char: %c\n", x); printf("as address(hexa): %p\n\n", x); printf("&x as address: %p\n", &x); printf("&x as int: %d\n", &x); } 9710 == 6116 12FF6016== as int: 97 as char: a as hexa: &x as addess: 0012FF60 &x as int:
11
אופרטור * כדי לפנות לתוכן שבכתובת אליה אנו מצביעים נשתמש באופרטור *
כדי לפנות לתוכן שבכתובת אליה אנו מצביעים נשתמש באופרטור * void main() { int x = 3, y; int* pX = &x; y = *pX; printf("x = %d\n", x); printf("*pX = %d\n", *pX); printf("y = %d\n", y); } תפנה לתוכן שבכתובת 1000 // same as y = x; int: x 3 1000 int: y ??? 1004 int: x 3 1000 int: y ??? 1004 int*: pX 1008 int: x 3 1000 int: y 1004 int*: pX 1008
12
דוגמא void main() { int x = 7; int* px = &x; int** ppx = &px; int*** pppx = &ppx; printf("x = %d \t &x=%p\n", x, &x); printf("px = %p &px=%p\n", px, &px); printf("ppx = %p &ppx=%p\n", ppx, &ppx); printf("pppx = %p &pppx=%p\n", pppx, &pppx); printf("pppx = %p\n", pppx); printf("*pppx = %p\n", *pppx); printf("**pppx = %p\n", **pppx); printf(“***pppx = %d\n", ***pppx); } 1000 7 int: x 1004 int*: px 1008 int**: ppx 1012 int***: pppx 1000 7 int: x 1004 int*: px 1008 int**: ppx 1000 7 int: x 1000 7 int: x 1004 int*: px x = 7 &x= 1000 px = &px=1004 ppx = 1004 &ppx=1008 pppx = 1008 &pppx=1012 pppx = 1008 *pppx = 1004 **pppx = 1000 ***pppx = 7
13
אתחול מצביעים כמו כל משתנה אחר, גם משתנה מטיפוס מצביע מכיל זבל עד אשר הוא מאותחל כאשר ננסה לפנות לתוכן של מצביע שהתוכן שלו הוא זבל – התוכנית תעוף!! כי למעשה אנחנו מנסים לגשת לתא זיכרון שלא קיים void main() { int x = 3, y; int* pX; y = *pX; } int: x 3 1000 int: y ??? 1004 int*: pX 1008
14
איך נראה מצביע מזובל בקומפיילר
ראינו שהכתובות שאיתן אנו מדגימים אינן הכתובות שהקומפיילר משתמש כתובת מזובלת בקומפיילר אינה ??? אלא כתובת מיוחדת בהקסה-דצימלי: void main() { int x; int* pX = &x, *p; printf("pX=%p, p=%p\n", pX, p); } פה התוכנית תעוף כי מנסים לגשת לכתובת מזובלת
15
מצביעים – מה יקרה בתוכנית? (1) (הנחה: הזיכרון מתחיל בכתובת 1000)
void main() { int num1=10, num2=20; int *p1, *p2; printf("&num1 = %p\n", &num1); printf("&p1 = %p\n", &p1); } יודפס: &num1 = 1000 יודפס: &p1 = 1008 int: num1 10 1000 int: num2 20 1004 int*: p1 ??? 1008 int*: p2 1012
16
מצביעים – מה יקרה בתוכנית? (2) (הנחה: הזיכרון מתחיל בכתובת 1000)
void main() { int num1=10, num2=20; int *p1, *p2; p1 = &num1; num2 = *p1; printf(“p1=%p\n”, p1); printf(“num2=%d\n”, num2); printf(“&num2=%p\n”, &num2); } יודפס: p1 = 1000 יודפס: num2 = 10 יודפס: &num2 = 1004 int: num1 10 1000 int: num2 1004 int*: p1 1008 int*: p2 ??? 1012 int: num1 10 1000 int: num2 20 1004 int*: p1 ??? 1008 int*: p2 1012 int: num1 10 1000 int: num2 20 1004 int*: p1 1008 int*: p2 ??? 1012
17
מצביעים – מה יקרה בתוכנית? (3) (הנחה: הזיכרון מתחיל בכתובת 1000)
void main() { int num1=10, num2=20; int *p1, *p2; *p1 = num1; num2 = *p1; printf(“num2=%d\n”, num2); } התוכנית תעוף כי מנסים לגשת לתוכן של זבל.. int: num1 10 1000 int: num2 20 1004 int*: p1 ??? 1008 int*: p2 1012
18
מצביעים – מה יקרה בתוכנית? (4) (הנחה: הזיכרון מתחיל בכתובת 1000)
void main() { int num1=10, num2=20; int *p1, *p2; p1 = &num1; *p1 = num2; printf(“num1=%d\n”, num1); } יודפס: num1 = 20 int: num1 20 1000 int: num2 1004 int*: p1 1008 int*: p2 ??? 1012 int: num1 10 1000 int: num2 20 1004 int*: p1 1008 int*: p2 ??? 1012 int: num1 20 1000 int: num2 1004 int*: p1 ??? 1008 int*: p2 1012
19
מצביעים – מה יקרה בתוכנית? (5) (הנחה: הזיכרון מתחיל בכתובת 1000)
void main() { int num1=10, num2=20; int *p1, *p2; p1 = &num1; *p1 = &num2; *p1 = *p2; printf(“num1=%d\n”, num1); } לא יתקמפל כי מנסים לשים כתובת בתוך משתנה המכיל int: cannot convert from int* to int int: num1 10 1000 int: num2 20 1004 int*: p1 1008 int*: p2 ??? 1012 int: num1 10 1000 int: num2 20 1004 int*: p1 ??? 1008 int*: p2 1012
20
מצביעים – מה יקרה בתוכנית? (6) (הנחה: הזיכרון מתחיל בכתובת 1000)
void main() { int num1=10, num2=20; int *p1, *p2; p1 = &num1; &num2= p1; printf(“num2=%d\n”, num2); } לא יתקמפל כי לא ניתן לשנות את הכתובת של משתנה, אלא רק את ערכו!! int: num1 10 1000 int: num2 20 1004 int*: p1 1008 int*: p2 ??? 1012 int: num1 10 1000 int: num2 20 1004 int*: p1 ??? 1008 int*: p2 1012
21
העברה by value – דוגמא: swap
המטרה: פונקציה המקבלת 2 מספרים ומחליפה ביניהם void swap(int a, int b) { printf("In function, before swap: a=%d, b=%d\n", a, b); int temp = b; b = a; a = temp; printf("In function, after swap: a=%d, b=%d\n", a, b); } void main() int num1=2, num2=3; printf("In main, before swap: num1=%d, num2=%d\n", num1, num2); swap(num1, num2); printf("In main, after swap: num1=%d, num2=%d\n", num1, num2); int: a 2 2000 int: b 3 2004 int: temp 2008 int: a 3 2000 int: b 2 2004 int: temp 2008 int: a 2 2000 int: b 2004 int: temp 3 2008 int: a 2 2000 int: b 3 2004 int: temp ??? 2008 הזיכרון של swap int: num1 2 1000 int: num2 3 1004 int: num1 ??? 1000 int: num2 1004 הפונקציה לא באמת החליפה בין הערכים... הזיכרון של ה- main
22
העברה by pointer – דוגמא: swap
המטרה: פונקציה המקבלת 2 מספרים ומחליפה ביניהם void swap(int* a, int* b) { int temp = *b; printf("In function, before swap: *a=%d, *b=%d\n", *a, *b); *b = *a; *a = temp; printf("In function, after swap: *a=%d,* b=%d\n",* a, *b); } void main() int num1=2, num2=3; printf("In main, before swap: num1=%d, num2=%d\n", num1, num2); swap(&num1, &num2); printf("In main, after swap: num1=%d, num2=%d\n", num1, num2); int*: a 1000 2000 int*: b 1004 2004 int: temp 3 2008 int*: a 1000 2000 int*: b 1004 2004 int: temp ??? 2008 הזיכרון של swap int: num1 2 1000 int: num2 1004 int: num1 3 1000 int: num2 2 1004 int: num1 2 1000 int: num2 3 1004 int: num1 ??? 1000 int: num2 1004 הזיכרון של ה- main
23
העברת פרמטר לפונקציה – by pointer
ראינו: כאשר מעבירים משתנה לפונקציה עותק שלו מועבר למחסנית של הפונקציה (העברה by value) אם בפונקציה משנים את הפרמטר זה לא משפיע על המשתנה המקורי גם כאשר מעבירים משתנה מטיפוס מצביע לפונקציה מעבירים עותק (של הכתובת), אבל כאשר מבצעים שינוי בתוכן המצביע בפונקציה אז השינוי משפיע גם על המשתנה המקורי העברת פרמטר מטיפוס מצביע לפונקציה המשנה את תוכן המצביע נקראת העברה by pointer
24
החזרת יותר מערך יחיד מפונקציה
למשל נרצה לכתוב פונקציה המקבלת מערך, וצריכה להחזיר מהו המספר המקסימאלי ומה המינימאלי מאחר וניתן ע"י הפקודה return להחזיר ערך אחד בלבד אנחנו בבעיה... הפתרון הוא להעביר כפרמטר by pointer משתנה שיכיל לבסוף את התוצאה
25
מציאת מינימום ומקסימום
void minMax(int arr[], int size, int* min, int* max) { int i; *min = *max = arr[0]; for ( ; ; ) } if (arr[i] < *min) *min = arr[i]; if (arr[i] > *max) *max = arr[i]; void main() int arr[] = {5,2,8}; int minimum, maximum; minMax(arr, sizeof(arr)/sizeof(arr[0]), &minimum, &maximum); printf("max is %d and min is %d\n", maximum, minimum); int: size 3 2000 int*: min 1012 2004 int*: max 1016 2008 int:i 1 2012 int: size 3 2000 int*: min 1012 2004 int*: max 1016 2008 int:i 2 2012 int: size 3 2000 int*: min 1012 2004 int*: max 1016 2008 int:i ??? 2012 int: size 3 2000 int*: min 1012 2004 int*: max 1016 2008 int:i 2012 i=1 i < size i++ הזיכרון של minMax int[]: arr 5 1000 2 1004 8 1008 int:minimum 1012 int:maximum 1016 int[]: arr 5 1000 2 1004 8 1008 int:minimum 1012 int:maximum 1016 int[]: arr 5 1000 2 1004 8 1008 int:minimum ??? 1012 int:maximum 1016 int[]: arr 5 1000 2 1004 8 1008 int:minimum 1012 int:maximum 1016 int[]: arr 5 1000 2 1004 8 1008 int:minimum 1012 int:maximum 1016 תזכורת: כאשר מעבירים מערך לפונקציה מתייחסים למערך המקורי, ולא לעותק שלו! הזיכרון של ה- main
26
הארות ניתן היה להעביר לפונקציה רק את min או רק את max ואת הערך השני להחזיר ע"י return אבל: כאשר הפונקציה מחזירה יותר מערך אחד והם כולם בעלי אותה תפקיד, נעדיף שכולם יוחזרו by pointer (אחידות בסגנון) אם מעבירים לפונקציה פרמטר by pointer שהפונקציה מתבססת על ערכו, חובה לאתחלו בפונקציה, ולא להתבסס על אתחול (שאולי) בוצע בפונקציה שקראה
27
אתחול פרמטר המועבר by pointer
void countPositive(int arr[], int size, int* count) { int i; *count = 0; // it is our responsibility to initialize the value! for ( ; ; ) if (arr[i] > 0) (*count)++; } void main() int arr[] = {-4,2,-8}; int numOfPositive; // we can’t assume that who wrote // the main initialized that variable!! countPositive(arr, sizeof(arr)/sizeof(arr[0]), &numOfPositive); printf("There are %d positive numbers in the array\n", numOfPositive); int: size 3 2000 int*: count 1012 2004 int: i 2008 int: size 3 2000 int*: count 1012 2004 int: i 1 2008 int: size 3 2000 int*: count 1012 2004 int: i ??? 2008 int: size 3 2000 int*: count 1012 2004 int: i 2008 int: size 3 2000 int*: count 1012 2004 int: i 2 2008 i=0 i < size i++ הזיכרון של countPositive int[]: arr -4 1000 2 1004 -8 1008 int: numOfPositive ? 1012 int[]: arr -4 1000 2 1004 -8 1008 int: numOfPositive 1012 int[]: arr -4 1000 2 1004 -8 1008 int: numOfPositive 1 1012 הזיכרון של ה- main
28
פונקציה המחזירה מצביע: כתובת האיבר המקסימלי
int* getAddressOfMaxElem(int arr[], int size) } int i, maxIndex = 0; for ( ; ; ) if (arr[i] > arr[maxIndex]) maxIndex = i; return &arr[maxIndex]; { void main() int arr[] = {3,7,2}; int size = sizeof(arr) / sizeof(arr[0]); int* pMax = printf("Max value is at address %p and is %d\n", pMax, *pMax); int: size 3 2000 int: i 1 2004 int: maxIndex 2008 int: size 3 2000 int: i ??? 2004 int: maxIndex 2008 int: size 3 2000 int: i 2004 int: maxIndex 1 2008 int: size 3 2000 int: i ??? 2004 int: maxIndex 2008 int: size 3 2000 int: i 1 2004 int: maxIndex 2008 int: size 3 2000 int: i 2 2004 int: maxIndex 1 2008 i=1 i < size i++ הזיכרון של getAddressOfMaxElem int[]: arr 3 1000 7 1004 2 1008 int: size 1012 int*: pMax ??? 1016 int[]: arr 3 1000 7 1004 2 1008 int: size 1012 int*: pMax 1016 int[]: arr ??? 1000 1004 1008 int: size 1012 int*: pMax 1016 int[]: arr 3 1000 7 1004 2 1008 int: size ??? 1012 int*: pMax 1016 getAddressOfMaxElem(arr, size); הזיכרון של ה- main
29
אתחול מצביע ראינו שניתן לאתחל מצביע עם כתובת של משתנה מהטיפוס המתאים
ראינו שניתן לאתחל מצביע עם כתובת של משתנה מהטיפוס המתאים ראינו שכאשר לא מאתחלים מצביע, אז כמו כל משתנה לא מאותחל, הוא מכיל לזבל ניתן לאתחל מצביע שלא מצביע לשום-מקום בערך מיוחד הנקרא NULL, שזוהי למעשה הכתובת 0 פניה למצביע שהוא NULL לא תגרום לתעופת התוכנית פניה לתוכן של מצביע שהוא NULL כן תגרום לתעופת התוכנית, כי אין בו כלום..
30
פניה למצביע NULL לעומת מצביע זבל
void main() { int* p1 = NULL, *p2; printf("%p", p1); printf("%p", p2); שורה זו עוברת בהצלחה ומדפיסה את הכתובת NULL ניסיון הדפסה לכתובת זבל מעיף את התוכנית
31
אתחול מצביע ל- NULL - דוגמא
void main() { int x; int* pX = &x, *p = NULL; printf("pX=%p, p=%p\n", pX, p); printf("*p=%d\n", *p); } פה התוכנית לא תעוף כי ניגשים לכתובת מאופסת פה התוכנית כן תעוף כי ניגשים לתוכן של מקום שאין בו כלום
32
פונקציה המחזירה NULL: כתובת איבר לחיפוש
int* findNumber(int arr[], int size, int lookFor) } int i; for ( ; ; ) if (arr[i] == lookFor) return &arr[i]; return NULL; { void main() int arr[] = {3,7,2}; int size = sizeof(arr) / sizeof(arr[0]); int* p = if (p != NULL) printf("The number is found at address %p\n", p); else printf("The number is not in the array\n"); int: size 3 2000 int: i 2 2004 int: lookFor 4 2008 int: size 3 2000 int: i 1 2004 int: lookFor 4 2008 int: size 3 2000 int: i ??? 2004 int: lookFor 4 2008 int: size 3 2000 int: i 2004 int: lookFor 4 2008 i=0 i < size i++ הזיכרון של findNumber int[]: arr 3 1000 7 1004 2 1008 int: size 1012 int*: p NULL 1016 int[]: arr 3 1000 7 1004 2 1008 int: size 1012 int*: p ??? 1016 int[]: arr ??? 1000 1004 1008 int: size 1012 int*: p 1016 int[]: arr 3 1000 7 1004 2 1008 int: size ??? 1012 int*: p 1016 findNumber(arr, size, 4); הזיכרון של ה- main
33
משתנה const ניתן להגדיר משתנה כ- const, ואז לא ניתן לשנות את ערכו במהלך ריצת התוכנית void main() } const double PI = 3.14; PI = ; // l-value specifies const object {
34
מצביע const על תוכן ההצבעה
void main() { int x = 2, y; const int* pX = &x; x = 5; *pX = 4; pX = &y; עדיין ניתן לפנות ל-x ישירות ולשנות את ערכו // l-value specifies const object int: x 2 1000 int: y ??? 1004 const int*: pX 1008 int: x 5 1000 int: y ??? 1004 const int*: pX 1008 int: x 5 1000 int: y ??? 1004 const int*: pX 1008 int: x 2 1000 int: y ??? 1004 const int*: pX 1008 int: x ??? 1000 int: y 1004 const int*: pX 1008 לא ניתן לפנות ל- *pX ולשנות את ערכו הזיכרון של ה- main מצביע const למשתנה אינו הופך את המשתנה ל- const, אלא אך ורק בעיני המצביע עצמו!
35
התוכן של pX קבוע ולא ניתן לשנות את ערכו לאחר האתחול
מצביע const על ההצבעה void main() { int x = 2, y; int* const pX = &x; x = 5; *pX = 4; pX = &y; התוכן של pX קבוע ולא ניתן לשנות את ערכו לאחר האתחול // l-value specifies const object int: x 2 1000 int: y ??? 1004 int* const: pX 1008 int: x 4 1000 int: y ??? 1004 int* const: pX 1008 int: x 5 1000 int: y ??? 1004 int* const: pX 1008 int: x ??? 1000 int: y 1004 int* const: pX 1008 int: x 2 1000 int: y ??? 1004 int* const: pX 1008 הזיכרון של ה- main
36
סיכום: מצביע const ניתן גם להגדיר מצביע כ- const
כך שלא ניתן לשנות את התוכן בכתובת שהמשתנה מצביע מכיל: const <type>* var; כך שלא ניתן לשנות את הכתובת אותה המשתנה מצביע מכיל: <type>* const var;
37
שימוש במצביע const בהעברת פרמטר לפונקציה
כאשר מעבירים נתונים לפונקציה, למעשה מעבירים העתק שלהם (by value), אלא אם מעבירים אותם by pointer ראינו שכאשר מעבירים מערך לפונקציה, למעשה מעבירים את המערך המקורי, ולא העתק (by pointer) הפונקציה יכולה "בטעות" לשנות אותו לכן פונקציות המקבלות מערך ללא כוונה לשנות אותו, יצהירו על הפרמטר שהוא const
38
דוגמא להעברת פרמטר כ- const
הפונקציה מצהירה שלא תשנה את ערכי המערך void foo(const int arr[], int size) { arr[0] = 10; } // l-value specifies const object ניסיון לשנות את תוכן המערך, בניגוד להצהרה!
39
תרגול
40
פניה לשם המערך נותנת לנו את כתובת ההתחלה שלו!
הקשר בין מערך וכתובת כאשר פונים למשתנה רגיל מקבלים את הערך בתא כאשר פונים למערך, מקבלים את כתובת תחילת המערך void main() { int arr[] = {4,2,8}; printf("In main1: The array starts at %p\n", arr); printf("In main2: The array starts at %p\n", &arr); } פניה לשם המערך נותנת לנו את כתובת ההתחלה שלו! ואפשר גם כך: פניה לכתובת של arr יודפס: In main: The array starts at 1000 int[]: arr 4 1000 2 1004 8 1008
41
הקשר בין מערך וכתובת (2) מאחר ושם המערך הוא למעשה כתובת תחילת המערך, ניתן להפעיל עליו את האופרטור * ראינו כי כאשר מפעילים את האופרטור * על משתנה המכיל כתובת של int, אנו מקבלים את הערך שבתוך התא של הכתובת, כלומר int כלשהו במערך, כתובת ההתחלה מכילה את האיבר הראשון במערך, לכן הפעלת האופרטור * על שם המערך תחזיר את הערך של האיבר הראשון במערך
42
הקשר בין מערך וכתובת (3) #include <stdio.h> void main() {
int arr[] = {4,2,8}; printf("value of first element is %d\n", *arr); } כתובת 1000 כתובת ההתחלה מכילה את האיבר הראשון במערך התוכן שבכתובת 1000 int[]: arr 4 1000 2 1004 8 1008
43
פעולות אריתמטיות על כתובות
מוגדרות 3 פעולות אריתמטיות לפעולות עם כתובות: כתובת + מספר שלם כתובת כתובת - מספר שלם כתובת כתובת– כתובת מספר שלם כאשר מחברים כתובת p לטיפוס type מספר שלם k התוצאה: p+k =p + k*sizeof(type) כאשר מחסרים מכתובת p לטיפוס type מספר שלם k התוצאה: p-k =p - k*sizeof(type)
44
פעולות אריתמטיות על כתובות – דוגמא 1
void main() { int arr[] = {4,2,8}; int* p = arr; printf("&arr=%p, p=%p\n", arr, p); p++; printf("&(arr+1)=%p, p=%p\n", arr+1, p); printf("*(arr+1)=%d, *p=%d\n", *(arr+1), *p); } מאחר וזוהי כתובת של int, קידום ב- 1 ייתן: p+k =p + k*sizeof(type) *4 = 1004 יודפס: &arr=1000, p=1000 יודפס: &(arr+1)=1004, p=1004 יודפס: *(arr+1)=2, *p=2 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int[]: arr 4 1000 2 1004 8 1008 int*: p ??? 1012 int[]: arr ??? 1000 1004 1008 int*: p 1012 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012
45
פעולות אריתמטיות על כתובות – דוגמא 2
void main() { int arr[] = {4,2,8}; int* p = NULL; printf("arr+0=%p, *(arr+0)=%d\n", (arr+0), *(arr+0)); printf("arr+1=%p, *(arr+1)=%d\n", (arr+1), *(arr+1)); printf("arr+2=%p, *(arr+2)=%d\n", (arr+2), *(arr+2)); p = arr + 2; printf("p=%p, *p=%d\n", p, *p); } יודפס: arr+0=1000, *(arr+0)=4 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int[]: arr 4 1000 2 1004 8 1008 int*: p ??? 1012 int[]: arr ??? 1000 1004 1008 int*: p 1012 יודפס: arr+1=1004, *(arr+1)=2 יודפס: arr+2=1008, *(arr+2)=8 יודפס: p=1008, *p=8
46
פעולות אריתמטיות על כתובות – דוגמא 3
void main() { int arr[] = {4,2,8}, i; int* p = NULL; for (i=0 ; i < sizeof(arr)/sizeof(arr[0]) ; i++) printf("arr+%d=%p, *(arr+%d)=%d\n", i, (arr+i), i,*(arr+i)); p = arr + 2; printf("p=%p, *p=%d\n", p, *p); } וכמובן שניתן גם עם לולאה... יודפס: arr+0=1000, *(arr+0)=4 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int[]: arr 4 1000 2 1004 8 1008 int*: p ??? 1012 int[]: arr ??? 1000 1004 1008 int*: p 1012 יודפס: arr+1=1004, *(arr+1)=2 יודפס: arr+2=1008, *(arr+2)=8 יודפס: p=1008, *p=8
47
פעולות אריתמטיות על שם מערך
ניתן לראות כי אם arr הינו שם של מערך אזי ערך הביטוי (arr+i) הוא כתובת האיבר ה- i במערך, והביטוי *(arr+i) הינו תוכן האיבר ה- i: &arr[i] ≡ (arr+i) arr[i] ≡ *(arr+i) void main() { int arr[] = {4,2,8}; printf("arr+1=%p, &arr[1]=%p\n", (arr+1), &arr[1]); printf("*(arr+1)=%d, arr[1]=%d\n", *(arr+1), arr[1]); }
48
פעולות אריתמטיות על כתובות – דוגמא 4
void main() { int* p; int size; int arr[] = {4,2,8}; size = sizeof(arr)/sizeof(arr[0]); p = arr + size; printf("p=%p, *p=%d\n", p, *p); p = p - 2; printf("After p=p-2:\n"); } int*: p ??? 1000 int: size 3 1004 int[]: arr 4 1008 2 1012 8 1016 1020 int*: p 1020 1000 int: size 3 1004 int[]: arr 4 1008 2 1012 8 1016 ??? int*: p ??? 1000 int: size 1004 int[]: arr 4 1008 2 1012 8 1016 1020 int*: p ??? 1000 int: size 1004 int[]: arr 1008 1012 1016 1020 int*: p 1012 1000 int: size 3 1004 int[]: arr 4 1008 2 8 1016 ??? 1020 יודפס: p=1020, *p=??? יודפס: p=1012, *p=2
49
סדר פעולות מה תהייה התוצאה של הפעולות הבאות: האם:
int x=3; int* pX = &x; *pX++; האם: קודם מקדמים את pX ל ועל זה מפעילים *? קודם מפעילים את * על pX ועל התוכן מפעילים ++? כאשר מופעלים כמה אופרטורים אונריים על משתנה סדר הפעולות הוא מימין לשמאל לסוגריים יש עדיפות, לכן התיקון יהיה (*pX)++ int: x 3 1000 int*: pX 1004
50
מעבר עולה על איברי מערך עם מצביע (ולא עם אינדקס) – פוינטר מטייל
p הוא משתנה שמחזיק כל פעם את הכתובת של האיבר הבא במערך אותו רוצים להדפיס, ומאחר והוא מכיל כתובת ל- int הוא מטיפוס int*. צריך לרוץ איתו עד אשר הוא יכיל את הכתובת של אחרי סיום המערך (התנאי לסיום הלולאה) int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 int[]: arr ??? 1000 1004 1008 int*: p 1012 int: size 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p ??? 1012 int: size 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p ??? 1012 int: size 3 1016 void main() { int arr[] = {4,2,8}; int* p; int size = sizeof(arr)/sizeof(arr[0]); printf("Values in the array: "); for ( ; ; ) printf("%d ", *p); printf("\n"); } תזכורת: arr+size = &arr + size*sizeof(int) = *4 = 1012 p=arr p < arr+size p++
51
מעבר יורד על איברי מערך עם מצביע (ולא עם אינדקס) – פוינטר מטייל
void main() { int arr[] = {4,2,8}; int* p; int size = sizeof(arr)/sizeof(arr[0]); printf("Values in the array: "); for ( ; ; ) printf("%d ", *p); printf("\n"); } int[]: arr 4 1000 2 1004 8 1008 int*: p 996 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 int[]: arr ??? 1000 1004 1008 int*: p 1012 int: size 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p ??? 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p ??? 1012 int: size 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 p=arr+size-1 p >= arr p-- תזכורת: arr+size-1 = &arr + (size-1)*sizeof(int) = (3-1)*4 = 1008
52
שורה זו לא תתקמפל, כי לא ניתן לשנות את מיקומם של משתנים בזיכרון!!
52 © Keren Kalif נשים לב... ראינו שניתן לבצע את הפעולה ++ על משתנה מטיפוס כתובת אבל אסור לקדם מערך (arr++) אפילו ש- arr הוא כתובת תחילת המערך כך למעשה ננסה לשנות את כתובת תחילת המערך, והרי לא ניתן לשנות את מיקומם של משתנים בזיכרון.. void main() { int arr[] = {4,2,8}; printf("*arr=%d\n", *arr); arr++; } int[]: arr 4 1004 2 8 1008 int[]: arr ??? 1000 1004 1008 int[]: arr 4 1000 2 1004 8 1008 // same as: arr = arr+1 arr = 1004 שורה זו לא תתקמפל, כי לא ניתן לשנות את מיקומם של משתנים בזיכרון!!
53
חיסור בין כתובות חיסור בין כתובות נותן את מספר התאים ביניהם (ולא את הפרש הכתובות!) void main() { int arr[] = {4,2,8}; int* p; int size = sizeof(arr)/sizeof(arr[0]); printf("The values in the array: "); for ( ; ; ) printf("%d ", *p); printf("\n"); } int[]: arr ??? 1000 1004 1008 int*: p 1012 int: size 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p ??? 1012 int: size 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p ??? 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 int[]: arr 4 1000 2 1004 8 1008 int*: p 1012 int: size 3 1016 = 2 = 3 = 1 = 0 p=arr p-arr < size p++
54
העברת מערך לפונקציה ראינו שבעזרת כתובת תחילת המערך (שם המערך) ניתן לגשת לכל איברי המערך ראינו כאשר מעבירים מערך לפונקציה, הפונקציה יכולה לשנות את המערך המקורי הסיבה היא שלא מועבר עותק של המערך לפונקציה, אלא מועברת כתובת ההתחלה שלו (שם המערך) כאשר מעבירים מערך לפונקציה יש להעביר כפרמטר גם את גודלו
55
העברת מערך לפונקציה #include <stdio.h> או: int* arr
void printArray(int arr[], int size) { printf("In func: The array starts at %p\n", arr); } void main() int arr[] = {4,2, 8}; printf("In main: The array starts at %p\n", arr); printArray(arr, sizeof(arr)/sizeof(arr[0])); int*: arr 1000 2000 int: size 3 2004 או: int* arr הזיכרון של printArray כאשר מעבירים מערך כפרמטר לפונקציה, למעשה מעבירים את כתובת ההתחלה שלו. לכן ניתן לכתוב בהצהרה שהפרמטר הוא int arr[] או int* arr בכל צורת כתיבה, ההתייחסות למערך בתוך הפונקציה היא כאל מצביע int[]: arr ??? 1000 1004 1008 int[]: arr 4 1000 2 1004 8 1008 הזיכרון של ה- main
56
דוגמא: פונקציה הסוכמת איברי מערך (1)
int sumArray(int* arr, int size) { int i; int sum = 0; for ( ; ; ) sum += arr[i]; return sum; } void main() int arr[] = {4,2,8}; printf("The sum is %d\”, ); int*: arr 1000 2000 int: size 3 2004 int: i 2008 int: sum 4 2012 int*: arr 1000 2000 int: size 3 2004 int: i 1 2008 int: sum 4 2012 int*: arr 1000 2000 int: size 3 2004 int: i 2008 int: sum 2012 int*: arr 1000 2000 int: size 3 2004 int: i 1 2008 int: sum 6 2012 int*: arr 1000 2000 int: size 3 2004 int: i 2 2008 int: sum 6 2012 int*: arr 1000 2000 int: size 3 2004 int: i 2008 int: sum 14 2012 int*: arr 1000 2000 int: size 3 2004 int: i 2 2008 int: sum 14 2012 int*: arr 1000 2000 int: size 3 2004 int: i ??? 2008 int: sum 2012 int*: arr 1000 2000 int: size 3 2004 int: i ??? 2008 int: sum 2012 i=0 i < size i++ הזיכרון של printArray // same as: sum += *(arr+i) int[]: arr 4 1000 2 1004 8 1008 int[]: arr ??? 1000 1004 1008 הזיכרון של ה- main sumArray(arr, sizeof(arr)/sizeof(arr[0]))
57
דוגמא: פונקציה הסוכמת איברי מערך (2)
int sumArray(int* arr, int size) { int* p; int sum = 0; for ( ; ; ) sum += *p; return sum; } void main() int arr[] = {4,2,8}; printf("The sum is %d\n", ); int*: arr 1000 2000 int: size 3 2004 int*: p 1004 2008 int: sum 4 2012 int*: arr 1000 2000 int: size 3 2004 int*: p 2008 int: sum 2012 int*: arr 1000 2000 int: size 3 2004 int*: p 1004 2008 int: sum 6 2012 int*: arr 1000 2000 int: size 3 2004 int*: p ??? 2008 int: sum 2012 int*: arr 1000 2000 int: size 3 2004 int*: p 1008 2008 int: sum 6 2012 int*: arr 1000 2000 int: size 3 2004 int*: p ??? 2008 int: sum 2012 int*: arr 1000 2000 int: size 3 2004 int*: p 1012 2008 int: sum 14 2012 int*: arr 1000 2000 int: size 3 2004 int*: p 1008 2008 int: sum 14 2012 int*: arr 1000 2000 int: size 3 2004 int*: p 2008 int: sum 4 2012 1012 p=arr p < arr+size p++ הזיכרון של printArray int[]: arr ??? 1000 1004 1008 int[]: arr 4 1000 2 1004 8 1008 הזיכרון של ה- main sumArray(arr, sizeof(arr)/sizeof(arr[0]))
58
דוגמא: סכימת רק חלק מאיברי המערך
int sumArray(int* arr, int size) { int* p; int sum = 0; for ( ; ; ) sum += *p; return sum; } void main() int arr[] = {4,2,8}; printf("The sum is %d\n", ); int*: arr 1004 2000 int: size 2 2004 int*: p 1008 2008 int: sum 2012 int*: arr 1004 2000 int: size 2 2004 int*: p 1008 2008 int: sum 10 2012 int*: arr 1004 2000 int: size 2 2004 int*: p 2008 int: sum 2012 int*: arr 1004 2000 int: size 2 2004 int*: p ??? 2008 int: sum 2012 int*: arr 1004 2000 int: size 2 2004 int*: p 1012 2008 int: sum 10 2012 int*: arr 1004 2000 int: size 2 2004 int*: p ??? 2008 int: sum 2012 int*: arr 1004 2000 int: size 2 2004 int*: p 2008 int: sum 2012 1012 p=arr p < arr+size p++ הזיכרון של printArray int[]: arr ??? 1000 1004 1008 int[]: arr 4 1000 2 1004 8 1008 הזיכרון של ה- main sumArray(arr+1, sizeof(arr)/sizeof(arr[0])-1)
59
מדוע צריך להעביר לפונקציה את גודל המערך (ולא להסתמך על sizeof..)
arr הוא מטיפוס כתובת, וגודלו של משתנה מטיפוס כתובת הוא תמיד 4 בתים... לכן כאשר מתייחסים לשם המערך בפונקציה לא ניתן לדעת את גודלו! רק בפונקציה שבה מוקצה שטח הזיכרון של המערך ניתן לדעת את גודלו ע"י sizeof! void printArraySize(int* arr) { int size = sizeof(arr)/sizeof(arr[0]); printf("size=%d because sizeof(arr) is %d...\n", size, sizeof(arr)); } void main() int arr[] = {4,2,8}; printArraySize(arr); 4 4
60
החזרת מערך מפונקציה פונקציה יכולה להחזיר כל טיפוס, פרט למערך (בינתיים)
ראינו שכאשר פונים לשם המערך, פונים לכתובת ההתחלה שלו כאשר מעבירים מערך לפונקציה, מעבירים רק את כתובת ההתחלה שלו, ולא עותק של כל המערך ובאופן דומה, כאשר מחזירים מערך שהוגדר בפונקציה, חוזרת כתובת ההתחלה שלו, ולא עותק של כל המערך הבעייתיות: כאשר יוצאים מהפונקציה שטח הזיכרון שלה משתחרר ויש לנו מצביע לזיכרון שנמחק... הפתרון: כאשר נלמד על הקצאות דינאמיות
61
החזרת מערך מפונקציה - דוגמא
int[]: arr 3 2000 5 2004 6 2008 int: i 2012 int[]: arr ??? 2000 2004 2008 int: i 2012 #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"); הזיכרון של readArray הקומפיילר נותן warning: returning address of local variable or temporary שפירושה שאנחנו מחזירים כתובת למשתנה בזיכרון שישתחרר לעולם לא נחזיר מפונקציה כתובת של משתנה שהוגדר מקומית בפונקציה! readArray(); int*: arr 2000 1000 int: i ??? 1004 int*: arr ??? 1000 int: i 1004 הזיכרון של ה- main
62
אריתמטיקה של מטריצות כאשר מחברים לשם של מערך מספר i, מקבלים את כתובת האיבר ה- i כאשר מחברים לשם של מטריצה מספר i, מקבלים את כתובת האיבר הראשון בשורה ה- i void printArr(int* arr, int size) { int i; for (i=0 ; i < size ; i++) printf("%d ", arr[i]); printf("\n"); } void main() int mat[2][3] = { {1,2,3}, {4,5,6} }; printArr(mat, 6); printArr((int*)mat, 6); printArr(mat+1, 6); printArr((int*)mat+1, 6); נקבל warning כי הפונקציה מצפה לקבל כתובת התחלה של מערך עושים למטריצה casting ל- int* כדי לא לקבל את ה- warning: למעשה אומרים לקומפיילר להתייחס לכתובת כאל כתובת התחלה של מערך שליחת כתובת השורה השניה שליחת כתובת האיבר השני
63
העברת מטריצה לפונקציה המקבלת מערך
#include <stdio.h> #define SIZE 3 void printMatrix(int mat[][SIZE], int rows) { int i, j; for (i=0 ; i < rows ; i++) } for (j=0 ; j < SIZE; j++) printf("%4d", mat[i][j]); printf("\n"); int getMax(int* arr, int size) int i, max=arr[0]; for (i=1 ; i < size ; i++) if (arr[i] > max) max = arr[i]; return max; void printArr(int* arr, int size) } int i; for (i=0 ; i < size ; i++) printf("%d ", arr[i]); printf("\n"); {
64
העברת מטריצה לפונקציה המקבלת מערך (2)
void main() } int i, mat[SIZE][SIZE]= { {1,2,3}, {4,5,2}, {8,2,3} }; printf("Matrix:\n"); printMatrix(mat, SIZE); printf("\nMatrix as arr:\n"); printArr((int*)mat, SIZE*SIZE); printf("\n"); for ( ; ; ) printf("The max in line #%d is %d\n", i+1, ); printf("\nThe max in the matrix is %d\n", getMax((int*)mat, SIZE*SIZE)); { int[][3]: mat 1 1000 2 1004 3 1008 4 1012 5 1016 1020 8 1024 1028 1032 int: i 1036 int[][3]: mat 1 1000 2 1004 3 1008 4 1012 5 1016 1020 8 1024 1028 1032 int: i 1036 int[][3]: mat 1 1000 2 1004 3 1008 4 1012 5 1016 1020 8 1024 1028 1032 int: i 1036 int[][3]: mat 1 1000 2 1004 3 1008 4 1012 5 1016 1020 8 1024 1028 1032 int: i ??? 1036 int[][3]: mat 1 1000 2 1004 3 1008 4 1012 5 1016 1020 8 1024 1028 1032 int: i 1036 סימון לקומפיילר להתייחס לכתובת ככתובת התחלה של מערך i=0 i < SIZE i++ getMax(mat[i], SIZE)
65
העברת מטריצה לפונקציה המקבלת מערך
מטריצה היא למעשה מערך דו-מימדי: שורות ועמודות ניתן גם להסתכל עליה כמערך של מערכים, ובכל איבר תשמר כתובת ההתחלה של המערך המתאים לכן ניתן לשלוח כל איבר בה (שהוא מערך בפני עצמו) לפונקציה המקבלת מערך חד-מימדי [0,0] [0,0]
66
מערך של כתובות ראינו כי מערך הוא אוסף של איברים מאותו טיפוס
ראינו שמשתנה המכיל כתובת הוא גם טיפוס ניתן להגדיר מערך של כתובות: int* matrix[] כל איבר במערך הוא כתובת כל כתובת כזו יכולה להיות כתובת התחלה של מערך חד-מימדי כאשר מעבירים מערך של כתובות לפונקציה נכתוב אותו בהצהרה כך: int** arr או כך: int* arr[]
67
מערך של מצביעים דוגמא (1)
int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int: i 2012 int: j 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int: i 2012 int: j 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int: i 1 2012 int: j 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int: i 1 2012 int: j 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int: i 2012 int: j 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int: i 2012 int: j ??? 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int: i 1 2012 int: j 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int: i ??? 2012 int: j 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int: i 2012 int: j 1 2016 הזיכרון של printMatrix int[]: arr1 1 1000 2 1004 3 1008 int[]: arr2 4 1012 5 1016 6 1020 int[]: arr3 7 1024 8 1028 9 1032 int*: mat1[] ??? 1036 1040 int[]: arr1 ??? 1000 1004 1008 int[]: arr2 1012 1016 1020 int[]: arr3 1024 1028 1032 int*: mat1[] 1036 1040 int[]: arr1 1 1000 2 1004 3 1008 int[]: arr2 4 1012 5 1016 6 1020 int[]: arr3 7 1024 8 1028 9 1032 int*: mat1[] 1036 1040 int[]: arr1 1 1000 2 1004 3 1008 int[]: arr2 4 1012 5 1016 6 1020 int[]: arr3 7 1024 8 1028 9 1032 int*: mat1[] 1036 1040 void printMatrix(int** mat, int rows, int cols) { int i, j; for ( ; ; ) for ( ; ; ) printf("%d ", mat [i][j]); printf("\n"); } void main() int arr1[]={1,2,3}, arr2[]={4,5,6}, arr3[]={7,8,9}; int* mat1[] = {arr1, arr2}; printf("matrix 1:\n"); printMatrix(mat1, 2, 3); כל איבר במערך הוא כתובת i=0 i < rows i++ j=0 j < cols j++ mat[ i ][ j ] = *(*(mat+i)+j)) דוגמא עבור i=j=0: *(*(1036+0)+0)=*(*1036)) = *1000=1 הזיכרון של ה- main
68
מערך של מצביעים דוגמא (2)
int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 1040 2012 int*: pCols 1012 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 2012 int*: pCols 1012 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 2012 int*: pCols 1008 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 2012 int*: pCols 1004 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 1040 2012 int*: pCols 1012 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 1040 2012 int*: pCols 1016 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 1044 2012 int*: pCols 1024 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 1040 2012 int*: pCols 1024 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 1040 2012 int*: pCols 1020 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 2012 int*: pCols 1000 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows 2012 int*: pCols ??? 2016 int**: mat 1036 2000 int: rows 2 2004 int: cols 3 2008 int**: pRows ??? 2012 int*: pCols 2016 מערך של מצביעים דוגמא (2) int[]: arr1 1 1000 2 1004 3 1008 int[]: arr2 4 1012 5 1016 6 1020 int[]: arr3 7 1024 8 1028 9 1032 int*: mat1[] 1036 1040 int[]: arr1 ??? 1000 1004 1008 int[]: arr2 1012 1016 1020 int[]: arr3 1024 1028 1032 int*: mat1[] 1036 1040 int[]: arr1 1 1000 2 1004 3 1008 int[]: arr2 4 1012 5 1016 6 1020 int[]: arr3 7 1024 8 1028 9 1032 int*: mat1[] 1036 1040 int[]: arr1 1 1000 2 1004 3 1008 int[]: arr2 4 1012 5 1016 6 1020 int[]: arr3 7 1024 8 1028 9 1032 int*: mat1[] ??? 1036 1040 הזיכרון של printMatrix void printMatrix(int** mat, int rows, int cols) { int** pRows, *pCols; for ( ; ; ) for ( ; ; ) printf("%d ", *pCols); printf("\n"); } void main() int arr1[]={1,2,3}, arr2[]={4,5,6}, arr3[]={7,8,9}; int* mat1[] = {arr1, arr2}; printf("matrix 1:\n"); printMatrix(mat1, 2, 3); 1036+2*4=1044 pRows=mat pRows < mat+rows pRows++ 1012+3*4=1024 1000+3*4=1012 pCols=*pRows pCols < *pRows+cols pCols++ הזיכרון של ה- main pRows מטיפוס int** משום שזה משתנה המכיל את הכתובת של האיבר במערך אליו אנו רוצים לפנות. מאחר והמערך מכיל כתובות ל- int הוא מטיפוס int**
69
הפונקציה שינתה את ההעתקים של הכתובות, לכן צריך להעביר כתובת של כתובת..
פונקציה המשנה מצביע void getMinMaxAddress(int arr[], int size, int* min, int* max) } int i; min = max = &arr[0]; for (i=1 ; i < size ; i++) if (arr[i] < *min) min = &arr[i]; if (arr[i] > *max) max = &arr[i]; { printf("Min at %p, Max at %p\n", min, max); void main() int arr[] = {5,6,3}; int* min=NULL, *max=NULL; getMinMaxAddress(arr, sizeof(arr)/sizeof(arr[0]), min, max); int*: arr 1000 2004 int: size 3 2008 int*: min NULL 2012 int*: max 2016 int*: arr 1000 2004 int: size 3 2008 int*: min 1008 2012 int*: max 1004 2016 int*: arr 1000 2004 int: size 3 2008 int*: min 2012 int*: max 2016 הזיכרון של ה- getMinMaxAddress הפונקציה שינתה את ההעתקים של הכתובות, לכן צריך להעביר כתובת של כתובת.. int[]: arr 5 1000 6 1004 3 1008 int*: min NULL 1012 int*: max 1016 int[]: arr ??? 1000 1004 1008 int*: min 1012 int*: max 1016 הזיכרון של ה- main
70
פונקציה המשנה מצביע (2) int*: arr 1000 2004 int: size 3 2008 int**: min 1012 2012 int**: max 1016 2016 int: i 1 2020 int*: arr 1000 2004 int: size 3 2008 int**: min 1012 2012 int**: max 1016 2016 int: i 2 2020 int*: arr 1000 2004 int: size 3 2008 int**: min 1012 2012 int**: max 1016 2016 int: i ??? 2020 int*: arr 1000 2004 int: size 3 2008 int**: min 1012 2012 int**: max 1016 2016 int: i 2020 void getMinMaxAddress(int arr[], int size, int** min, int** max) } int i; *min = *max = &arr[0]; for ( ; ; ) if (arr[i] < **min) *min = &arr[i]; if (arr[i] > **max) *max = &arr[i]; { printf("Min at %p, Max at %p\n", *min, *max); void main() int arr[] = {5,6,3}; int* min=NULL, *max=NULL; getMinMaxAddress(arr, sizeof(arr)/sizeof(arr[0]), &min, &max); printf("Min at %p, Max at %p\n", min, max); i=1 i < size i++ הזיכרון של ה- getMinMaxAddress int[]: arr 5 1000 6 1004 3 1008 int*: min 1012 int*: max 1016 int[]: arr 5 1000 6 1004 3 1008 int*: min 1012 int*: max 1016 int[]: arr 5 1000 6 1004 3 1008 int*: min 1012 int*: max 1016 int[]: arr 5 1000 6 1004 3 1008 int*: min NULL 1012 int*: max 1016 int[]: arr ??? 1000 1004 1008 int*: min 1012 int*: max 1016 הזיכרון של ה- main
71
ביחידה זו למדנו: מהו מצביע (פוינטר) מוטיבציה למצביעים אופרטורים * ו- &
אתחול מצביע העברת פרמטר לפונקציה by pointer מצביע const הקשר בין מערך לכתובת פעולות חיבור וחיסור עם כתובות מצביע מטייל על מערך העברת מערך לפונקציה הבעייתיות בהחזרת מערך מפונקציה מערך של כתובות העברת מצביע לפונקציה לצורך שינוי הצבעתו
72
תרגול
73
תרגיל 2 הגדר 4 מערכים של מספרים באורך 5 כל אחד
הגדר מערך של כתובות A, כך שבאיבר הראשון תהייה כתובת המערך הראשון שהגדרת, באיבר השני כתובת המערך השני וכו' בעזרת המערך A בלבד, יש להדפיס את הערכים הזוגיים שבמערכים.
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.