Presentation is loading. Please wait.

Presentation is loading. Please wait.

מבוא למדעי המחשב מחרוזות, מצביעים וכתובות www.cs.tau.ac.il/courses/cs4math/09b.

Similar presentations


Presentation on theme: "מבוא למדעי המחשב מחרוזות, מצביעים וכתובות www.cs.tau.ac.il/courses/cs4math/09b."— Presentation transcript:

1 מבוא למדעי המחשב מחרוזות, מצביעים וכתובות www.cs.tau.ac.il/courses/cs4math/09b

2 מערכים: תזכורת עד עכשיו הגדרנו כל משתנה בנפרד, ויכולנו לעבוד כך רק עם מספר קטן של משתנים. עד עכשיו הגדרנו כל משתנה בנפרד, ויכולנו לעבוד כך רק עם מספר קטן של משתנים. אפשר להגדיר קבוצה של כמה משתנים מאותו סוג בבת אחת. קבוצה כזאת נקראת "מערך". גודל המערך נרשם בהגדרה שלו. למשל: אפשר להגדיר קבוצה של כמה משתנים מאותו סוג בבת אחת. קבוצה כזאת נקראת "מערך". גודל המערך נרשם בהגדרה שלו. למשל: int A[10]; לכל אחד מהמשתנים במערך יש מספר סידורי, החל מ-0. ההתייחסות אל כל משתנה היא על-ידי שם המערך והמספר הסידורי של המשתנה (בתוך סוגריים מרובעים). למשל: לכל אחד מהמשתנים במערך יש מספר סידורי, החל מ-0. ההתייחסות אל כל משתנה היא על-ידי שם המערך והמספר הסידורי של המשתנה (בתוך סוגריים מרובעים). למשל:A[0]=97; A[0]A[9]. 97........

