C/C++ מדריכי
C הקדמה לתכנות ב
C++ הקדמה להיררכית מחלקות ב הצגה מזורזת :C++ הבנת דף הבית

שיעור 9: הקדמה למצביעים ב C

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

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

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

יסודות המצביעים

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

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

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

דוגמת הקוד הבאה מציגה מצביע טיפוסי:

#include <stdio.h> 
 
void main()  
{    
    int i,j;    
    int *p;   /* a pointer to an integer */ 
     p = &i;    
    *p=5;    
    j=i;    
    printf("%d %d %d\n",i,j,*p);  
}

השורה int *p מצהירה על מצביע. היא מבקשת מהמהדר להצהיר על משתנה p שהוא מצביע למספר שלם. ה * מציינת שמצהירים על מצביע ולא על ערך רגיל. ניתן ליצור מצביע לכל דבר: ל float, למבנה, לתו וכן הלאה.

השורה ;p=&i תהיה ללא ספק חדשה בשבילך. ב C, & נקרא אופרטור כתובת. משמעות הביטוי i& היא "הכתובת בזיכרון של המשתנה i". לכן, משמעות הביטוי p=&i היא "שים ב p את הכתובת של i". ברגע שתבצע את הפקודה , p יצביע ל i. לפני שתעשה זאת, p יכיל כתובת רנדומלית, לא ידועה, ושימוש בה קרוב לודאי שיגרום לשגיאת סגמנטציה.

לאחר השורה p ,p=&i; מצביע ל i. ברגע ש p מצביע ל i, למיקום בזיכרון i יש שני שמות. הוא עדיין מוכר כ i, אבל עכשיו הוא מוכר גם כ p*. כך C מדברת על שני חלקי מצביע: p הוא המיקום שמכיל את הכתובת, בעוד ש p* הוא המיקום שאליו מצביעה כתובת זו. לפיכך p=5* משמעותו שהמיקום ש p מצביע אליו צריך להיקבע ל 5. כיוון שמיקום זה הוא גם i, i גם מקבל את הערך 5. כתוצאה מכך, ;j=i קובע את j ל 5, ופקודת ה printf מייצרת 5 5 5.

נסה את הקוד הבא:

#include <stdio.h> 
 
void main()  
{    
    int i,j;    
    int *p;   /* a pointer to an integer */ 
     printf("%d %d\n",p,&i);    
    p = &i;    
    printf("%d %d\n",p,&i);  
} 

קוד זה מורה למהדר להדפיס את הכתובת המוחזקת ב p, ביחד עם הכתובת של i. המשתנה p מתחיל עם איזשהו ערך מטורף או עם 0. הכתובת של i היא באופן כללי ערך גדול. לדוגמא, כאשר הרצתי את הקוד הזה, קיבלתי את הפלט הבא:

0    2147478276
2147478276    2147478276

שמשמעותו שהכתובת של i היא 2147478276. ברגע שהפקודה ;p=&i התבצעה, p מכיל את הכתובת של i. נסה גם את זה:

#include <stdio.h> 
  
void main()  
{    
    int *p;   /* a pointer to an integer */ 
    
    printf("%d\n",*p);  
} 

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




לדף הראשון

<< לדף הקודם

לדף הבא >>