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