3 מערכים: תזכורת מערכים שימושיים כשצריך לשמור בזיכרון הרבה נתונים מאותו סוג (למשל שכר של כל אחד מהעובדים, ציונים של כל התלמידים, הפז"מ של כל חייל וכו'). מערכים שימושיים כשצריך לשמור בזיכרון הרבה נתונים מאותו סוג (למשל שכר של כל אחד מהעובדים, ציונים של כל התלמידים, הפז"מ של כל חייל וכו'). כפי שראינו, בעבודה עם מערכים אפשר לעשות לולאה על המס' הסידורי של המשתנה במערך, וכך לכתוב פעם אחת את פעולות הקלט/פלט והחישובים ולבצע אותן באמצעות הלולאה על כל אחד מהמשתנים. כפי שראינו, בעבודה עם מערכים אפשר לעשות לולאה על המס' הסידורי של המשתנה במערך, וכך לכתוב פעם אחת את פעולות הקלט/פלט והחישובים ולבצע אותן באמצעות הלולאה על כל אחד מהמשתנים.

4 תזכורת: אין גישה לכל המערך בבת-אחת אחרי הגדרת המערך אפשר רק להתייחס לכל אחד מהתאים שלו בנפרד. אחרי הגדרת המערך אפשר רק להתייחס לכל אחד מהתאים שלו בנפרד. אי-אפשר לשים ערכים בתאי המערך בבת-אחת: אי-אפשר לשים ערכים בתאי המערך בבת-אחת: למשל אי-אפשר לאפס בבת-אחת את כל התאים, וצריך לעשות זאת ע"י לולאה: למשל אי-אפשר לאפס בבת-אחת את כל התאים, וצריך לעשות זאת ע"י לולאה: for(i=0; i<10; i++) A=0 for(i=0; i<10; i++) A=0 A[i]=0; A[i]=0; אי-אפשר להעתיק מערך למערך אחר ע"י פעולת = גם זה דורש לולאה: אי-אפשר להעתיק מערך למערך אחר ע"י פעולת = גם זה דורש לולאה: for(i=0; i<10; i++) A=B for(i=0; i<10; i++) A=B A[i]=B[i]; A[i]=B[i]; כך גם לגבי פעולות השוואה, הדפסה, קלט, וכו' (דרושה לולאה). כך גם לגבי פעולות השוואה, הדפסה, קלט, וכו' (דרושה לולאה).

5 תזכורת: אסור לחרוג מתחום המערך אם נכתוב לתא במערך שאינו בתחום שהוגדר, התוכנית "תעוף". אם נכתוב לתא במערך שאינו בתחום שהוגדר, התוכנית "תעוף". למשל: למשל: int a[10]; a[10]=0;a[-1]=5;

6 תזכורת: גודל מערך הוא מספר קבוע בהגדרת מערך הגודל שלו חייב להיות מספר קבוע, ולא יכול להיות ערך של משתנה. בהגדרת מערך הגודל שלו חייב להיות מספר קבוע, ולא יכול להיות ערך של משתנה. למשל: למשל: int a[10]; int my_size=10; ולא: int a[my_size]; גודל מערך יכול להיות קבוע שמוגדר ב- #define (כי זה מספר שכתוב בתוכנית ולא יכול להשתנות במהלך הריצה). גודל מערך יכול להיות קבוע שמוגדר ב- #define (כי זה מספר שכתוב בתוכנית ולא יכול להשתנות במהלך הריצה). #define SIZE 10 int a[SIZE];

7 תזכורת: העברת מערך לפונקציה אמרנו שלא ניתן לבצע פעולות רגילות על מערך בבת-אחת, אבל אפשר להעביר מערך לפונקציה. אותה פונקציה יכולה לקבל מערכים בגדלים שונים. אמרנו שלא ניתן לבצע פעולות רגילות על מערך בבת-אחת, אבל אפשר להעביר מערך לפונקציה. אותה פונקציה יכולה לקבל מערכים בגדלים שונים. כשכותבים פונקציה שמיועדת עבור מערכים בגדלים שונים, אז בדרך-כלל צריך שאחד המשתנים שהיא תקבל יהיה גודל המערך, כדי שנוכל להתייחס בתוך הפונקציה לגודל הזה ולא לחרוג ממנו. כשכותבים פונקציה שמיועדת עבור מערכים בגדלים שונים, אז בדרך-כלל צריך שאחד המשתנים שהיא תקבל יהיה גודל המערך, כדי שנוכל להתייחס בתוך הפונקציה לגודל הזה ולא לחרוג ממנו. למשל כותרת פונקציה לסכימת איברי מערך תהיה: למשל כותרת פונקציה לסכימת איברי מערך תהיה: int calc_sum(int my_array[ ], int size) והשימוש בפונקציה יהיה למשל על-ידי: והשימוש בפונקציה יהיה למשל על-ידי: sum=calc_sum(grades, 10); sum=calc_sum(grades, 10);

8 תזכורת: העברת מערך לפונקציה בשונה ממשתנים אחרים, כשמעבירים מערך לפונקציה הוא לא מועתק למשתנה חדש, כלומר לפונקציה מועבר המערך המקורי. הפונקציה עובדת על אותו מערך, אפילו אם יש לו שם אחר. בשונה ממשתנים אחרים, כשמעבירים מערך לפונקציה הוא לא מועתק למשתנה חדש, כלומר לפונקציה מועבר המערך המקורי. הפונקציה עובדת על אותו מערך, אפילו אם יש לו שם אחר. לכן גם אין צורך לציין גודל מערך בתוך ה-[ ] בכותרת הפונקציה, כי הוא לא מוגדר מחדש (וכאמור אפשר להעביר מערך בכל גודל). לכן גם אין צורך לציין גודל מערך בתוך ה-[ ] בכותרת הפונקציה, כי הוא לא מוגדר מחדש (וכאמור אפשר להעביר מערך בכל גודל).

9 מחרוזות מחרוזת היא רצף תווים בעל משמעות (למשל מילה או משפט). מחרוזת היא רצף תווים בעל משמעות (למשל מילה או משפט). לשמירת מחרוזת בזיכרון נשתמש באופן טבעי במערך של תווים. למשל: char word[20]; לשמירת מחרוזת בזיכרון נשתמש באופן טבעי במערך של תווים. למשל: char word[20]; אבל בהרבה מקרים נירצה להתייחס אל מחרוזת בבת-אחת, ולא רק כמערך של תווים (למשל לקלוט מילה בבת-אחת ולא תו-תו). אבל בהרבה מקרים נירצה להתייחס אל מחרוזת בבת-אחת, ולא רק כמערך של תווים (למשל לקלוט מילה בבת-אחת ולא תו-תו). כמו-כן, במקרים רבים אורך המחרוזת לא ידוע מראש (יתכן שנקבל מהקלט מילה קצרה יותר מגודל המערך שהגדרנו). כמו-כן, במקרים רבים אורך המחרוזת לא ידוע מראש (יתכן שנקבל מהקלט מילה קצרה יותר מגודל המערך שהגדרנו). נכיר היום פקודות ופונקציות-ספריה שכבר נכתבו במיוחד לצורך עבודה עם מחרוזות, ומאפשרות להתגבר על הבעיות האלה. נכיר היום פקודות ופונקציות-ספריה שכבר נכתבו במיוחד לצורך עבודה עם מחרוזות, ומאפשרות להתגבר על הבעיות האלה.

10 מחרוזות – איתחול לדוגמא, יש דרך יותר טבעית לאתחל מערך שמייצג מחרוזת (במקום לרשום את התווים מופרדים בפסיקים): לדוגמא, יש דרך יותר טבעית לאתחל מערך שמייצג מחרוזת (במקום לרשום את התווים מופרדים בפסיקים): char message[]=“Hello world!”; char message[]=“Hello world!”; שימו לב שמשתמשים כאן בגרשיים כפולים, ולא בגרשיים בודדים כמו שעשינו עבור תו בודד. שימו לב שמשתמשים כאן בגרשיים כפולים, ולא בגרשיים בודדים כמו שעשינו עבור תו בודד. (ניראה בהמשך שיצוג מחרוזת קבועה נעשה תמיד ע"י גרשיים כפולים). (ניראה בהמשך שיצוג מחרוזת קבועה נעשה תמיד ע"י גרשיים כפולים).

11 סימון סוף המחרוזת: התו המיוחד '0\' בכל הפקודות והפעולות הקיימות לגבי מחרוזות יש מוסכמה, שלפיה מייד אחרי תווי המחרוזת מופיע במערך התו ‘\0’ (זהו התו במקום 0 בטבלת האסקי(. בכל הפקודות והפעולות הקיימות לגבי מחרוזות יש מוסכמה, שלפיה מייד אחרי תווי המחרוזת מופיע במערך התו ‘\0’ (זהו התו במקום 0 בטבלת האסקי(. כפי שניראה בהמשך, זה מאפשר לעשות פעולות על מחרוזת בלי לדעת מראש את אורכה (ברגע שמגיעים לתו הזה יודעים שהמחרוזת הסתיימה). כפי שניראה בהמשך, זה מאפשר לעשות פעולות על מחרוזת בלי לדעת מראש את אורכה (ברגע שמגיעים לתו הזה יודעים שהמחרוזת הסתיימה). A[0]A[9].‘H’...‘E’‘L’ ‘O’‘\0’

12 סימון סוף המחרוזת: התו המיוחד '0\' כל הפעולות שנכיר על מחרוזות דואגות בעצמן לשמירת המוסכמה הזו של תו הסיום. כל הפעולות שנכיר על מחרוזות דואגות בעצמן לשמירת המוסכמה הזו של תו הסיום. למשל פעולת האיתחול הזו: למשל פעולת האיתחול הזו: char word[ ]=“HELLO”; char word[ ]=“HELLO”; שקולה לפעולת האיתחול הזו: שקולה לפעולת האיתחול הזו: char word[ ]={‘H’,’E’,’L’,’L’,’O’,’\0’}; char word[ ]={‘H’,’E’,’L’,’L’,’O’,’\0’}; (כלומר מחרוזת באורך 5 דורשת מערך של 6 תווים). word[0] ‘H’‘E’‘L’ ‘O’‘\0’ word[5]

13 הוספת תו-הסיום '0\' אם ניצור מערך של תווים בלי תו-הסיום, ונירצה להפעיל עליו פעולות על מחרוזות (שנכיר בהמשך), אז נצטרך להוסיף את התו '0\' אחרי התווים ששמנו בו. אם ניצור מערך של תווים בלי תו-הסיום, ונירצה להפעיל עליו פעולות על מחרוזות (שנכיר בהמשך), אז נצטרך להוסיף את התו '0\' אחרי התווים ששמנו בו. למשל אם נירצה להשתמש בפעולות על מחרוזות עבור המערך: למשל אם נירצה להשתמש בפעולות על מחרוזות עבור המערך: אז נצטרך להוסיף את התו '0\' אחרי האות 'O' על-ידי : אז נצטרך להוסיף את התו '0\' אחרי האות 'O' על-ידי : word[5]=‘\0’; word[5]=‘\0’; word[0] ‘H’‘E’‘L’ ‘O’ word[6] word[0] ‘H’‘E’‘L’ ‘O’ word[6] ‘\0’

14 דוגמא: מציאת אורך מחרוזת הפונקציה הבאה מקבלת מחרוזת ומחזירה את האורך שלה, בהסתמך על כך שיש תו '0\' בסוף המחרוזת. הפונקציה הבאה מקבלת מחרוזת ומחזירה את האורך שלה, בהסתמך על כך שיש תו '0\' בסוף המחרוזת. int my_strlen(char str[ ]) { int i=0; int i=0; while (str[ i ] != ‘\0’) while (str[ i ] != ‘\0’) i++; i++; return i; return i;}

15 קלט/פלט של מחרוזות אפשר כמובן לקלוט ולהדפיס את תווי המחרוזת בזה-אחר-זה, בעזרת scanf, printf של תווים בודדים (או בעזרת getchar,putchar). אפשר כמובן לקלוט ולהדפיס את תווי המחרוזת בזה-אחר-זה, בעזרת scanf, printf של תווים בודדים (או בעזרת getchar,putchar). לדוגמא, הקטע הבא קולט מחרוזת באורך 200 תווים לכל היותר, ומפסיק אם נקלטת ירידת-שורה: לדוגמא, הקטע הבא קולט מחרוזת באורך 200 תווים לכל היותר, ומפסיק אם נקלטת ירידת-שורה: char sentence[201], tav; int i=0; do { scanf(“%c”, &tav); scanf(“%c”, &tav); if (tav != ‘\n’) { if (tav != ‘\n’) { sentence[ i ] = tav; sentence[ i ] = tav; i++; i++; } } while ( (tav != ‘\n’) && (i<200) ) ; sentence[ i ]=‘\0’; אם זו לא ירידת-שורה אז מוסיפים אותו למקום הבא במחרוזת התנאי להמשך הקלט סימון סיום המחרוזת קולטים תו (צריך מקום ל- 200 תווים ולתו '0\')

16 קלט/פלט של מחרוזות אפשר כמובן לקלוט ולהדפיס את תווי המחרוזת בזה-אחר-זה, בעזרת scanf, printf של תווים בודדים (או בעזרת getchar,putchar). אפשר כמובן לקלוט ולהדפיס את תווי המחרוזת בזה-אחר-זה, בעזרת scanf, printf של תווים בודדים (או בעזרת getchar,putchar). לדוגמא, הקטע הבא קולט מחרוזת באורך 200 תווים לכל היותר, ומפסיק אם נקלטת ירידת-שורה: לדוגמא, הקטע הבא קולט מחרוזת באורך 200 תווים לכל היותר, ומפסיק אם נקלטת ירידת-שורה: char sentence[201], tav; int i=0; do { tav=getchar(); tav=getchar(); if (tav != ‘\n’) { if (tav != ‘\n’) { sentence[ i ] = tav; sentence[ i ] = tav; i++; i++; } } while ( (tav != ‘\n’) && (i<200) ) ; sentence[ i ]=‘\0’; אם זו לא ירידת-שורה אז מוסיפים אותו למקום הבא במחרוזת התנאי להמשך הקלט סימון סיום המחרוזת פקודת-קלט שמשמעותה זהה (צריך מקום ל- 200 תווים ולתו '0\')

17 קלט/פלט של מחרוזת שלמה אפשר גם לקלוט ולהדפיס מחרוזות בבת-אחת ב- scanf אפשר גם לקלוט ולהדפיס מחרוזות בבת-אחת ב- scanf וב- printf, באמצעות הקידוד %s. וב- printf, באמצעות הקידוד %s. למשל: למשל: char answer[100]; char answer[100]; scanf(“%s”, answer); scanf(“%s”, answer); הקלט יהיה רק עד רווח או ירידת-שורה (כלומר זו דרך לקלוט מילים). הקלט יהיה רק עד רווח או ירידת-שורה (כלומר זו דרך לקלוט מילים). התו ‘\0’ מוכנס אוטומטית ע"י ה- scanf אחרי תווי המחרוזת שנקלטה. התו ‘\0’ מוכנס אוטומטית ע"י ה- scanf אחרי תווי המחרוזת שנקלטה. שימו לב שלא רושמים את הסימן & ב- scanf של מחרוזת שלמה. שימו לב שלא רושמים את הסימן & ב- scanf של מחרוזת שלמה.

18 קלט של מחרוזת שלמה – מס' התווים אם תוכנס כקלט מחרוזת ארוכה יותר מהמקום שהגדרנו למערך, אז התוכנית צפויה "לעוף", כי תהיה גישה למערך מעבר לגבולות שהוגדרו לו (בהקשר הזה זיכרו גם שצריך להשאיר מקום אחד לתו הסיום). אם תוכנס כקלט מחרוזת ארוכה יותר מהמקום שהגדרנו למערך, אז התוכנית צפויה "לעוף", כי תהיה גישה למערך מעבר לגבולות שהוגדרו לו (בהקשר הזה זיכרו גם שצריך להשאיר מקום אחד לתו הסיום). אם רוצים להגביל את מספר התווים שיקלטו אז אפשר לכתוב את זה ב- scanf. למשל: אם רוצים להגביל את מספר התווים שיקלטו אז אפשר לכתוב את זה ב- scanf. למשל: scanf(“%99s”, answer); דרך אחרת להגבלת מספר התווים היא לעשות לולאה ולקלוט תו- תו כמו שראינו קודם (אז הקלט לא נפסק כשמגיעים לרווח). דרך אחרת להגבלת מספר התווים היא לעשות לולאה ולקלוט תו- תו כמו שראינו קודם (אז הקלט לא נפסק כשמגיעים לרווח).

19 פלט של מחרוזת שלמה printf(“%s”, answer) מדפיס את תווי המחרוזת answer עד לתו '0\'. printf(“%s”, answer) מדפיס את תווי המחרוזת answer עד לתו '0\'.HELLO אם בטעות אין תו '0\' בסוף המחרוזת, אז printf ידפיס גם את מה שיש אחרי המחרוזת בזיכרון, עד שיגיע במקרה לתו ‘\0’ (כלומר יודפס אחרי המחרוזת רצף סתמי ארוך של תווים). למשל: אם בטעות אין תו '0\' בסוף המחרוזת, אז printf ידפיס גם את מה שיש אחרי המחרוזת בזיכרון, עד שיגיע במקרה לתו ‘\0’ (כלומר יודפס אחרי המחרוזת רצף סתמי ארוך של תווים). למשל:HELLO3894o9fhsdughw34or/uvhjnxu)ioghyw89tpcvj

20 עוד אפשרות לקלט/פלט של מחרוזות יש פקודת קלט נוספת למחרוזות: gets(str); שקוראת גם רווחים. יש פקודת קלט נוספת למחרוזות: gets(str); שקוראת גם רווחים. אבל בפקודה הזאת לא ניתן להגביל את אורך הקלט, אז אם המשתמש יכניס יותר מידי תווים התוכנית "תעוף". אבל בפקודה הזאת לא ניתן להגביל את אורך הקלט, אז אם המשתמש יכניס יותר מידי תווים התוכנית "תעוף". יש גם פקודת פלט נוספת: puts(str); שפועלת כמו printf(“%s\n”,str) (מדפיסה ירידת שורה אחרי המחרוזת). יש גם פקודת פלט נוספת: puts(str); שפועלת כמו printf(“%s\n”,str) (מדפיסה ירידת שורה אחרי המחרוזת).

21 פעולות על מחרוזות לא נוכל לבצע פעולות על מחרוזת שלמה כמו השוואה או השמה באופן רגיל: לא נוכל לבצע פעולות על מחרוזת שלמה כמו השוואה או השמה באופן רגיל: string1==string2 string1==string2 string1=string2 string1=string2 זה מכיוון שכמו בכל מערך שם המחרוזת מכיל את כתובתה בזיכרון, ולכן גם הפעולות האלה יבוצעו על הכתובות ולא על תוכן המחרוזת. זה מכיוון שכמו בכל מערך שם המחרוזת מכיל את כתובתה בזיכרון, ולכן גם הפעולות האלה יבוצעו על הכתובות ולא על תוכן המחרוזת. לביצוע פעולות על מחרוזות שלמות נוכל להשתמש בספריה string.h, שכוללת פעולות כמו השוואת מחרוזות, העתקת מחרוזות, הוספת מחרוזת למחרוזת אחרת ("שירשור"), ועוד. לביצוע פעולות על מחרוזות שלמות נוכל להשתמש בספריה string.h, שכוללת פעולות כמו השוואת מחרוזות, העתקת מחרוזות, הוספת מחרוזת למחרוזת אחרת ("שירשור"), ועוד.

22 הספריה string.h – פונקציות לדוגמא פונקציה למציאת אורך מחרוזת (לפי מקום התו '0\'): פונקציה למציאת אורך מחרוזת (לפי מקום התו '0\'): int strlen(const char str[ ]); int strlen(const char str[ ]); האורך לא כולל את התו '0\'. האורך לא כולל את התו '0\'. - השימוש ניראה כך: len = strlen(my_string); (למשל עבור מחרוזת שהוגדרה char my_string[6]=“Hello”; יוחזר 5) - השימוש ניראה כך: len = strlen(my_string); (למשל עבור מחרוזת שהוגדרה char my_string[6]=“Hello”; יוחזר 5) פונקציה להשוואה בין מחרוזות: פונקציה להשוואה בין מחרוזות: int strcmp(const char str1[ ], const char str2[ ]); int strcmp(const char str1[ ], const char str2[ ]); מחזירה 0 אם המחרוזות זהות, כלומר שוות בכל תו עד לתו ‘\0’. מחזירה 0 אם המחרוזות זהות, כלומר שוות בכל תו עד לתו ‘\0’. אחרת מוחזר מס' שונה מ-0: חיובי אם המחרוזת הראשונה גדולה יותר לקסיקוגרפית (כלומר לפי סדר מילוני, סדר טבלת-האסקי), ושלילי אם היא קטנה יותר. אחרת מוחזר מס' שונה מ-0: חיובי אם המחרוזת הראשונה גדולה יותר לקסיקוגרפית (כלומר לפי סדר מילוני, סדר טבלת-האסקי), ושלילי אם היא קטנה יותר.

23 דוגמא: מה עושה התוכנית הבאה? #include<stdio.h>#include<string.h> int main() { int o_votes = 0, m_votes=0; int o_votes = 0, m_votes=0; char vote[7]; char vote[7]; while(1) while(1) { scanf(“%6s”, vote); scanf(“%6s”, vote); if (strcmp(vote, “Obama”)==0) o_votes++; if (strcmp(vote, “Obama”)==0) o_votes++; else if (strcmp(vote, “McCain”)==0) m_votes++; else if (strcmp(vote, “McCain”)==0) m_votes++; else if (strcmp(vote, “Stop”)==0) break; else if (strcmp(vote, “Stop”)==0) break; else printf(“Wrong vote!\n”) else printf(“Wrong vote!\n”) } printf(“Obama received %d votes and McCain received %d printf(“Obama received %d votes and McCain received %d votes\n”, o_votes, m_votes); votes\n”, o_votes, m_votes);} סופרת הצבעות לאובאמה ולמקיין, עד שאומרים לה לעצור משתנים לספירת הקולות breakהלולאה נמשכת עד שנצא ע"י קליטת מחרוזת באורך 6 לכל היותר בודקים איזה מחרוזת נקלטה

24 - דוגמאות נוספותstring.h - דוגמאות נוספותstring.h העתקת המחרוזת שבמשתנה source למחרוזת target תתבצע על-ידי: העתקת המחרוזת שבמשתנה source למחרוזת target תתבצע על-ידי: strcpy(target, source); strcpy(target, source); שירשור מחרוזות (מעתיק את str2 לסוף str1): שירשור מחרוזות (מעתיק את str2 לסוף str1): strcat(str1, str2); strcat(str1, str2); חיפוש ההופעה הראשונה של התו במשתנה c מסוג char במחרוזת str: חיפוש ההופעה הראשונה של התו במשתנה c מסוג char במחרוזת str: strchr(str, c); strchr(str, c); חיפוש ההופעה הראשונה של מחרוזת str2 במחרוזת str1: חיפוש ההופעה הראשונה של מחרוזת str2 במחרוזת str1: strstr(str1, str2); strstr(str1, str2); בעתיד נפרט יותר על השימוש בפעולות האלה ועל פעולות נוספות בעתיד נפרט יותר על השימוש בפעולות האלה ועל פעולות נוספות בספריה הזאת. בספריה הזאת.

25 נקודה לתשומת-לב: הגרשיים תו קבוע מסויים רושמים בתוך גרש בודדת. למשל: ‘a’ או ‘?’ תו קבוע מסויים רושמים בתוך גרש בודדת. למשל: ‘a’ או ‘?’ מחרוזת קבועה מסויימת רושמים בתוך גרשיים כפולים. למשל: “HELLO” מחרוזת קבועה מסויימת רושמים בתוך גרשיים כפולים. למשל: “HELLO” אם רושמים “a” אז המשמעות היא מחרוזת שיש בה את התו ‘a’ ואחריו את התו ‘\0’. כלומר זה שונה מאשר לרשום ‘a’. אם רושמים “a” אז המשמעות היא מחרוזת שיש בה את התו ‘a’ ואחריו את התו ‘\0’. כלומר זה שונה מאשר לרשום ‘a’. אז כשעושים פעולות על מחרוזות צריך להקפיד על גרשיים כפולים, למשל: אז כשעושים פעולות על מחרוזות צריך להקפיד על גרשיים כפולים, למשל: strcmp(input,”a”) strcmp(input,”a”) ולא: ולא: strcmp(input,’a’) strcmp(input,’a’) ‘a’'0\'

26 מחרוזות – נקודה לתשומת-לב כשמכניסים למחרוזת ערך ע"י פעולה על מחרוזת שלמה, אז תו-הסיום '0\' מוכנס אוטומטית אחרי תווי המחרוזת (בלי שרושמים אותו במפורש). למשל: כשמכניסים למחרוזת ערך ע"י פעולה על מחרוזת שלמה, אז תו-הסיום '0\' מוכנס אוטומטית אחרי תווי המחרוזת (בלי שרושמים אותו במפורש). למשל: char str[ ]=“Hello”; scanf(“%s”, str); scanf(“%40s”, str); gets(str); אם מכניסים למחרוזת תו-תו, לא ע"י פעולה על מחרוזת שלמה, אז צריך להוסיף במפורש את תו-הסיום כדי לעשות לאחר-מכן פעולות על המחרוזת בשלמותה. למשל: אם מכניסים למחרוזת תו-תו, לא ע"י פעולה על מחרוזת שלמה, אז צריך להוסיף במפורש את תו-הסיום כדי לעשות לאחר-מכן פעולות על המחרוזת בשלמותה. למשל: char str[ ]={‘H’,’e’,’l’,’l’,’o’,’\0’};

27 מחרוזות – עוד נקודה לתשומת-לב זיכרו שה- Enter שמוכנס בסיום הקלט נשאר בזיכרון-הקלט של המחשב. כלומר אם נקלוט תו אחרי שקלטנו מחרוזת אז הוא יכיל את ‘\n’ (ולא נתבקש לרשום תו נוסף): זיכרו שה- Enter שמוכנס בסיום הקלט נשאר בזיכרון-הקלט של המחשב. כלומר אם נקלוט תו אחרי שקלטנו מחרוזת אז הוא יכיל את ‘\n’ (ולא נתבקש לרשום תו נוסף): scanf(“%s”, str); abcd scanf(“%c”, &tav); כמו שראינו בעבר, ניתן להתגבר על כך ע"י זה שנגיד לפקודת-הקלט הבאה לדלג על ‘\n’: כמו שראינו בעבר, ניתן להתגבר על כך ע"י זה שנגיד לפקודת-הקלט הבאה לדלג על ‘\n’: scanf(“%s”, str); abcd scanf(“\n%c”, &tav); e

28 נושאי השיעור היום: כתובות ומצביעים מהן כתובות בזיכרון מהן כתובות בזיכרון פעולות עם כתובות פעולות עם כתובות מהם מצביעים ומה אפשר לעשות איתם מהם מצביעים ומה אפשר לעשות איתם

29 כתובות בזיכרון אמרנו בעבר שהזיכרון של המחשב מורכב מתאים (בתים) רבים. במחשבים הנוכחיים מדובר במיליונים ואפילו מיליארדים. אמרנו בעבר שהזיכרון של המחשב מורכב מתאים (בתים) רבים. במחשבים הנוכחיים מדובר במיליונים ואפילו מיליארדים. לכל תא בזיכרון יש כתובת (מספר תא). לכל תא בזיכרון יש כתובת (מספר תא). כל משתנה נשמר בזיכרון החל מכתובת מסויימת (הוא יכול לתפוס יותר מתא אחד, בהתאם לטיפוס המשתנה). כל משתנה נשמר בזיכרון החל מכתובת מסויימת (הוא יכול לתפוס יותר מתא אחד, בהתאם לטיפוס המשתנה). למשל, כשמגדירים משתנה על-ידי: int i; למשל, כשמגדירים משתנה על-ידי: int i; המחשב מקצה עבור המשתנה i מקום בזיכרון בכתובת שהוא בוחר. הוא שומר טבלה של כתובות המשתנים. המחשב מקצה עבור המשתנה i מקום בזיכרון בכתובת שהוא בוחר. הוא שומר טבלה של כתובות המשתנים.

30 כתובות בזיכרון - דוגמא int main() { int i; int i;} התוכניתהזיכרון ? 7500 טבלת הכתובות i 7500 משתנהכתובת כתובת כלשהי שהמחשב בחר

31 כתובות בזיכרון - דוגמא int main() { int i; int i; i=10; i=10;} התוכניתהזיכרון 10 7500 טבלת הכתובות i 7500 משתנהכתובת ערך המשתנה מתעדכן בזיכרון

32 כתובות בזיכרון - דוגמא int main() { int i=10; int i=10;} התוכניתהזיכרון 10 7500 טבלת הכתובות i 7500 משתנהכתובת אותו דבר אם הערך ניתן באיתחול

33 כתובות בזיכרון - דוגמא int main() { int i=10; int i=10; char c=‘A’; char c=‘A’;} התוכניתהזיכרון 10 7500 טבלת הכתובות i 7500 c 9200 משתנהכתובת ‘A’ 9200 כך גם עבור משתנים מסוגים אחרים

34 כתובות בזיכרון - דוגמא int main() { int i=10; int i=10; char c=‘A’; char c=‘A’;} התוכניתהזיכרון 10 7500 טבלת הכתובות i 7500 c 9200 משתנהכתובת 65 9200 כזכור, למעשה נשמר ערך האסקי של התו

35 כתובות בזיכרון – גודל המשתנה בטבלת הכתובות נשמר נתון נוסף, שהוא מספר התאים (בתים) שמשמשים ליצוג המשתנה הזה. בטבלת הכתובות נשמר נתון נוסף, שהוא מספר התאים (בתים) שמשמשים ליצוג המשתנה הזה. למשל, char מיוצג על-ידי בית אחד, int על-ידי ארבעה, ועבור double שמונה (ברוב הקומפיילרים). למשל, char מיוצג על-ידי בית אחד, int על-ידי ארבעה, ועבור double שמונה (ברוב הקומפיילרים). כשאנחנו מתייחסים בתוכנית לשם של משתנה, המחשב בודק בטבלה איפה המשתנה הזה נמצא בזיכרון וכמה מקום הוא תופס, וכך הוא יודע להביא לנו את הערך שלו. כשאנחנו מתייחסים בתוכנית לשם של משתנה, המחשב בודק בטבלה איפה המשתנה הזה נמצא בזיכרון וכמה מקום הוא תופס, וכך הוא יודע להביא לנו את הערך שלו.

36 כתובות בזיכרון - דוגמא int main() { int i=10; int i=10; char c=‘A’; char c=‘A’;} התוכניתהזיכרון 0 7500 טבלת הכתובות i 7500 4 c 92001 משתנהכתובת 65 9200 גודל 00 10101010 המחשב מקצה 4 תאים (בתים). intעבור המחשב מקצה 4 תאים (בתים). intעבור אם המספר קטן אז הספרות הראשונות יהיו מאופסות.

37 כתובות בזיכרון כל משתנה נשמר בכתובת מסויימת בזיכרון כל משתנה נשמר בכתובת מסויימת בזיכרון יש טבלה של כתובות המשתנים וגדליהם יש טבלה של כתובות המשתנים וגדליהם שאלות? שאלות?

38 כתובות – איך מתייחסים אליהן שפת C מאפשרת לנו לדעת מהי הכתובת בזיכרון שבה נשמר משתנה מסויים. שפת C מאפשרת לנו לדעת מהי הכתובת בזיכרון שבה נשמר משתנה מסויים. הפעולה & נותנת את הכתובת של המשתנה. הפעולה & נותנת את הכתובת של המשתנה. למשל &i היא הכתובת בזיכרון של המשתנה i. למשל &i היא הכתובת בזיכרון של המשתנה i. בדוגמא הקודמת &i נותן 7500. בדוגמא הקודמת &i נותן 7500.

39 כתובות בזיכרון - דוגמא int main() { int i=10; int i=10;} התוכניתהזיכרון 7500 טבלת הכתובות i 7500 4 משתנהכתובתגודל 10101010 &i זה 7500

40 כתובות – איך ניגשים אליהן אמרנו שהפעולה & נותנת את הכתובת של משתנה. אמרנו שהפעולה & נותנת את הכתובת של משתנה. למשל &i היא הכתובת בזיכרון של המשתנה i. למשל &i היא הכתובת בזיכרון של המשתנה i. בדוגמא הקודמת &i היה נותן 7500. בדוגמא הקודמת &i היה נותן 7500. הפעולה * ניגשת לערך שנמצא בכתובת מסויימת. הפעולה * ניגשת לערך שנמצא בכתובת מסויימת. למשל *(4000) זה הערך שנמצא בכתובת 0040. למשל *(4000) זה הערך שנמצא בכתובת 0040. *(&c) זה הערך של המשתנה c (כי זה הערך שנמצא בכתובת של(c. *(&c) זה הערך של המשתנה c (כי זה הערך שנמצא בכתובת של(c.

41 כתובות בזיכרון - דוגמא int main() { char c=‘A’; char c=‘A’;} התוכניתהזיכרון 65 9200 טבלת הכתובות c 9200 משתנהכתובת *(9200) זה 65

42 כתובות – מה אפשר לעשות איתן לא ניתן לשנות כתובת של משתנה. לא ניתן לשנות כתובת של משתנה. כלומר לא ניתן לכתוב למשל: &i=5000. כלומר לא ניתן לכתוב למשל: &i=5000. ניתן לשנות את הערך שנמצא בכתובת מסויימת. ניתן לשנות את הערך שנמצא בכתובת מסויימת. למשל: *(&c)=‘A’;. למשל: *(&c)=‘A’;. זה כמו לכתוב c=‘A’;. זה כמו לכתוב c=‘A’;. מסוכן לשנות את הערך שנמצא בכתובת כלשהי שאיננה כתובת של משתנה. למשל *(4000)=123;. מסוכן לשנות את הערך שנמצא בכתובת כלשהי שאיננה כתובת של משתנה. למשל *(4000)=123;. אנחנו עלולים לשנות מקום בזיכרון ששייך למערכת ההפעלה, ולגרום לתוכנית לעוף. אנחנו עלולים לשנות מקום בזיכרון ששייך למערכת ההפעלה, ולגרום לתוכנית לעוף. לכן נשנה רק ערכים של משתנים שהגדרנו. לכן נשנה רק ערכים של משתנים שהגדרנו.

43 בשביל מה כל זה טוב? הרי כדי לשנות ערך של משתנה אפשר פשוט לשים בו ערך. למשל i=10;. אז בשביל מה נירצה לדעת מה הכתובת שלו? הרי כדי לשנות ערך של משתנה אפשר פשוט לשים בו ערך. למשל i=10;. אז בשביל מה נירצה לדעת מה הכתובת שלו? למה שנירצה לשים ערך בכתובת מסויימת ולא להשתמש פשוט בשם המשתנה? למה שנירצה לשים ערך בכתובת מסויימת ולא להשתמש פשוט בשם המשתנה?

44 בשביל מה נשתמש בכתובות אמרנו שכל משתנה מוכר רק בתוך הפונקציה שהוא הוגדר בה. אמרנו שכל משתנה מוכר רק בתוך הפונקציה שהוא הוגדר בה. נשתמש בכתובות כדי לשנות ערך של משתנה מתוך פונקציה אחרת, שהוא לא הוגדר בה. נשתמש בכתובות כדי לשנות ערך של משתנה מתוך פונקציה אחרת, שהוא לא הוגדר בה. בפרט, אם רוצים שפונקציה תשנה כמה משתנים (ולא רק תחזיר ערך אחד), אז אפשר להעביר אליה את הכתובות שלהם. זה מאפשר לה לשנות באופן ישיר את ערכיהם בזיכרון. בפרט, אם רוצים שפונקציה תשנה כמה משתנים (ולא רק תחזיר ערך אחד), אז אפשר להעביר אליה את הכתובות שלהם. זה מאפשר לה לשנות באופן ישיר את ערכיהם בזיכרון.

45 דוגמא שהכרנו השתמשנו בהעברת כתובת בפונקציה scanf, שמקבלת את כתובת המשתנה שאליו יוכנס הקלט. למשל: השתמשנו בהעברת כתובת בפונקציה scanf, שמקבלת את כתובת המשתנה שאליו יוכנס הקלט. למשל: int grade; scanf(“%d”, &grade); המשתנה grade לא מוגדר בפונקציה scanf והיא גם לא מחזירה לתוכו ערך. המשתנה grade לא מוגדר בפונקציה scanf והיא גם לא מחזירה לתוכו ערך. הפונקציה scanf מקבלת את הכתובת של המשתנה בזיכרון, וזה מאפשר לה לכתוב לתוכו את הקלט (בעזרת הפעולה * שהכרנו קודם). הפונקציה scanf מקבלת את הכתובת של המשתנה בזיכרון, וזה מאפשר לה לכתוב לתוכו את הקלט (בעזרת הפעולה * שהכרנו קודם). קידוד הקלט (למשל %d) אומר ל- scanf גם מה גודל המשתנה הזה בזיכרון, כלומר לכמה תאים הקלט אמור להיכנס. קידוד הקלט (למשל %d) אומר ל- scanf גם מה גודל המשתנה הזה בזיכרון, כלומר לכמה תאים הקלט אמור להיכנס.

46 כתובות בזיכרון - פעולות מציאת הכתובת של משתנה עם הפעולה & מציאת הכתובת של משתנה עם הפעולה & גישה לערך של משתנה לפי כתובתו עם הפעולה * גישה לערך של משתנה לפי כתובתו עם הפעולה * זה יאפשר לנו לשנות יותר ממשתנה אחד בפונקציה זה יאפשר לנו לשנות יותר ממשתנה אחד בפונקציה שאלות? שאלות?

47 עבודה עם כתובות - מצביעים יש ב-C טיפוסי-משתנים לשמירת כתובות בזיכרון. יש ב-C טיפוסי-משתנים לשמירת כתובות בזיכרון. הם נקראים "מצביעים" או "פויינטרים". הם נקראים "מצביעים" או "פויינטרים". אפשר להגדיר למשל: אפשר להגדיר למשל: char *my_pointer; char *my_pointer; int *pointer1; int *pointer1; באופן כללי, כדי להגדיר משתנה מסוג מצביע, רושמים את סוג המשתנה שעליו מצביעים, וכותבים * לפני שם המשתנה. באופן כללי, כדי להגדיר משתנה מסוג מצביע, רושמים את סוג המשתנה שעליו מצביעים, וכותבים * לפני שם המשתנה. עוד דוגמאות: double *my_pointer1; float *ptr; עוד דוגמאות: double *my_pointer1; float *ptr; char משתנה לשמירת כתובת של int משתנה לשמירת כתובת של

48 עבודה עם כתובות - מצביעים יש ב-C טיפוסי-משתנים לשמירת כתובות בזיכרון. יש ב-C טיפוסי-משתנים לשמירת כתובות בזיכרון. הם נקראים "מצביעים" או "פויינטרים". הם נקראים "מצביעים" או "פויינטרים". אפשר להגדיר למשל: אפשר להגדיר למשל: char *my_pointer; char *my_pointer; int *pointer1; int *pointer1; באופן כללי, כדי להגדיר משתנה מסוג מצביע, רושמים את סוג המשתנה שעליו מצביעים, וכותבים * לפני שם המשתנה. באופן כללי, כדי להגדיר משתנה מסוג מצביע, רושמים את סוג המשתנה שעליו מצביעים, וכותבים * לפני שם המשתנה. שימו לב שזה שימוש אחר בכוכבית (להגדרת משתנה מטיפוס מצביע), נוסף לשימוש שראינו קודם (לגישה לערך בכתובת). שימו לב שזה שימוש אחר בכוכבית (להגדרת משתנה מטיפוס מצביע), נוסף לשימוש שראינו קודם (לגישה לערך בכתובת). char משתנה לשמירת כתובת של int משתנה לשמירת כתובת של

49 מצביעים – משמעות מצביע מכיל למעשה הפניה ("חץ") למשתנה אחר. לדוגמא: מצביע מכיל למעשה הפניה ("חץ") למשתנה אחר. לדוגמא: int i=10; int i=10; int *my_pointer; int *my_pointer; my_pointer = &i; my_pointer = &i; 10 i my_pointer כתובת 300 (לדוגמא) 300 int הגדרת מצביע לשמירת כתובת של iמוכנסת אליו הכתובת בזיכרון של המשתנה

50 גישה למשתנה על-ידי מצביע כאמור, כדי להתייחס לערך שנמצא בכתובת מסויימת, משתמשים בסימן *. ניראה קטע תוכנית שמדגים את זה: כאמור, כדי להתייחס לערך שנמצא בכתובת מסויימת, משתמשים בסימן *. ניראה קטע תוכנית שמדגים את זה: int i=10; int i=10; int *my_pointer; int *my_pointer; my_pointer = &i; my_pointer = &i; *my_pointer = 100; *my_pointer = 100; printf(“The value of i is now %d”, i); printf(“The value of i is now %d”, i); שמים 100 במקום שהפויינטר מצביע עליו יודפס המספר 100 intהגדרת מצביע על iשמים במצביע את כתובת המשתנה 100 i my_pointer_to_int

51 למה יש מצביע שונה לכל טיפוס? סוג המצביע אומר מה גודל המשתנה שמצביעים עליו, ואיך רוצים להתייחס לתוכן הכתובת הזאת (למשל שלם או ממשי). סוג המצביע אומר מה גודל המשתנה שמצביעים עליו, ואיך רוצים להתייחס לתוכן הכתובת הזאת (למשל שלם או ממשי). אז כשמתייחסים לערך שעליו מצביעים, המחשב יודע בכמה בתים הוא מיוצג ואיך, ומביא/מאחסן את הערך הנכון. אז כשמתייחסים לערך שעליו מצביעים, המחשב יודע בכמה בתים הוא מיוצג ואיך, ומביא/מאחסן את הערך הנכון. ניראה בעתיד איך זה מאפשר גם לעשות "חישובי-כתובות". ניראה בעתיד איך זה מאפשר גם לעשות "חישובי-כתובות". אם נירצה לגשת ל- int שנמצא בזיכרון מייד אחרי i, אז נוכל להוסיף 1 למצביע על i, והמחשב ידע בכמה בתים להתקדם בהתאם לגודל של int אם נירצה לגשת ל- int שנמצא בזיכרון מייד אחרי i, אז נוכל להוסיף 1 למצביע על i, והמחשב ידע בכמה בתים להתקדם בהתאם לגודל של int

52 מצביעים וכתובות – נקודות לתשומת-לב נשים לב שלקבועים וביטויים אין כתובת. למשל, לא נכתוב: נשים לב שלקבועים וביטויים אין כתובת. למשל, לא נכתוב:&5&(i*2) רק למשתנים יש כתובת. רק למשתנים יש כתובת. כיוון שלא ניתן לשנות כתובת של משתנה (המחשב קובע אותה), לא ניתן לבצע עליה השמה: &a=&b. כיוון שלא ניתן לשנות כתובת של משתנה (המחשב קובע אותה), לא ניתן לבצע עליה השמה: &a=&b.

53 מצביעים – דוגמא נתאר פונקציה שמקבלת כתובות של שני משתנים מסוג int, ומחליפה בין הערכים שלהם. נתאר פונקציה שמקבלת כתובות של שני משתנים מסוג int, ומחליפה בין הערכים שלהם. (עד עכשיו לא יכולנו לכתוב פונקציה להחלפת ערכים של שני משתנים מסוג int, כי שינוי משתנים מסוג int בפונקציה לא משפיע על ערכם המקורי, ופונקציה יכולה להחזיר רק ערך אחד). (עד עכשיו לא יכולנו לכתוב פונקציה להחלפת ערכים של שני משתנים מסוג int, כי שינוי משתנים מסוג int בפונקציה לא משפיע על ערכם המקורי, ופונקציה יכולה להחזיר רק ערך אחד). void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} השימוש למשל על-ידי: swap(&i, &j); first ערך המשתנה שכתובתו הועברה אל יוחלף עם ערך המשתנה שכתובתו הועברה אל second

54 דוגמא: פונקציה להחלפה בין ערכי משתנים void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);}

55 דוגמא: פונקציה להחלפה בין ערכי משתנים void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);}

56 דוגמא: פונקציה להחלפה בין ערכי משתנים void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);} 10 20 i j

57 void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);} 10 first 20 second i j דוגמא: פונקציה להחלפה בין ערכי משתנים

58 void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);} 10 first temp 20 second i j דוגמא: פונקציה להחלפה בין ערכי משתנים

