logo

C מצביע כפול (מצביע למצביע)

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

מצביע למצביע ב-c

התחביר של הכרזה על מצביע כפול ניתן להלן.

 int **p; // pointer to a pointer which is pointing to an integer. 

שקול את הדוגמה הבאה.

מה גודל מסך המסך שלי
 #include void main () { int a = 10; int *p; int **pp; p = &a; // pointer p is pointing to the address of a pp = &p; // pointer pp is a double pointer pointing to the address of pointer p printf('address of a: %x
',p); // Address of a will be printed printf('address of p: %x
',pp); // Address of p will be printed printf('value stored at p: %d
',*p); // value stoted at the address contained by p i.e. 10 will be printed printf('value stored at pp: %d
',**pp); // value stored at the address contained by the pointer stoyred at pp } 

תְפוּקָה

 address of a: d26a8734 address of p: d26a8738 value stored at p: 10 value stored at pp: 10 

C דוגמה כפולה

בואו נראה דוגמה שבה מצביע אחד מצביע לכתובת של מצביע אחר.

C מצביע למצביע דוגמה

כפי שניתן לראות באיור לעיל, p2 מכיל את הכתובת של p (fff2), ו-p מכיל את הכתובת של משתנה מספר (fff4).

 #include int main(){ int number=50; int *p;//pointer to int int **p2;//pointer to pointer p=&number;//stores the address of number variable p2=&p; printf('Address of number variable is %x 
',&number); printf('Address of p variable is %x 
',p); printf('Value of *p variable is %d 
',*p); printf('Address of p2 variable is %x 
',p2); printf('Value of **p2 variable is %d 
',*p); return 0; } 

תְפוּקָה

 Address of number variable is fff4 Address of p variable is fff4 Value of *p variable is 50 Address of p2 variable is fff2 Value of **p variable is 50 

ש. מה תהיה הפלט של התוכנית הבאה?

 #include void main () { int a[10] = {100, 206, 300, 409, 509, 601}; //Line 1 int *p[] = {a, a+1, a+2, a+3, a+4, a+5}; //Line 2 int **pp = p; //Line 3 pp++; // Line 4 printf('%d %d %d
',pp-p,*pp - a,**pp); // Line 5 *pp++; // Line 6 printf('%d %d %d
',pp-p,*pp - a,**pp); // Line 7 ++*pp; // Line 8 printf('%d %d %d
',pp-p,*pp - a,**pp); // Line 9 ++**pp; // Line 10 printf('%d %d %d
',pp-p,*pp - a,**pp); // Line 11 } 

הֶסבֵּר

שאלה כפולה

בשאלה לעיל, נעשה שימוש באריתמטיקה של המצביע עם המצביע הכפול. מוגדר מערך של 6 אלמנטים אשר מצביע על ידי מערך של מצביע p. מערך המצביע p מצביע על ידי מצביע כפול pp. עם זאת, התמונה שלמעלה נותנת לך מושג קצר על האופן שבו הזיכרון מוקצה למערך a ולמערך המצביע p. האלמנטים של p הם המצביעים המצביעים על כל רכיב במערך a. מכיוון שאנו יודעים ששם המערך מכיל את כתובת הבסיס של המערך ולכן, הוא יעבוד כמצביע וניתן לעבור את הערך באמצעות *(a), *(a+1) וכו'. כפי שמוצג בתמונה , ניתן לגשת אל a[0] בדרכים הבאות.

  • a[0]: זוהי הדרך הפשוטה ביותר לגשת לרכיב הראשון של המערך
  • *(א): מכיוון שמאחסן את הכתובת של האלמנט הראשון של המערך, נוכל לגשת לערך שלו באמצעות מצביע עקיף עליו.
  • *p[0]: אם יש לגשת ל-a[0] באמצעות מצביע p אליו, אז נוכל להשתמש באופרטור עקיפה (*) באלמנט הראשון של מערך המצביע p, כלומר, *p[0].
  • **(pp): מכיוון ש-pp מאחסן את כתובת הבסיס של מערך המצביע, *pp ייתן את הערך של האלמנט הראשון של מערך המצביע שהוא הכתובת של האלמנט הראשון של מערך המספרים השלמים. **p ייתן את הערך האמיתי של האלמנט הראשון של מערך המספרים השלמים.

