מצביע
במדעי המחשב, מצביע (באנגלית: Pointer) הוא טיפוס נתונים אשר ערכו מפורש ככתובת בזיכרון המחשב. שימוש בו מאפשר טיפול בנתונים תוך התייחסות לכתובתם, ולא להם עצמם.
מצביעים הם מאבני היסוד של שפות התכנות הקלאסיות.
מהות המצביע
[עריכת קוד מקור | עריכה]המחשב שומר נתונים ביחידות זיכרון נפרדות, כך שלכל יחידת זיכרון מוקצת כתובת. לרוב כל מילה או בית בזיכרון המחשב מהווים יחידת זיכרון.
מצביע מאפשר להתייחס אל יחידת הזיכרון דרך מיקומה בזיכרון המחשב, ולא באופן ישיר. דרך פעולה זו מאפשרת:
- להתייחס לנתונים המאוחסנים בזיכרון המחשב באופן מופשט ולא ישיר; כלומר התייחסות למיקום יחידת הזיכרון, ולא ליחידה עצמה.
- העברת פרמטרים בין שגרות באופן חסכוני משום שלא נדרשת העתקה; כאשר אנו שולחים את הכתובת בלבד, ולא את כל הנתון, נדרשת פעולה חסכונית הרבה יותר.
- הקצאה דינמית של זיכרון בזמן ריצה; יצירת מבנה זיכרון דינמי ומשתנה, באמצעות המצביע המייצג שלו.
שימוש שגוי במצביעים עלול לגרום לחריגות ולשגיאות זמן ריצה, משום שניסיון גישה לכתובת לא חוקית יגרום לחריגת גישה. שורש הבעיה נעוץ בקלות בה ניתן לבצע מניפולציות על המצביע, המכיל ערכים מספריים לכל דבר ועניין.
סוגי מצביעים
[עריכת קוד מקור | עריכה]מצביעי טיפוס
[עריכת קוד מקור | עריכה]בשפות תכנות רבות ניתן להגביל מצביעים כך שיוכלו להצביע רק על טיפוסי נתונים מסוימים.
לדוגמה, ניתן להגדיר מצביע לטיפוס int (מספרים שלמים). מצביע כזה יוכל להצביע אך ורק למבנה נתונים מסוג int ונוכל להתייחס אל ערכו רק באופן בו ניתן להתייחס ל- int.
מצביע לשגרה
[עריכת קוד מקור | עריכה]ניתן להשתמש במצביעים על מנת להצביע על שגרה. יכולת זו מאפשרת להעביר שגרות כפרמטרים.
למצביע האפס יש ערך שמור (ע"פ רוב 0) המהווה אינדיקציה לכך שזוהי כתובת ריקה. כתובת זו שאליה הוא מצביע מוגדרת מראש.
מצביעי אפס הם בשימוש רב, על מנת לסמן מצבי קצה. מצבי קצה כאלו יכולים להיות מגוונים ביותר, לדוגמה- השבתת מצביע להקצאה דינמית או ציון קצה מבנה נתונים.
שימוש מושכל במצביעים
[עריכת קוד מקור | עריכה]השימוש במצביעים הוא בעל פוטנציאל גבוה לבעיות בעיקר בגלל האפשרות לחריגת גישה. למרות זאת, היתרונות שבשימוש בהם גוברים על החסרונות. בשל כך הביאו שפות תכנות מתקדמות במדעי המחשב (כמו Java ו־C#), לשימוש בטיפוסים קצת יותר מוגבלים המאפשרים הצבעה על אובייקטים, אולם לא מאפשרים ביצוע מניפולציה מספרית על המצביע. כך למעשה מתבצעת הפרדה בין היכולת להצביע על אובייקטים, לבין היכולת לגשת לכתובות אקראיות בזיכרון.
בעיה נוספת הקשורה במצביעים ובהקצאת זיכרון דינאמית, היא דליפת זיכרון, הנובע מכך שבהליך מסוים זיכרון שהוקצה לצורך פעולה, לא שוחרר בתום הפעילות ונשאר תפוס. כאשר פעולה זו מתבצעת פעמים רבות זיכרון נתפס על ידי התוכנית לשווא, מכיוון שלא נעשה בו כל שימוש. גם כאן שפות תכנות מתקדמות ניסו לתת פתרונות הולמים, כגון "איסוף זבל" (garbage collector) מובנה אוטומטי.
מחלקות לטיפול במצביעים
[עריכת קוד מקור | עריכה]גישה נוספת לפתרון ולכיסוי החסרונות, היא ליצור מחלקה שתעטוף את נושא המצביעים ותאפשר לבצע פעילות שכזו, מבלי להיות חשוף לאותן הבעיות הכרוכות בהקצאה דינמית.
כך למשל ב-Visual Basic באמצעות מחלקת אוסף (Collection), ניתן להוסיף ולהסיר איברים באופן דינמי. האיברים יכולים להיות מכל סוגי המשתנים הקיימים, ויכולים להיות בו-זמנית שונים בגודלם ובסוגם, והגישה לאיברים היא באמצעות אינדקס כמו במערך. בדוט נט ישנה מחלקה מיוחדת (Marshal בשיתוף עם המשתנה IntPtr) שתפקידה לטפל בהקצאה דינמית מבלי לאפשר הקצאה דינמית ישירה. למחלקה זו יש אפשרויות רבות ומגוונות, וייחודה בכך שהיא מאפשרת הקצאה דינמית אך מצמצמת את הבעיות הכרוכות בכך.