59 void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);} 10 first temp 10 20 second i j דוגמא: פונקציה להחלפה בין ערכי משתנים

60 void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);} 20 first temp 10 20 second i j דוגמא: פונקציה להחלפה בין ערכי משתנים

61 void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);} 20 first temp 10 second i j דוגמא: פונקציה להחלפה בין ערכי משתנים

62 void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);} 20 10 i j דוגמא: פונקציה להחלפה בין ערכי משתנים

63 void swap(int *first, int *second) { int temp; int temp; temp=*first; temp=*first; *first=*second; *first=*second; *second=temp; *second=temp;} int main() { int i=10, j=20; int i=10, j=20; swap(&i,&j); swap(&i,&j);} 20 10 i j בלי מצביעים לא יכולנו לשנות יותר ממשתנה אחד על- ידי פונקציה דוגמא: פונקציה להחלפה בין ערכי משתנים

64 עוד על פונקציות ומצביעים פונקציה יכולה להחזיר מצביע (כלומר כתובת של משתנה). פונקציה יכולה להחזיר מצביע (כלומר כתובת של משתנה). אבל לא נחזיר כתובת של משתנה שהוגדר בתוך הפונקציה, כי אחרי שהפונקציה מסתיימת המשתנה כבר לא נמצא שם (גישה לשם תגרום לתעופה). אבל לא נחזיר כתובת של משתנה שהוגדר בתוך הפונקציה, כי אחרי שהפונקציה מסתיימת המשתנה כבר לא נמצא שם (גישה לשם תגרום לתעופה).

