מצביעים לפונקציות בשפת C

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

אינטרפרטציה גרפית של AI כלשהי לפרומפט "מצביע לפונקציה"
אינטרפרטציה גרפית של AI כלשהי לפרומפט "מצביע לפונקציה"

מצביע פשוט (תזכורת)

בתוכנית הבאה, i הוא משתנה רגיל ו-p הוא מצביע שמקבל (בשורה 9) את הכתובת של i. המספרים בפלט של התוכנית יהיו "2" ואז "3". אפרופו, כל התוכניות בפוסט זה הן למחשב, לא למיקרו-בקר, ומכאן השימוש החופשי ב-printf.

הגדרה ושימוש במצביע פשוט ב-C
הגדרה ושימוש במצביע פשוט ב-C

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

הגדרת מצביע עם מאפיינים נוספים למצביע ולמוצבע
הגדרת מצביע עם מאפיינים נוספים למצביע ולמוצבע

המצביע, ששמו p, מצביע לטיפוס int קבוע (כי מילת המפתח const היא משמאל לכוכבית!), אבל הוא עצמו לא קבוע אלא volatile. אם תכתבו את השורה הזו במקום שורה 6 בקוד למעלה, הקומפיילר יודיע על שגיאה בשורה 10, כי מתבצע שם שינוי ערך של המוצבע, והרי אסור לשנות const.

מצביע לפונקציה

פונקציה היא יצור מורכב יותר מאשר משתנה רגיל: יש לה טיפוס ערך חוזר (או void), ורשימת פרמטרים (או void). איך כוללים את כל זה בתוך הגדרת מצביע? התשובה היא, כאמור, תחביר שרירותי שחייבים לשנן. בגדול הוא נראה כמו הגדרת פונקציה, אבל עם כוכבית לפני השם שלה, וסוגריים סביבם. כמו כן, לא חייבים לכתוב את שמות הפרמטרים, אלא רק את הטיפוס שלהם. הנה תוכנית לדוגמה, עם מצביע-לפונקציה שנקרא funcPtr (מוגדר בשורה 9). המצביע הזה מקבל (בשורה 11) את הכתובת של הפונקציה helloPrint וקורא לה עם הערך 123 (שורה 12). לכן, הפלט של התוכנית יהיה "Hello 123".

הגדרה ושימוש במצביע לפונקציה ב-C
הגדרה ושימוש במצביע לפונקציה ב-C

פרמטר שהוא מצביע לפונקציה

אפשר להשתמש בצורת ההגדרה המגושמת הזו גם ברשימת פרמטרים של פונקציה רגילה. בקוד הבא, הפונקציה callbackUser מקבלת פרמטר בשם callback, שהוא בעצמו מצביע לפונקציה. שימו לב שצורת ההגדרה שלו (בשורה 7) זהה לזו של funcPtr בקוד למעלה. גם כאן הפלט יהיה "Hello 123".

הגדרת פרמטר בפונקציה שהוא מצביע לפונקציה
הגדרת פרמטר בפונקציה שהוא מצביע לפונקציה

אבל זה מסורבל – דמיינו הגדרה של פרמטר, שהוא מצביע לפונקציה שמקבלת שלושה או ארבעה פרמטרים שונים! כדי ליצור קוד נקי יותר ונוח יותר לקריאה ולשינויים, עדיף ליצור טיפוס של מצביע לפונקציה הזו. יצירת טיפוסים נעשית באמצעות typedef. הנה תוכנית דומה, רק עם typedef למצביע-לפונקציה (מוגדר בשורה 3, ונעשה בו שימוש בשורה 9):

מערך של מצביעים לפונקציות

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

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

אבל אפשר גם להגדיר את המערך עצמו ב-typedef, וכך ליצור קבוצת מצביעים שלמה ולהעביר אותה, לפי הצורך, בתור פרמטר. איפה לדעתכם צריך לשים בהגדרה את הסוגריים המרובעים שיוצרים מערך? התשובה, שאפשר להתווכח לגבי ההיגיון שבה, היא מיד אחרי שם המערך. הנה תוכנית הדגמה שעושה את זה. שורה 3 מגדירה טיפוס של מערך של מצביעים לפונקציות, ובשורה 4 מוגדר משתנה אחד מטיפוס זה. בשורות 22 ו-23 המערך מאוכלס בהפניות לפונקציות שונות, ושורה 24 שולחת אותו כפרמטר לפונקציה "callbackUser", שעוברת על כולו ומריצה את הפונקציות שבו בזו אחר זו.

הגדרה ושימוש במערך של מצביעים לפונקציות ב-C
הגדרה ושימוש במערך של מצביעים לפונקציות ב-C
להרשמה
הודע לי על
4 תגובות
מהכי חדשה
מהכי ישנה לפי הצבעות
Inline Feedbacks
הראה את כל התגובות

לא צריך להוסיף & בקריאה?

הפלט של התוכנית בסעיף "מצביע לפונקציה" יהיה Hello 123,
ולא 123.