3 - קרן כליף

Download Report

Transcript 3 - קרן כליף

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