65 עוד על פונקציות ומצביעים למשל: למשל: int *address_of _the_higher_number(int *a, int*b) { if(*a > *b) if(*a > *b) return a; return a; return b; return b;}

66 עוד על פונקציות ומצביעים למשל: למשל: int *address_of _the_higher_number(int *a, int*b) { if(*a > *b) if(*a > *b) return a; return a; return b; return b;} int main() { int *ptr, i=10, j=0; ptr = address_of _the_higher_number(&i, &j); ptr = address_of _the_higher_number(&i, &j); return 0; return 0;} והשימוש ניראה כך למשל: מוחזרת כתובת המשתנה שמכיל את הערך הגדול יותר מבין השניים מוחזרת כתובת המשתנה שמכיל את הערך הגדול יותר מבין השניים

67 עוד על פונקציות ומצביעים אמרנו שהערך שמוחזר מפונקציה יכול להיות מצביע: אמרנו שהערך שמוחזר מפונקציה יכול להיות מצביע: int *address_of _the_higher_number(int *a, int*b); אבל החזרת כתובת משתנה שהוגדר בתוך הפונקציה תגרום לשגיאה. כי המשתנים שמוגדרים בתוך הפונקציה נעלמים עם סיומה. למשל הפונקציה הבאה תגרום לשגיאה: אבל החזרת כתובת משתנה שהוגדר בתוך הפונקציה תגרום לשגיאה. כי המשתנים שמוגדרים בתוך הפונקציה נעלמים עם סיומה. למשל הפונקציה הבאה תגרום לשגיאה: int *give_pointer_to_zero() { int i=0; int i=0; return &i; return &i;} 0 i

