Transcript 1000

‫כתובות ומערכים‬
‫אריתמטיקה של כתובות‬
‫קרן כליף‬
‫ביחידה זו נלמד‪:‬‬
‫‪ ‬הקשר בין מערך לכתובת‬
‫‪ ‬פעולות חיבור וחיסור עם כתובות‬
‫‪ ‬מצביע מטייל על מערך‬
‫‪ ‬הפונקציות ‪ strstr‬ו‪strchr -‬‬
‫‪ ‬העברת מערך לפונקציה‬
‫‪ ‬הבעייתיות בהחזרת מערך מפונקציה‬
‫‪ ‬מערך של כתובות‬
‫‪ ‬איתחול מצביע למחרוזת קבועה‬
‫‪ ‬העברת מצביע לפונקציה לצורך שינוי הצבעתו‬
‫‪2‬‬
‫‪© Keren Kalif‬‬
‫הקשר בין מערך וכתובת‬
‫‪‬‬
‫‪‬‬
‫כאשר פונים למשתנה רגיל מקבלים את הערך בתא‬
‫כאשר פונים למערך‪ ,‬מקבלים את כתובת תחילת המערך‬
‫)(‪void main‬‬
‫{‬
‫פניה לשם המערך נותנת לנו את כתובת ההתחלה שלו!‬
‫;}‪int arr[] = {4,2,8‬‬
‫;‪cout << "In main1: The array starts at " << arr << endl‬‬
‫;‪cout << "In main2: The array starts at " << &arr << endl‬‬
‫ואפשר גם כך‪:‬‬
‫}‬
‫פניה לכתובת של ‪arr‬‬
‫‪1000‬‬
‫‪4‬‬
‫‪1004‬‬
‫‪2‬‬
‫‪1008‬‬
‫‪8‬‬
‫‪3‬‬
‫‪© Keren Kalif‬‬
‫‪int[]: arr‬‬
‫יודפס‪In main: The array starts at 1000 :‬‬
‫הקשר בין מערך וכתובת ‪ -‬מחרוזות‬
‫‪‬‬
‫‪‬‬
‫מערך הוא גם מחרוזות‪ ,‬אבל הדפסתו תדפיס את כל‬
‫התווים עד ה‪'\0’ -‬‬
‫כדי להדפיס את כתובת תחילת המחרוזת נשתמש ב‪& -‬‬
‫)(‪void main‬‬
‫{‬
‫;"‪char arr[] = "hello‬‬
‫;‪cout << "In main1: The array starts at " << arr << endl‬‬
‫;‪cout << "In main2: The array starts at " << &arr << endl‬‬
‫}‬
‫‪4‬‬
‫‪© Keren Kalif‬‬
‫הקשר בין מערך וכתובת (‪)2‬‬
‫‪‬‬
‫מאחר ושם המערך הוא למעשה כתובת תחילת המערך‪,‬‬
‫ניתן להפעיל עליו את האופרטור *‬
‫‪ ‬ראינו כי כאשר מפעילים את האופרטור * על משתנה‬
‫המכיל כתובת של ‪ ,int‬אנו מקבלים את הערך שבתוך‬
‫התא של הכתובת‪ ,‬כלומר ‪ int‬כלשהו‬
‫‪ ‬במערך‪ ,‬כתובת ההתחלה מכילה את האיבר הראשון‬
‫במערך‪ ,‬לכן הפעלת האופרטור * על שם המערך תחזיר‬
‫את הערך של האיבר הראשון במערך‬
‫‪5‬‬
‫‪© Keren Kalif‬‬
‫הקשר בין מערך וכתובת (‪)3‬‬
‫)(‪void main‬‬
‫{‬
‫כתובת ‪1000‬‬
‫;}‪int arr[] = {4,2,8‬‬
‫;‪cout <<"value of first element is “ << *arr << endl‬‬
‫}‬
‫כתובת ההתחלה התוכן שבכתובת ‪1000‬‬
‫מכילה את האיבר‬
‫הראשון במערך‬
‫‪1000‬‬
‫‪4‬‬
‫‪1004‬‬
‫‪2‬‬
‫‪1008‬‬
‫‪8‬‬
‫‪6‬‬
‫‪© Keren Kalif‬‬
‫‪int[]: arr‬‬
‫פעולות אריתמטיות על כתובות‬
‫‪ ‬מוגדרות ‪ 3‬פעולות אריתמטיות לפעולות עם כתובות‪:‬‬
‫‪ .1‬כתובת ‪ +‬מספר שלם ‪ ‬כתובת‬
‫‪ ‬כאשר מחברים כתובת ‪ p‬לטיפוס ‪ type‬מספר שלם ‪ k‬התוצאה‪:‬‬
‫)‪p+k =p + k*sizeof(type‬‬
‫‪.2‬‬
‫‪‬‬
‫כתובת ‪ -‬מספר שלם ‪ ‬כתובת‬
‫כאשר מחסרים מכתובת ‪ p‬לטיפוס ‪ type‬מספר שלם ‪ k‬התוצאה‪:‬‬
‫)‪p-k =p - k*sizeof(type‬‬
‫‪.3‬‬
‫‪7‬‬
‫‪© Keren Kalif‬‬
‫כתובת– כתובת ‪ ‬מספר שלם‬
1 ‫דוגמא‬
– ‫פעולות אריתמטיות על כתובות‬
void main()
:‫ ייתן‬1 -‫ קידום ב‬,int ‫מאחר וזוהי כתובת של‬
{
p+k =p + k*sizeof(type)
1000 + 1*4 = 1004
int arr[] = {4,2,8};
int* p = arr;
cout << "&arr=" << arr << " p=" << p << endl;
p++;
cout << "&(arr+1)=" << arr+1 << ", p=" << p << endl;
cout << "*(arr+1)=" << *(arr+1) << ", *p=" << *p << endl;
}
&arr=1000, p=1000 :‫יודפס‬
int[]: arr
int*: p
???
4
1000
???
2
1004
???
8
1008
1000
1004
???
1012
&(arr+1)=1004, p=1004 :‫יודפס‬
*(arr+1)=2, *p=2 :‫יודפס‬
8
© Keren Kalif
2 ‫דוגמא‬
– ‫פעולות אריתמטיות על כתובות‬
void main()
{
int arr[] = {4,2,8};
int* p = NULL;
cout << "arr+0=" << arr+0 << ", *(arr+0)=" << *(arr+0) << endl;
cout << "arr+1=" << arr+1 << ", *(arr+1)=" << *(arr+1) << endl;
cout << "arr+2=" << arr+2 << ", *(arr+2)=" << *(arr+2) << endl;
p = arr + 2;
cout << "p=" << p << ", *p=" << *p << endl;
}
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 :‫יודפס‬
9
© Keren Kalif
3 ‫דוגמא‬
– ‫פעולות אריתמטיות על כתובות‬
void main()
{
int arr[] = {4,2,8};
int* p = NULL;
for (int i=0 ; i < sizeof(arr)/sizeof(arr[0]) ; i++)
cout << "arr+" << i << "=" << arr+i
<< " *(arr+" << i << ")=" << *(arr+i) << endl;
...‫וכמובן שניתן גם עם לולאה‬
p = arr + 2;
cout << "p=" << p << ", *p=" << *p << endl;
}
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 :‫יודפס‬
10
© Keren Kalif
‫חיסור בין כתובות‬
‫חיסור בין כתובות נותן את מספר התאים ביניהם (ולא את הפרש‬
)!‫הכתובות‬
void main()
{
int arr[] = {4,2,8};
int* p;
int size = sizeof(arr)/sizeof(arr[0]);
cout << "The values in the array: “;
1000-1000 = 3
1004-1000
1008-1000
1012-1000
0
1
2

4
???
1000
1000
2
???
1004
1004
8
???
1008
1008
int*: p
1012
1000
1004
???
1008
1012
1012
int: size
???
3
1016
1016
int[]: arr
for ( p=arr ; p-arr < size ; p++ )
cout << *p << “ “;
cout << endl;
}
11
© Keren Kalif
4 ‫דוגמא‬
– ‫פעולות אריתמטיות על כתובות‬
void main()
{
int* p;
int size;
int arr[] = {4,2,8};
int*: p
1020
1012
???
1000
int: size
???
3
1004
int[]: arr
???
4
1008
???
2
1012
???
8
1016
???
1020
size = sizeof(arr)/sizeof(arr[0]);
p = arr + size;
cout << "p=“ << p << “, *p=“ << *p << endl;
p = p - 2;
cout << "After p=p-2:\n”;
cout << "p=“ << p << “, *p=“ << *p << endl;
p=1020, *p=??? :‫יודפס‬
p=1012, *p=2 :‫יודפס‬
}
12
© Keren Kalif
‫שקילויות‬
‫‪‬‬
‫ניתן לראות כי אם ‪ arr‬הינו שם של מערך אזי ערך הביטוי )‪(arr+i‬‬
‫הוא כתובת האיבר ה‪ i -‬במערך‪ ,‬והביטוי )‪ *(arr+i‬הינו תוכן‬
‫האיבר ה‪:i -‬‬
‫)‪ &arr[i] ≡ (arr+i‬‬
‫)‪ arr[i] ≡ *(arr+i‬‬
‫‪13‬‬
‫‪© Keren Kalif‬‬
‫מעבר עולה על איברי מערך עם מצביע‬
‫(ולא עם אינדקס) – פוינטר מטייל‬
‫‪1000‬‬
‫???‬
‫‪4‬‬
‫‪1004‬‬
‫???‬
‫‪2‬‬
‫‪1008‬‬
‫???‬
‫‪8‬‬
‫‪1012‬‬
‫‪1000‬‬
‫‪1004‬‬
‫‪1008‬‬
‫‪1012‬‬
‫???‬
‫‪int*: p‬‬
‫‪1016‬‬
‫???‬
‫‪3‬‬
‫‪int: size‬‬
‫‪int[]: arr‬‬
‫)(‪void main‬‬
‫{‬
‫;}‪int arr[] = {4,2,8‬‬
‫;‪int* p‬‬
‫;)]‪int size = sizeof(arr)/sizeof(arr[0‬‬
‫תזכורת‪:‬‬
‫= )‪arr+size = &arr + size*sizeof(int‬‬
‫‪1000 + 3*4 = 1012‬‬
‫;“ ‪cout << "Values in the array:‬‬
‫) ‪for ( p=arr ; p < arr+size ; p++‬‬
‫;“ “ << ‪cout << *p‬‬
‫‪ p‬הוא משתנה שמחזיק כל פעם את‬
‫;‪cout << endl‬‬
‫הכתובת של האיבר הבא במערך אותו רוצים להדפיס‪,‬‬
‫ומאחר והוא מכיל כתובת ל‪ int -‬הוא מטיפוס *‪.int‬‬
‫צריך לרוץ איתו עד אשר הוא יכיל את‬
‫הכתובת של אחרי סיום המערך (התנאי לסיום הלולאה)‬
‫}‬
‫‪14‬‬
‫‪© Keren Kalif‬‬
‫מעבר יורד על איברי מערך עם מצביע‬
‫(ולא עם אינדקס) – פוינטר מטייל‬
void main()
{
int arr[] = {4,2,8};
int* p;
int size = sizeof(arr)/sizeof(arr[0]);
int[]: arr
int*: p
???
4
1000
???
2
1004
???
8
1008
1008
1004
1000
???
996
1012
cout << "Values in the array: “;
???
3
int: size
1016
for ( p=arr+size-1 ; p >= arr ; p-)
cout << *p << “ “;
:‫תזכורת‬
cout << endl;
arr+size-1 = &arr + (size-1)*sizeof(int) =
}
1000 + (3-1)*4 = 1008
15
© Keren Kalif
‫סדר פעולות‬
‫‪‬‬
‫מה תהייה התוצאה של הפעולות הבאות‪:‬‬
‫‪1000‬‬
‫‪3‬‬
‫‪1004‬‬
‫‪1000‬‬
‫‪int: x‬‬
‫‪int*: pX‬‬
‫;‪int x=3‬‬
‫;‪int* pX = &x‬‬
‫;‪*pX++‬‬
‫‪ ‬האם‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫קודם מקדמים את ‪ pX‬ל‪ 1004 -‬ועל זה מפעילים *?‬
‫קודם מפעילים את * על ‪ pX‬ועל התוכן מפעילים ‪?++‬‬
‫כאשר מופעלים כמה אופרטורים אונריים על משתנה סדר‬
‫הפעולות הוא מימין לשמאל‬
‫‪‬‬
‫‪16‬‬
‫‪© Keren Kalif‬‬
‫לסוגריים יש עדיפות‪ ,‬לכן התיקון יהיה ‪(*pX)++‬‬
‫‪17‬‬
‫‪© Keren Kalif‬‬
‫נשים לב‪...‬‬
‫‪ ‬ראינו שניתן לבצע את הפעולה ‪ ++‬על משתנה מטיפוס‬
‫כתובת‬
‫‪ ‬אבל אסור לקדם מערך (‪ )arr++‬אפילו ש‪ arr -‬הוא‬
‫כתובת תחילת המערך‬
‫‪1000‬‬
‫‪1004‬‬
‫???‬
‫‪4‬‬
‫‪1004‬‬
‫???‬
‫‪2‬‬
‫‪1008‬‬
‫???‬
‫‪8‬‬
‫‪1012‬‬
‫‪1000‬‬
‫‪1004‬‬
‫???‬
‫)(‪void main‬‬
‫{‬
‫;}‪int arr[] = {4,2,8‬‬
‫;‪int* p = arr‬‬
‫‪int[]: arr‬‬
‫‪int*: p‬‬
‫‪// same as: arr = arr+1  arr = 1004‬‬
‫;‪p++‬‬
‫;‪arr++‬‬
‫}‬
‫שורה זו לא תתקמפל‪ ,‬כי לא ניתן לשנות‬
‫את מיקומם של משתנים בזיכרון!!‬
‫הפונקציה ‪strchr‬‬
‫‪‬‬
‫פונקציה המקבלת מחרוזת ותו‪ ,‬ומחזירה את הכתובת של‬
‫המופע הראשון של התו במחרוזת‪ NULL ,‬אם לא קיים‬
‫)‪char* strchr(const char str[], char ch‬‬
‫‪18‬‬
‫‪© Keren Kalif‬‬
void main()
{
char str[] = “abcdef";
char* pos;
char ch = ‘c';
‫דוגמא‬- strchr ‫הפונקציה‬
cout << "|" << str << "| appears at address " << &str << "\n\n";
pos = strchr(str, ch);
if (pos != NULL)
cout << "'" << ch << "' appears first at index=" << pos-str << "\n";
}
ch = 'm';
pos = strchr(str, ch);
if (pos == NULL)
cout << "'" << ch << "' is not in the string\n";
char[]: str
char*: pos
char: ch
‘a’
1000
‘b’
1001
‘c’
1002
‘d’
1003
‘e’
1004
‘f’
1005
0
1006
NULL
1002
???
1007
‘m’
‘c’
1011
19
© Keren Kalif
‫הפונקציה ‪strstr‬‬
‫‪‬‬
‫פונקציה המקבלת ‪ 2‬מחרוזות‪ ,‬ובודקת האם המחרוזת‬
‫השניה היא תת‪-‬מחרוזת בראשונה‬
‫‪‬‬
‫אם כן‪ ,‬מחזירה את כתובת תחילת הרצף בראשונה‪ ,‬אחרת‬
‫מחזירה ‪NULL‬‬
‫‪char* strstr(const char str1[],‬‬
‫)][‪const char str2‬‬
‫‪20‬‬
‫‪© Keren Kalif‬‬
‫דוגמא‬- strstr ‫הפונקציה‬
void main()
{
char str[] = "abcdef“, *pos, subStr[] = "cde“;
cout << "|" << str << "| appears at address " << &str << "\n\n";
pos = strstr(str, subStr);
if (pos != NULL)
char[]: str
cout << "|" << subStr << "| starts at index="
<< pos-str << "\n\n";
‘a’
1000
‘b’
1001
‘c’
1002
‘d’
1003
‘e’
1004
‘f’
1005
0
1006
char*: pos
NULL
1002
???
1007
char[]: sub
‘c’
1011
‘d’
1012
‘d’
‘e’
1013
0
1014
strcpy(subStr, "cdd”);
pos = strstr(str, subStr);
if (pos == NULL)
cout << "|" << subStr << "| is not a sub-string\n";
}
21
© Keren Kalif
‫העברת מערך לפונקציה‬
‫‪ ‬ראינו שבעזרת כתובת תחילת המערך (שם המערך) ניתן‬
‫לגשת לכל איברי המערך‬
‫‪ ‬ראינו כאשר מעבירים מערך לפונקציה‪ ,‬הפונקציה יכולה‬
‫לשנות את המערך המקורי‬
‫‪ ‬הסיבה היא שלא מועבר עותק של המערך לפונקציה‪,‬‬
‫אלא מועברת כתובת ההתחלה שלו (שם המערך)‬
‫‪ ‬כאשר מעבירים מערך לפונקציה יש להעביר כפרמטר גם‬
‫את גודלו‬
‫‪22‬‬
‫‪© Keren Kalif‬‬
23
© Keren Kalif
‫העברת מערך לפונקציה‬
int*: arr
1000
2000
3
int: size
2004
int* arr :‫או‬
void printArray(int arr[], int size)
printArray ‫הזיכרון של‬
{
cout << "In func: The array starts at “ << arr << endl;
}
‫ למעשה מעבירים‬,‫כאשר מעבירים מערך כפרמטר לפונקציה‬
‫ לכן ניתן לכתוב בהצהרה שהפרמטר‬.‫את כתובת ההתחלה שלו‬
int* arr ‫ או‬int arr[] ‫הוא‬
‫ ההתייחסות למערך בתוך הפונקציה היא כאל מצביע‬,‫בכל צורת כתיבה‬
void main()
{
int arr[] = {4, 2, 8};
cout << "In main: The array starts at “ << arr << endl;
printArray(arr, sizeof(arr)/sizeof(arr[0]));
???
4
int[]: arr
}
???
2
???
8
1000
1004
1008
main -‫הזיכרון של ה‬
)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};
cout << "The sum is “
<< sumArray(arr, sizeof(arr)/sizeof(arr[0]))
}
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 -‫הזיכרון של ה‬
<< endl;
24
© 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;
}
void main()
{
int arr[] = {4,2,8};
cout << "The sum is “
<< sumArray(arr, sizeof(arr)/sizeof(arr[0]))
{
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
???
8 1008
main -‫הזיכרון של ה‬
<< endl;
25
© 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;
}
void main()
{
int arr[] = {4,2,8};
cout << "The sum is “ <<
{
int*: arr
1004
2000
int: size
2
2004
int*: p
1004
1008
1012
???
2008
int: sum
???
10
0
2
2012
printArray ‫הזיכרון של‬
int[]: arr
sumArray(arr+1, sizeof(arr)/sizeof(arr[0])-1)
???
4
1000
???
2
1004
???
8 1008
main -‫הזיכרון של ה‬
<< endl;
26
© Keren Kalif
‫ פונקציה המגדילה את כל איברי‬:‫דוגמא‬
1 -‫המערך ב‬
void incArray(int* arr, int size)
{
1012
int* p;
for ( p=arr ; p < arr+size ; p++)
(*p)++;
}
void printArray(int* arr, int size)
{
1012
int* p;
for ( p=arr ; p < arr+size ; p++ )
cout << *p << “ “;
cout << endl;
}
void main()
{
int arr[] = {4,2,8};
int size = sizeof(arr)/sizeof(arr[0]);
incArray(arr, size);
cout << "The array after increment is: “;
printArray(arr, size);
}
int*: arr
1000
2000
int: size
3
2004
1000
1004
1008
1012
???
2008
int*: p
printArray
incArray ‫הזיכרון של‬
int[]: arr
int: size
???
4
5
1000
???
2
3
1004
???
8
9
1008
???
3
1012
main -‫הזיכרון של ה‬
27
© Keren Kalif
‫מדוע צריך להעביר לפונקציה את גודל‬
)..sizeof ‫המערך (ולא להסתמך על‬
void printArraySize(int* arr)
{
int size;
‫ וגודלו של משתנה‬,‫ הוא מטיפוס כתובת‬arr
...‫ בתים‬4 ‫מטיפוס כתובת הוא תמיד‬
‫לכן כאשר מתייחסים לשם המערך בפונקציה‬
!‫לא ניתן לדעת את גודלו‬
‫רק בפונקציה שבה מוקצה שטח הזיכרון של‬
!sizeof ‫המערך ניתן לדעת את גודלו ע"י‬
cout << "in function: sizeof(arr)= “ << sizeof(arr) << endl;
size = sizeof(arr)/sizeof(arr[0]);
4
4
cout << "in function: size = “ << size << endl;
}
void main()
{
int arr[] = {4,2,8};
}
cout << "in main: sizeof(arr)= “ << sizeof(arr) << endl;
printArraySize(arr);
28
© Keren Kalif
)1( strlen ‫ מימוש הפונקציה‬:‫דוגמא‬
int myStrlen(char str[])
{
int len = 0;
for ( ; *str != ‘\0’ ; str++ )
len++;
return len;
‫ על שם‬++ ‫בפונקציה כן מותר לבצע‬
}
‫ שכן פה מתייחסים לתוכנו‬,‫המערך‬
‫ ולא משנים את כתובתו‬,‫של משתנה‬
!‫הפיזית של המערך‬
void main()
{
char str[4] = "Hi";
cout << "The len of |” << str << “| is “
<< myStrlen(str) << endl
}
char*: str
Int: len
1000
1001
1002
2004
???
0
1
2
2008
myStrlen -‫הזיכרון של ה‬
char[]: str
???
‘H’
1000
???
‘i’
1001
???
0 1002
main -‫הזיכרון של ה‬
29
© Keren Kalif
)2( strlen ‫ מימוש הפונקציה‬:‫דוגמא‬
int myStrlen(char str[])
{
*p != ‘\0’ :‫שקול ל‬
char* p;
for ( p=str ; *p ; p++ );
return p-str; // returns: 2
}
void main()
{
char str[4] = "Hi";
cout << "The len of |” << str << “| is “
myStrlen(str) << endl;
<<
}
char*: str
1000
2000
1002
1000
1001
???
2004
myStrlen -‫הזיכרון של ה‬
char*: p
char[]: str
???
‘H’
1000
???
‘i’
1001
???
0 1002
main -‫הזיכרון של ה‬
30
© Keren Kalif
31
© Keren Kalif
strcpy ‫ מימוש‬:‫דוגמא‬
void myStrcpy(char* dest, char* src)
{
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = '\0';
char*: dest
}
char*: src
char[]: str1
char[]: str2
???
‘H’
1000
???
‘i’
1001
???
0
1002
???
0
1003
???
‘H’
1004
1004
1005
1006
1004
2000
???
‘i’
1005
1000
1001
1002
1000
2004
???
0
1006
void main()
myStrcpy ‫הזיכרון של‬
??? 1007
{
main -‫הזיכרון של ה‬
char str1[4] = "Hi", str2[4];
cout << "Before copy: str1=|” << str1 << “|, str2=|” << str2 << “|\n”;
myStrcpy(str2, str1);
cout << “After copy: str1=|” << str1 << “|, str2=|” << str2 << “|\n”;
}
32
© Keren Kalif
strcmp ‫ מימוש‬:‫דוגמא‬
int myStrcmp(char str1[], char str2[])
{
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
}
char*: str1
1004
1005
1006
2000
char*: str2
1000
1001
1002
2004
myStrcmp ‫הזיכרון של‬
char[]: str1
if (*str1 < *str2)
return -1;
return 1;
void main()
{
char str1[4] = "Hi", str2[4]="Hii";
cout << “Result of strcmp: “
myStrcmp(str2, str1))
<<
}
char[]: str2
<< endl;
‘H’
??
1000
??
‘i’
1001
??
0
1002
??
0
1003
‘H’
??
1004
??
‘i’
1005
??
‘i’
1006
??
0
1007
main -‫הזיכרון של ה‬
strcat ‫ מימוש‬:‫דוגמא‬
char[]: str1
???
‘H’
1000
???
‘i’
1001
???
‘Y’
0
1002
???
‘o’
0
1003
0
???
‘u’
1004
???
0
1005
???
‘Y’
1006
void myStrcat(char dest[], char src[])
2
{
strcpy(dest+strlen(dest), src);
}
1000
2000
1006
???
‘o’
1007
2004
???
‘u’
1008
1002
void main()
{
char*: dest
char*: src
char[]: str2
myStrcat ‫הזיכרון של‬
???
0 1009
main -‫הזיכרון של ה‬
char str1[6] = "Hi", str2[4]="You";
cout << "Before: str1=|” << str1 << “|, str2=|” << str2 << “|\n”;
myStrcat(str1, str2);
cout << “After: str1=|” << str1 << “|, str2=|” << str2 << “|\n”;
}
33
© Keren Kalif
‫החזרת מערך מפונקציה‬
‫‪ ‬פונקציה יכולה להחזיר כל טיפוס‪ ,‬פרט למערך (בינתיים)‬
‫‪ ‬ראינו שכאשר פונים לשם המערך‪ ,‬פונים לכתובת‬
‫ההתחלה שלו‬
‫‪ ‬כאשר מעבירים מערך לפונקציה‪ ,‬מעבירים רק את כתובת‬
‫ההתחלה שלו‪ ,‬ולא עותק של כל המערך‬
‫‪ ‬ובאופן דומה‪ ,‬כאשר מחזירים מערך שהוגדר בפונקציה‪,‬‬
‫חוזרת כתובת ההתחלה שלו‪ ,‬ולא עותק של כל המערך‬
‫‪ ‬הבעייתיות‪ :‬כאשר יוצאים מהפונקציה שטח הזיכרון שלה‬
‫משתחרר ויש לנו מצביע לזיכרון שנמחק‪...‬‬
‫‪ ‬הפתרון‪ :‬כאשר נלמד על הקצאות דינאמיות‬
‫‪34‬‬
‫‪© Keren Kalif‬‬
35
© Keren Kalif
‫ דוגמא‬- ‫החזרת מערך מפונקציה‬
const int SIZE=3;
int[]: arr
???
3
2000
int* readArray()
???
5
2004
{
int arr[SIZE], i;
???
6
2008
cout << "Please enter “ << SIZE << “ numbers: “;
???
3
int: i
2012
for (i=0 ; i < SIZE ; i++)
readArray ‫הזיכרון של‬
cin >> arr[i];
return arr;
:warning ‫הקומפיילר נותן‬
}
void main()
{
int* arr, i;
arr = readArray();
returning address of local variable or temporary
‫שפירושה שאנחנו מחזירים כתובת למשתנה בזיכרון שישתחרר‬
‫לעולם לא נחזיר מפונקציה‬
‫כתובת של משתנה‬
cout << “The array is: \n”;
!‫שהוגדר מקומית בפונקציה‬
}
for (i=0 ; i < SIZE ; i++)
cout << arr[i] << “ “;
cout << endl;
int*: arr
2000
???
1000
int: i
???
1004
main -‫הזיכרון של ה‬
‫אריתמטיקה של מטריצות‬
‫‪ ‬כאשר מחברים לשם של מערך מספר ‪ ,i‬מקבלים את‬
‫כתובת האיבר ה‪i -‬‬
‫‪ ‬כאשר פונים למטריצה רק עם מימד אחד מקבלים את‬
‫)‪void printArr(int* arr, int size‬‬
‫כתובת תחילת השורה‬
‫{‬
‫)‪for (int i=0 ; i < size ; i++‬‬
‫;“ “ << ]‪cout << arr[i‬‬
‫;‪cout << endl‬‬
‫}‬
‫אומרים לקומפיילר להתייחס לכתובת‬
‫המטריצה כאל כתובת התחלה של מערך‬
‫שליחת כתובת השורה השניה‬
‫‪36‬‬
‫‪© Keren Kalif‬‬
‫שליחת כתובת האיבר השני‬
‫)(‪void main‬‬
‫{‬
‫;} }‪int mat[2][3] = { {1,2,3}, {4,5,6‬‬
‫;)‪printArr((int*)mat, 6‬‬
‫;)‪printArr(mat[0], 6‬‬
‫;)‪printArr(mat[1], 6‬‬
‫;)‪printArr(mat[1]+1, 6‬‬
‫}‬
‫העברת מטריצה לפונקציה המקבלת‬
‫מערך‬
const int SIZE=3;
void printMatrix(int mat[][SIZE], int rows)
{
for (int i=0 ; i < rows ; i++)
{
for (int j=0 ; j < SIZE; j++)
cout << mat[i][j] << "\t";
cout << endl;
}
}
int getMax(int* arr, int size)
{
int max=arr[0];
for (int i=1 ; i < size ; i++)
if (arr[i] > max)
max = arr[i];
return max;
}
void printArr(int* arr, int size)
{
for (int i=0 ; i < size ; i++)
cout << arr[i] << " ";
cout << endl;
}
37
© Keren Kalif
‫העברת מטריצה לפונקציה‬
)2( ‫המקבלת מערך‬
int[][3]: mat
1.
2.
3.
4.
void main()
}
int i, mat[SIZE][SIZE]=
{ {1,2,3}, {4,5,2}, {8,2,3} };
cout << "Matrix:\n”;
printMatrix(mat, SIZE);
cout << "\nMatrix as arr:\n”;
printArr((int*)mat, SIZE*SIZE);
cout << endl;
‫סימון לקומפיילר להתייחס לכתובת‬
5.
6.
7.
8.
9.
‫ככתובת התחלה של מערך‬
for ( i=0 ; i < SIZE ; i++ )
cout << "The max in line #" << i+1 << " is " <<
getMax(mat[i], SIZE) << endl;
cout << "\nThe max in the matrix is “ <<
getMax((int*)mat, SIZE*SIZE) << endl;
10.
11.
12.
13.
14.
15.
int: i
{
1
1000
2
1004
3
1008
4
1012
5
1016
2
1020
8
1024
2
1028
3
1032
???
0
1
2
3
1036
38
© Keren Kalif
‫העברת מטריצה לפונקציה המקבלת‬
‫מערך‬
‫‪‬‬
‫מטריצה היא למעשה מערך דו‪-‬מימדי‪ :‬שורות ועמודות‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫]‪[0,0‬‬
‫‪‬‬
‫ניתן גם להסתכל עליה כמערך של מערכים‪ ,‬כאשר כל‬
‫איבר הוא מערך חד‪-‬מימדי‬
‫‪‬‬
‫לכן ניתן לשלוח כל איבר בה (שהוא מערך בפני עצמו)‬
‫לפונקציה המקבלת מערך חד‪-‬מימדי‬
‫‪39‬‬
‫‪© Keren Kalif‬‬
‫מערך של כתובות‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ראינו כי מערך הוא אוסף של איברים מאותו טיפוס‬
‫ראינו שמשתנה המכיל כתובת הוא גם טיפוס‬
‫ניתן להגדיר מערך של כתובות‪:‬‬
‫][‪int* matrix‬‬
‫‪‬‬
‫‪‬‬
‫כל איבר במערך הוא כתובת‬
‫כל כתובת כזו יכולה להיות כתובת התחלה של מערך חד‪-‬מימדי‬
‫‪ ‬כאשר מעבירים מערך של כתובות לפונקציה נכתוב אותו‬
‫בהצהרה כך‪ int** arr :‬או כך‪int* arr[] :‬‬
‫‪40‬‬
‫‪© Keren Kalif‬‬
1036
2000
int: rows
2
2004
int: cols
3
2008
1
0
???
2012
int**: mat
int: i
int* mat[]
‫מערך של מצביעים‬
)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++ )
cout << mat [i][j] << “\t”;
cout << endl;
}
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};
}
41
© Keren Kalif
cout << "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 -‫הזיכרון של ה‬
int**: mat
1036
2000
int: rows
2
2004
int: cols
3
2008
1036
1040
1044
???
2012
int**: pRows
42
© Keren Kalif
‫מערך של מצביעים‬
)2( ‫דוגמא‬
1000
1004
1008
1012
1016
1020
1024
??? 2016
printMatrix ‫הזיכרון של‬
int*: pCols
void printMatrix(int** mat, int rows, int cols)
{
int[]: arr1
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++)
int[]: arr2
cout << *pCols << “\t”;
cout << endl;
}
‫ משום שזה משתנה המכיל‬int** ‫ מטיפוס‬pRows
}
.‫את הכתובת של האיבר במערך אליו אנו רוצים לפנות‬
void main()
int[]: arr3
int**
‫מטיפוס‬
‫הוא‬
int
‫ל‬
‫כתובות‬
‫מכיל‬
‫והמערך‬
‫מאחר‬
{
int arr1[]={1,2,3}, arr2[]={4,5,6}, arr3[]={7,8,9};
int* mat1[] = {arr1, arr2};
}
cout << "matrix 1:\n”;
printMatrix(mat1, 2, 3);
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 -‫הזיכרון של ה‬
‫‪43‬‬
‫‪© Keren Kalif‬‬
‫איתחול מצביע למחרוזת קבועה‬
‫‪‬‬
‫‪‬‬
‫ראינו איתחול מחרוזת‪:‬‬
‫‪1000‬‬
‫’‪‘H‬‬
‫‪1001‬‬
‫’‪‘i‬‬
‫‪1002‬‬
‫’‪‘\0‬‬
‫‪char[]: str‬‬
‫;”‪char str[] = “Hi‬‬
‫ניתן לאתחל מצביע למחרוזת כך‪:‬‬
‫‪2500‬‬
‫’‪‘H‬‬
‫‪char‬‬
‫‪2501‬‬
‫’‪‘i‬‬
‫‪char‬‬
‫‪2502‬‬
‫’‪‘\0‬‬
‫‪char‬‬
‫זיכרון ה‪static storage -‬‬
‫‪1000‬‬
‫‪2500‬‬
‫‪char*: str‬‬
‫;”‪char* str = “Hi‬‬
‫‪ str ‬יכיל את כתובת ההתחלה של המערך‬
‫‪ ‬כאשר מאתחלים מצביע למחרוזת בצורה זו‬
‫המערך נשמר בזיכרון הנקרא ‪static storage‬‬
‫‪ ‬כל פעולה שניתן לבצע על מערך ניתן לבצע על‬
‫מערך תווים זה (פרט לשינוי ערכי המערך)‬
‫ה‪static storage -‬‬
‫‪‬‬
‫‪‬‬
‫זהו שטח זיכרון המכיל מחרוזות סטטיות שהוגדרו בזמן‬
‫קומפילציה‪ ,‬ושלא הוקצה להן שטח זיכרון על ה‪,heap -‬‬
‫כמו בדוגמאת איתחול מצביע למחרוזת‪:‬‬
‫;”‪char* str = “Hi‬‬
‫זיכרון זה הינו סטטי ולא ניתן לשנות את תוכנו‪:‬‬
‫)(‪int main‬‬
‫{‬
‫;"‪char* str = "hhhhh‬‬
‫;‪cin >> str‬‬
‫}‬
‫‪44‬‬
‫‪© Keren Kalif‬‬
‫ דוגמא‬- ‫מערך של מצביעים למחרוזות‬
int main()
(*(arr+2))[1] ≡ *)*(arr+2)+1)
{
char* arr[3] = {"This", "is", "nice"};
cout << (*(arr+2))[1] << endl;
}
1000
char*[]: arr
2500
???
1000
2600
???
1004
3200
???
1008
1008
3200
i
char
char
char
‘i’
‘s’
‘\0’
2600
2601
2602
char
‘n’
3200
char
‘T’
2500
char
‘i’
3201
char
‘h’
2501
char
‘c’
3202
char
‘i’
2502
char
‘e’
3203
char
‘s’
2503
char
‘\0’
3204
char
‘\0’
2504
static storage -‫זיכרון ה‬
45
© Keren Kalif
46
© Keren Kalif
‫ הדפסת איברי מערך של מחרוזות‬:‫דוגמא‬
int main()
{
char* words[] = {"Hi", "Bye", "Nice"};
char** ptr;
int numOfWord = sizeof(words)/sizeof(words[0]);
words
ptr
1012
for ( ptr=words ; ptr < words+numOfWords ; ptr++ )
cout << "Day: “ << *ptr << “\t2nd letter:”
<< *(*ptr+1)) << endl;
{
char*[]: words
char**: ptr
int: numOfWords
char
‘B’
3200
char
‘y’
3201
2600
???
1000
char
3200
???
‘e’
3202
1004
char
2500
???
‘\0’
3203
1008
char
1000
1004
1008
1012
???
1012
char
‘i’
2601
???
3
1016
char
‘\0’
2602
‘H’
2600
2600
“Hi”
3200
“Bye”
2500
“Nice”
char
‘N’
2500
char
‘i’
2501
char
‘c’
2502
char
‘e’
2503
char
‘\0’
2504
static storage -‫זיכרון ה‬
‫פונקציה המשנה מצביע‬
void getMinMaxAddress(int arr[], int size, int* min, int* max)
}
min = max = &arr[0];
1000 2004
int*: arr
for (int i=1 ; i < size ; i++)
3
int: size
2008
}
if (arr[i] < *min)
NULL
1000 2012
1008
int*: min
min = &arr[i];
NULL
1000 2016
1004
int*: max
if (arr[i] > *max)
max = &arr[i];
getMinMaxAddress -‫הזיכרון של ה‬
{
cout << "Min at “ << min <<
“ Max at “ << max << endl;
???
5
int[]: arr
1000
{
???
6
1004
,‫הפונקציה שינתה את ההעתקים של הכתובות‬
???
3
1008
void main()
..‫לכן צריך להעביר כתובת של כתובת‬
}
NULL
???
int*: min
1012
int arr[] = {5,6,3};
NULL
???
int*: max
1016
int* min=NULL, *max=NULL;
main -‫הזיכרון של ה‬
getMinMaxAddress(arr, sizeof(arr)/sizeof(arr[0]), min, max);
cout << "Min at “ << min << “ Max at “ << max << endl;
47
© Keren Kalif
)2( ‫פונקציה המשנה מצביע‬
void getMinMaxAddress(int arr[], int size, int** min, int** max)
}
*min = *max = &arr[0];
1000 2004
int*: arr
for ( int i=1 ; i < size ; i++ )
3
int: size
2008
}
1012 2012
int**: min
if (arr[i] < **min)
*min = &arr[i];
1016 2016
int**: max
if (arr[i] > **max)
???
1
2
3
int: i
2020
*max = &arr[i];
getMinMaxAddress -‫הזיכרון של ה‬
{
cout << "Min at “ << *min <<
???
5
int[]: arr
1000
“ Max at “ << *max << endl;
???
6
1004
{
???
3
1008
void main()
NULL
1000
1008
???
int*: min
1012
}
int arr[] = {5,6,3};
NULL
1000
1004
???
int*: max
1016
int* min=NULL, *max=NULL;
main -‫הזיכרון של ה‬
getMinMaxAddress(arr, sizeof(arr)/sizeof(arr[0]), &min, &max);
cout << "Min at “ << min << “ Max at “ << max << endl;
48
© Keren Kalif
‫ביחידה זו למדנו‪:‬‬
‫‪ ‬הקשר בין מערך למצביע‬
‫‪ ‬פעולות חיבור וחיסור עם מצביעים‬
‫‪ ‬מצביע מטייל על מערך‬
‫‪ ‬הפונקציות ‪ strstr‬ו‪strchr -‬‬
‫‪ ‬העברת מערך לפונקציה‬
‫‪ ‬הבעייתיות בהחזרת מערך מפונקציה‬
‫‪ ‬מערך של מצביעים‬
‫‪ ‬איתחול מצביע למחרוזת קבועה‬
‫‪ ‬העברת מצביע לפונקציה לצורך שינוי הצבעתו‬
‫‪49‬‬
‫‪© Keren Kalif‬‬
‫תרגיל ‪:1‬‬
‫‪ ‬כתוב תוכנית‪:‬‬
‫‪ ‬הגדר מערך של מחרוזות‬
‫‪ ‬עבור כל מחרוזת יש לדאוג שהאות הראשונה תהיה‬
‫גדולה‬
‫‪ ‬הדפס את מערך המחרוזות המעודכן‬
‫‪ ‬דוגמא‪:‬‬
‫‪‬‬
‫עבור המערך ”‪ “Kuku”, “abc”, “good‬הפונקציה תשנה‬
‫אותו להיות ”‪“Kuku”, “Abc”, “Good‬‬
‫‪ ‬הגבלה‪ :‬אין להשתמש ב‪ ,[] -‬אלא רק במצביעים‬
‫‪50‬‬
‫‪© Keren Kalif‬‬
‫תרגיל ‪:2‬‬
‫‪.1‬‬
‫כתוב פונקציה המקבלת מערך של מספרים עשרוניים‬
‫וגודלו‪ .‬הפונקציה תקלוט ערכים לתוך המערך‬
‫מהמשתמש‬
‫‪‬‬
‫‪.2‬‬
‫כתוב פונקציה המקבלת מערך של מספרים עשרוניים‬
‫וגודלו ומדפיסה את איבריו מהסוף להתחלה‬
‫‪‬‬
‫‪51‬‬
‫‪© Keren Kalif‬‬
‫הגבלה‪ :‬הפונקציה תשתמש אך ורק במצביעים‪ ,‬ולא ב‪[] -‬‬
‫הגבלה‪ :‬הפונקציה תשתמש אך ורק במצביעים‪ ,‬ולא ב‪[] -‬‬
‫תרגיל ‪( 2‬המשך)‪:‬‬
‫‪ ‬כתוב ‪:main‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫הגדר מערך של ‪ 7‬מספרים עשרוניים‬
‫קלוט לתוכו ערכים באמצעות הפונקציה מסעיף ‪1‬‬
‫קרא לפונקציה מסעיף ‪ 2‬עבור ‪ 3‬האיברים האחרונים המערך‬
‫קרא לפונקציה מסעיף ‪ 2‬עבור כל האיברים פרט לראשון‬
‫ולאחרון‬
‫‪ ‬שימו לב‪ :‬הקוד ב‪ main-‬צריך להיות כללי כך שאם נשנה‬
‫את גודל המערך‪ ,‬לא נצטרך לעשות שינויים נוספים ב‪-‬‬
‫‪main‬‬
‫‪52‬‬
‫‪© Keren Kalif‬‬
‫תרגיל ‪:3‬‬
‫‪‬‬
‫בתרגיל זה יש להשתמש ב‪ 2-‬הפונקציות מתרגיל ‪2‬‬
‫‪‬‬
‫כתוב ‪:main‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪53‬‬
‫‪© Keren Kalif‬‬
‫הגדר מטריצה של מספרים בגודל ‪4X5‬‬
‫שלח כל שורה במטריצה לפונקציה הקוראת נתונים‬
‫שלח את חצי האיברים הראשונים לפונקציה השניה‪ ,‬ואח"כ את‬
‫חצי האיברים השניים‬
‫תרגיל ‪( 3‬המשך)‪:‬‬
‫‪‬‬
‫למשל‪ :‬עבור המטריצה הבאה בגודל ‪ 4X5‬עם ה‪20 -‬‬
‫הערכים הבאים (שנקראו ע"י הפונקציה ראשונה)‬
‫‪‬‬
‫עליך להדפיס את חצי מכמות האיברים הראשונים‬
‫(בצהוב) מהסוף להתחלה‪ ,‬ואח"כ תדפיס את חצי מכמות‬
‫האיברים האחרונים (בירוק) מהסוף להתחלה‪:‬‬
‫‪1,2,5,6,7,8,1,2,3,4, 7,8,9,0,3,4,5,6,9,0‬‬
‫‪54‬‬
‫‪© Keren Kalif‬‬