
typedef, ואיפה משתמשים בו: מערך של מצביעים, callbacks ב-Windows API ופונקציית המיון qsort.void f() אפשר להגדיר מצביע ממש כמו לכותרת שלה: void (*pf)(). עוטפים את השם בסוגריים ומוסיפים כוכבית לפניו. אחרי pf = f קריאה ל-pf() מפעילה את f.typedef: מוסיפים typedef לכותרת של המצביע, והשם הופך לטיפוס. אז כותבים PF pf = f;, וההמרה המסובכת נעלמת.int f2(int i, int j). ערך ההחזרה נמצא באוגר AX, וניתן לראות אותו בחלון Registers בדיבאגר. בדוגמה 4 כפול 5 פחות 3 החזיר 17.swap(int*, int*) מגדירים typedef בשם PSwap, ומפעילים אותה דרך המצביע עם הכתובות של i ו-j, והערכים מתחלפים.PSwap עם שלוש פונקציות swap מופעל בקריאה דרך האיברים שלו. אפשר גם להעביר אותו כפרמטר לפונקציה אחרת (calc), ובדיבאגר רואים לאיזו פונקציה כל איבר מצביע.CreateThread מקבל מצביע לפונקציה (callback), ומערכת ההפעלה היא שקוראת לה. הפונקציה מחזירה DWORD, מקבלת void* ונהוג לקרוא לה ThreadProc. הפונקציה מסומנת WINAPI, כלומר __stdcall, כי מערכת ההפעלה קוראת לה לפי שיטת קריאה שונה מזו של C.qsort: מקבלת את המערך, מספר האיברים, גודל כל איבר ופונקציית השוואה שאנחנו כותבים. היא לא יודעת מה בתוך התאים. הפונקציה מקבלת שני void*, ובתוכה ממירים ל-int*, כי אי אפשר לקחת תוכן של void*. מחזירים הפרש בין שני הערכים: חיובי, שלילי או אפס. היפוך ההפרש הופך את כיוון המיון.typedef צריך להיות מוגדר לפני השימוש בו.CreateThread: היא חוזרת מיד ומבקשת ממערכת ההפעלה ליצור thread, ואם main מסתיים לפני כן, התהליך מסתיים בקריאה ל-ExitProcess וה-thread מת איתו.qsort שגדל האיבר הוא 4 בתים ל-int, והפרמטר הוא הגודל של איבר אחד ולא של המערך כולו. בניסיון ראשון נתנו 28, והערכים יצאו שגויים.CreateThread ול-qsort.הטקסט המלא של הדקות הראשונות בהרצאה «מצביעה לפונקציה - Function Pointer» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.
טוב, בארצה הזה אנחנו נלמד על מצביעים לפונקציות, בואו נפתח visual studio וניצור create project ב-other languages C++ 32 ונקרא לזה function pointer פוינטר זו אולי, 0,1 ונגיד לו ok ופה נגיד לו אפילו בלי
pre-compiled header ונגיד לו finish. עכשיו בוא נראה איזה מתקמפל, build, יפה. טוב, אז מה זה בעצם מצביע לפונקציה? אז בואו נגיד שיש לי כאן פונקציה, הכי הכי פשוטה שאני יכול לדמיין, זה void f, וניתן פה גוף לפונקציה, גוף רק.
זו הפונקציה הכי הכי פשוטה. אפילו נשים את זה ככה שזה ממש יהיה כלום וטום. יפה, אז אני יכול לקרוא ל-f, למה הוא לא משלים אותי, אני לא יודע, בוא נעשה f6, נראה שהכל בסדר, build succeeded, רואים פה נמדה יפה, אני יכול לקרוא לפונקציה, אבל אני יכול גם לכוון פוינטר ולהפעיל את הפוינטר לפונקציה.
אז הפוינטר לפונקציה, איך אני מגדיר פוינטר לפונקציה? אז הוא פונקציה שמקבלת void ומחזירה void, מקבלת כלום ומחזירה void. אז אני אומר, אוקיי, אני בעצם לוקח ומשכפל את הכותרת של הפונקציה, נכתוב את זה כאן, void f, אבל אז אני בא ל-f ועוטף אותו בסוגריים ושם לו כוכבית לפני.
וזה בעצם אומר pointer לפונקציה, זה טיפוס, סליחה, זה לא טיפוס, זה כבר משתנה עצמו. הגדרתי את f, ואולי נקרא לו pf. הגדרתי את pf, pf הוא פוינטר לפונקציה, pf הוא פוינטר, כשאני שם את זה ככה לפונקציה, שמקבלת void, מקבלת כלום, ומחזירה void.
ואז אני יכול לכוון את pf, להגיד שpf שווה ל-f, ואז להפעיל את pf, ובעצם f תופעל. אולי נעבור ככה, וככה, וככה, ונעשים פה F9, נעשים פה Breakpoint, וננסה את מה זה לנו, בואו נראה מה קורה.
זה לא מעניין, אפילו זה לא מעניין, אפילו זה לא מעניין, אוקיי. ונגיד לו Step Over, ונקרא ל-F, טוב זה לא בעיה, נכנסנו ל-F וסיימנו. ועכשיו אני, אוקיי, F בפני עצמה היא כתובת, כן? F זה בעצם כתובת.
זה אופרטור הקריאה לפונקציה באופן כללי אני יכול לקחת כתובת להצמיד לה את אופרטור הקריאה לפונקציה ובעצם אני אפעיל את הפונקציה תכף ננסה את זה ממש ככה ואני מכוון את pf להצביע לאנשה f מצביע שזה 4111c2 יפה וזה הערך של f גם 411370 או זה הערך רגע רגע רגע
F עצמה היא כתובת וזה כתובת אחרת קצת מוזר אבל בסדר הוא גם מראה לי שזה בעצם F ופה הוא מראה לי זה 370 וזה לא לא מובן אבל בסדר וקריאה לפונקציה והגענו ל-F ואנחנו חוזרים
יפה אבל למה הם לא אותה כתובת? למה PF הוא 4111 C2 ו-F היא 4 וכו'.
בואו נראה אם אני אעשה אכבר ימני ואני אגיד לו addWatch זה PF הוא מראה לי פה הוא מראה לי כאן שזה F void נורא מוזר. בואו נגיד לו אכבר ימני ו-goat disassembly אז F נמצאת באמת ב-370
אוקיי, F באמת ב-370, איפה ה-Code שלנו? בואו אנחנו פה, עקנו פה עכשיו goat disassembly אה, אז מה קורה כאן?
f, pf, הערך שלו לא נכון. כוכבית pf, מה הערך שלו גם לא נכון. אז איך זה עובד? אוקיי, pf בעצמו, איפה, מה הכתובת של, בוא נראה, אולי אני מסלב, מה הכתובת של pf?
זה בכלל, כתובת של pf, pf הוא על המחסנית, זה לא מעניין, המחסנית היא ב-12ff6, אוקיי, בואו נחזור כל הקוד שלנו, בואו אני אראה לכם משהו נורא נחמד, אוקיי, אז אנחנו יודעים שהפונקציה f היא בכתובת הזו, אני אקח את הכתובת הזו, אני אעשה לזה copy, ולמעשה אני יכול לכתוב את הכתובת ולקרוא לפונקציה. בואו ננסה את זה, אבל הוא לא מוכן. בואו נגיד
לו עצור, אולי הוא כן מתקמפל, בואו נגיד לו, לא, אי אפשר. למה? The term does not evaluate to our function taking fs arguments.
זה פשוט לא מתקמפל לפונקציה. אבל אני כן יכול לעשות לזה קאסטינג לפונקציה ואז להפעיל את זה. נכון? אני יכול רגע לקחת את זה ולעשות לזה קאסטינג לפונקציה או קאסטינג לכל דבר, לכל דבר.
אבל זה קצת יהיה מסובך, אבל זה אפשרי. איך עושים קאסטינג לגלה? אז אמרנו שזה הגדרה של המשתנה זה אז מה זה הקאסטינג?
אז הקאסטינג זה זה רק בלי הפי-אף כל הכחול בלי הפי-אף זה הקאסט זה הטיפוס זאת אומרת אם אני רוצה לקחת את המספר הזה ולעשות לו קאסטינג אז זה יהיה הקאסטינג שלי לכאן אני אדחוף את זה אני אקח את זה אני אגרור אותו עם החבר אם אני לחן
אני אגיד לו קופי אני אוריד את הפי-אף, אני אמחק אותו, יפה, אז עכשיו יש לי את הקאסטינג, אבל אני צריך את הקאסטינג על זה לעשות, אני אשים על זה סוגריים, על זה סוגריים, שיפט סוגר שמאלי, יפה, אז זה עכשיו פוינטר לפונקציה, מסוג שמקבל כלום, זה פוינטר לפונקציה, שמקבל כלום, מחזיר וויד, כן?
זה הקאסט וזה כבר המספר קאסטד וזה הפעלה שלו בוא נראה אם זה עובד מי יודע אולי זה גם עובד מתקמבל זה כבר נס מדעי קוראים ל-F פעם ראשונה קוראים ל-F פעם שנייה וקוראים ל-F פעם שלישית תמיד זה מפתיע יפה אני עדיין לא יודע
למה PF הזה במספר הזה אם תמצאו פתרון אז תבוא ותסבירו לי יפה אבל כמו ששמתם לב זה מאוד לא קרי לעשות דבר שכזה.
מאוד לא קרי. יפה, מאוד לא בריא, אבל גם מאוד לא קרי. זאת אומרת, אם אני רוצה לעשות קאסטינג, אז יש לי בעיה. אם אני רוצה לעשות קאסטינג בקושלי, וזו הפונקציה הכי פשוטה בעולם.
תארו לכם פונקציה שמקבלת כמה פרמטרים, ומחזירה איזה טיפוס מורכב, אולי איזה פוינטר לפוינטר, בגיצור התאבדות המונים. אז מה הפתרון להקל על הכאב ראש?
הפתרון הוא להגדיר את זה כטיפוס, כיצד, נורא פשוט. לוקחים את השורה, משכפלים עכבר ימני, copy, ומוסיפים מילה אחת מראש. type, type, def, מילה כחולה, יפה, ואת pf אנחנו נשנה ל-Ctrl Shift U, ל-pf גדול, יפה.
ולמעשה אפשר אפילו לוותר על השורה הזו ולהגדיר את pf קטן מסוג pf גדול. pf גדול הוא pf קטן, יפה. ואז אני בכלל יכול בוא אפילו נמחק את השאר ופשוט מאוד.
pf אני כבר מאתחר אותו ל-f, יפה, ואז אני קורא ל-pf. כל דבר כאן, במקום כל זה, לא במקום אני אשיר את זה, לא מפריע, אבל משהו יותר קרי, אני אגיד ככה, אני אגיד שאני ממיר לפי f את הערך המספרי הזה, אני אקח את הערך המספרי, אני אשים אותו פה, אני ממיר אותו לפי f, ואני יכול להגיד שפי f
גדול, אפילו אני יכול להגיד שפי f קטן שווה לזה, או שאני אגדיר את PF2 מסוג PF גדול ואז אני יכול לקרוא גם ל-PF2 וזה בעצם יפה לי את F יפה בואו נשחק עם זה פוינטרים לפינקציות
פוינטרים לפינקציות דבר נורא נחמד אין ספק אחד מהדברים היותר מורכבים בבקשה, לכו תעקבו אחרי הדבר הזה לקחנו את המספר מעין שהוא אני לא אתפלש את זה בעצם מכאן, control, נכון?
כן, ונריצה, וקוראים ל-f, נכנסים ל-f, ומגדירים את pf, ואז מכוונים את pf וקוראים ל-f, יפה מאוד, וזה כבר ראינו שזה עובד, וזה בעצם כבר הבנו שזה יעבוד, יפה מאוד.
טוב, בואו נראה כמה הרצאה, זו הייתה הרצאה די קצרה, בואו נמשיך. אולי למשהו טיפה יותר מורכב. וזה void f. בואו נגדיר משהו יותר מורכב, ונראה שבעצם אין הבדל.
בעזרת ה-type def, המורכבות בעצם כבר ביטלנו אותה. אז נגדיר פה, מקבלת את int i, ואת int j.
אולי גם, אולי אפילו כבר נקרא לה, לא, עדיין לא נקרא לה swap, אבל נקרא לה f2. וגם תחזיר int.
יפה מאוד. אז איך עושים את זה? אז כמו שהגדלנו type def הזאת, נגדיר את pointer לפונקציה 2, תראו זה יופי, שמקבלת את int i ואת int j, תראה, זה מחזירה, int יפה מאוד, ואז Ctrl-CV אני מגדיר את 2, ייצוג 2, מרפנה ל-2, יפה, ו-Ctrl-CV אני קורא ל-2, יפה מאוד, אותו דבר כל השעה, אני נבדל, בואו נראה שזה עובד,
נשים ב-f2 breakpoint ונריץ הוא לא רוצה להגיד לסטופ, אני לא יודע שלמה הוא כבר היה בריצה, נריץ אותו שוב יש לו שגיאות, redefinition, pf2 כבר יש לנו את pf2 מסוג pf או, הזאת תהיה pfa זאת אומרת, אני לא יודע, אני גם pf1 בעצם pf1 ונריץה עדיין Too few arguments ב-pf2, יפה, איזה יופי,
הוא זיהה ש-pf2 הוא פעלו לו נכון, הוא אומר int i ו-int j, ונביא לו את 4 ו-5, גם החזירה int, אז נגיד int i שווה למה שהיא תחזיר, בואו נגיד שהיא צריכה להחזיר גם return i כפול j פחות 3, ונרחיץ אותה, יפה מאוד, ואיפה אנחנו כרגע?
כל סטאק אנחנו כאן ומתוך אוקיי, מפה קראנו ל-pf2, אולי נעבור שורה שורה כי זה באמת קצת אוקיי, אבל נדלג על מה שכבר עשינו, אוקיי, f כבר ראינו ו-type def, ו-pf2 אני מכוון ל-f2
pf כבר ראינו, אבל בואו נקרא ל-f1 אם קוראים סליחה ל-f2, קוראים ל-f2