אריתמטיקה של מצביעים
הערה: נושא זה יהיה מאוד שימושי כאשר נתחיל לדבר על מערכים בהרצאה הבאה.
כפי שראיונ, מצביעים מכילים כתובת כלשהי בזכרון. לדוגמא, נבחן את המקרה הבא:
נניח שיש לנו איזור בזכרון שמכיל מספר משתני int זה אחר זה, ומצביע ptr שמצביע לראשון מביניהם. כפי שמופיע בתמונה, המשתנה הראשון נמצא בכתובת 100. sizeof(int) הוא 4 בתים ולכן המשתנה הבא נמצא בכתובת 104, הבא אחריו ב-108 וכך הלאה.
הביטוי ptr* מבצע dereference * לכתובת שאליה המצביע מצביע - במקרה זה נקבל את הערך 176. איך נוכל להגיע למשתנים הבאים? השפה מעמידה לטובתנו קונספט הנקרא אריתמטיקת מצביעים, המאפשרת לבצע פעולות מתמטיות פשוטות על מצביעים ולקבל בהתאמה פעולות בזכרון. למשל:
int* ptr = 100;
ptr++;
*ptr == 12; // true!
ptr == 104; // also true!
להזכירכם, הפעולה ++ptr שקולה ל-ptr = ptr + 1. מה עשינו כאן בעצם? קידום של המצביע ב-1 קידמה את הכתובת אליו הוא מצביע ב-4 בתים, שהם במקרה גם sizeof(int).
הגדרה: עבור מצביע type* ptr, הגדלה של המצביע ב-1 שקולה לקידום הכתובת אליה המצביע מצביע ב-sizeof(type).
המשמעות היא שכאשר יש לנו מספר איברים באותו סוג זה אחר זה ומצביע לאחד מהם, ניתן להגדיל ולהקטין את המצביע במספר האיברים בו נרצה להתקדם. למשל, נניח שנרצה לכתוב לאיבר השני בדוגמא למעלה את הערך 1234:
int* ptr = 100;
ptr += 2;
*ptr = 1234;
לאריתמטיקת מצביעים יש מספר חוקים - נרחיב עליהם:
- ניתן לחבר מספר למצביע בשביל לקדם את הכתובת ולחסר מספר בשביל לבצע נסיגה לכתובת באותה מידה. ניתן כמובן להשתמש בקיצורים
++,=+והמקבילים החיסוריים שלהם. - אריתמטיקת מצביעים מתבצעת עם מצביע ומספר כלשהו, לא בין שני מצביעים (אפשר ברמת העקרון לחסר שני מצביעים ולקבל את מספר האיברים ביניהם, אבל לא רצוי לעשות זאת).
תרגילים ושאלות:
- עבור הדוגמא לעיל, נגדיר
char* cptr = (char*)ptr. כיצד תתנהג אריתמטיקת מצביעים במקרה זה? - עבור הדוגמא לעיל, קבעו מהו ערך הביטויים הבאים, וציינו האם מדובר בכתובת או ערך בזכרון:
- *ptr
- *(ptr + 4) - 2
- *(ptr + 5)
- *(ptr + 1) - *(ptr + 3)
- *(ptr + 4) / *(ptr + 1)
- ptr + 3