כשמגיעים לתוכנית, שורה 1 ו-2 מכריזות על מערך המספרים השלמים והמצביע באופן יחסי. שורה 3 מאתחלת את המצביע הכפול למערך המצביע p. כפי שמוצג בתמונה, אם הכתובת של המערך מתחילה מ-200 וגודל המספר השלם הוא 2, אז מערך המצביע יכיל את הערכים כמו 200, 202, 204, 206, 208, 210. הבה ניקח בחשבון ש- כתובת הבסיס של מערך המצביעים היא 300; המצביע הכפול pp מכיל את הכתובת של מערך המצביע, כלומר 300. שורה מספר 4 מגדילה את הערך של pp ב-1, כלומר, pp יצביע כעת לכתובת 302.

java לעומת c++

שורה מספר 5 מכילה ביטוי שמדפיס שלושה ערכים, כלומר pp - p, *pp - a, **pp. בואו נחשב אותם כל אחד מהם.

  • pp = 302, p = 300 => pp-p = (302-300)/2 => pp-p = 1, כלומר, 1 יודפס.
  • pp = 302, *pp = 202, a = 200 => *pp - a = 202 - 200 = 2/2 = 1, כלומר, 1 יודפס.
  • pp = 302, *pp = 202, *(*pp) = 206, כלומר, 206 יודפסו.

לכן כתוצאה משורה 5, הפלט 1, 1, 206 יודפס על הקונסולה. בשורה 6 נכתב *pp++. כאן, עלינו לשים לב ששני אופרטורים אונריים * ו++ יהיו בעלי קדימות זהה. לכן, לפי כלל האסוציאטיביות, זה יוערך מימין לשמאל. לכן ניתן לכתוב מחדש את הביטוי *pp++ בתור (*(pp++)). מאז, pp = 302 אשר יהפוך כעת, 304. *pp ייתן 204.

בשורה 7, שוב נכתב הביטוי שמדפיס שלושה ערכים, כלומר, pp-p, *pp-a, *pp. בואו נחשב כל אחד מהם.

  • pp = 304, p = 300 => pp - p = (304 - 300)/2 => pp-p = 2, כלומר, 2 יודפסו.
  • pp = 304, *pp = 204, a = 200 => *pp-a = (204 - 200)/2 = 2, כלומר, 2 יודפסו.
  • pp = 304, *pp = 204, *(*pp) = 300, כלומר, 300 יודפסו.

לכן, כתוצאה משורה 7, הפלט 2, 2, 300 יודפס על הקונסולה. בשורה 8 כתוב ++*pp. על פי כלל האסוציאטיביות, ניתן לכתוב זאת מחדש בתור, (++(*(pp))). מאז, pp = 304, *pp = 204, הערך של *pp = *(p[2]) = 206 אשר יצביע כעת על a[3].

פיתון גמלים

בשורה 9, שוב נכתב הביטוי שמדפיס שלושה ערכים, כלומר, pp-p, *pp-a, *pp. בואו נחשב כל אחד מהם.

  • pp = 304, p = 300 => pp - p = (304 - 300)/2 => pp-p = 2, כלומר, 2 יודפסו.
  • pp = 304, *pp = 206, a = 200 => *pp-a = (206 - 200)/2 = 3, כלומר, 3 יודפסו.
  • pp = 304, *pp = 206, *(*pp) = 409, כלומר, 409 יודפסו.

לכן, כתוצאה משורה 9, הפלט 2, 3, 409 יודפס על הקונסולה. בשורה 10 נכתב ++**pp. על פי כלל האסוציאטיביות, ניתן לכתוב זאת מחדש בתור, (++(*(*(pp)))). pp = 304, *pp = 206, **pp = 409, ++**pp => *pp = *pp + 1 = 410. במילים אחרות, a[3] = 410.

בשורה 11, שוב נכתב הביטוי שמדפיס שלושה ערכים, כלומר, pp-p, *pp-a, *pp. בואו נחשב כל אחד מהם.

  • pp = 304, p = 300 => pp - p = (304 - 300)/2 => pp-p = 2, כלומר, 2 יודפסו.
  • pp = 304, *pp = 206, a = 200 => *pp-a = (206 - 200)/2 = 3, כלומר, 3 יודפסו.
  • בשורה 8, **pp = 410.

לכן כתוצאה משורה 9, הפלט 2, 3, 410 יודפס על הקונסולה.

לבסוף, הפלט של התוכנית השלמה יינתן כ:

תְפוּקָה

 1 1 206 2 2 300 2 3 409 2 3 410