68 מצביעים ומערכים הגדרת מערך מקצה בזיכרון מקומות רצופים עבור התאים שלו. בנוסף, מוקצה משתנה ששמו הוא שם-המערך, שמכיל את כתובת התא הראשון של המערך (לא ניתן לשנות את ערכו). בנוסף, מוקצה משתנה ששמו הוא שם-המערך, שמכיל את כתובת התא הראשון של המערך (לא ניתן לשנות את ערכו). לכן אפשר לגשת לתאי מערך גם ע"י חישוב הכתובת שלהם. למשל *(array+5) שקול ל- array[5]. לכן אפשר לגשת לתאי מערך גם ע"י חישוב הכתובת שלהם. למשל *(array+5) שקול ל- array[5]. המחשב יודע לאיזה כתובת לגשת כשעושים את החשבון הזה, כי הוא יודע כמה מקום תופס כל תא במערך לפי הטיפוס. המחשב יודע לאיזה כתובת לגשת כשעושים את החשבון הזה, כי הוא יודע כמה מקום תופס כל תא במערך לפי הטיפוס. 500 array array[0]array[9].......... כתובת 500 (למשל)

69 העברת מערך לפונקציה (האמת...) כאשר מועבר מערך לפונקציה, בעצם מועבר מצביע למערך כאשר מועבר מערך לפונקציה, בעצם מועבר מצביע למערך זו הסיבה שאין צורך לכתוב את גודל המערך בהגדרת הפונקציה. זו הסיבה שאין צורך לכתוב את גודל המערך בהגדרת הפונקציה. לדוגמא לדוגמא int calc_sum(int my_array, int size) שקול ל int calc_sum(int *my_array, int size)

70 סיכום דיברנו היום על: כתובות בזיכרון מצביעים מצביעים ופונקציות


Download ppt "מבוא למדעי המחשב מחרוזות, מצביעים וכתובות www.cs.tau.ac.il/courses/cs4math/09b."

Similar presentations


Ads by Google