מבוא לשפת C תירגול 8: פונקציות שבוע שעבר... מערכים מיזוג מערכים ממויינים מערכים דו-ממדיים מבוא לשפת סי - תירגול 8 2 תוכנייה פונקציות ברמת התקשורת הבין-אישית חלוקה לתתי בעיות בדומה למפתח של ספר קריאות גבוהה יותר של הקוד, "כמעט אנגלית" הסתרת הפרטים מהמשתמש, לדוגמא printf() ברמת התקשורת עם המחשב מחסנית הקריאות העברת פרמטרים ע"י ערך value( )call by תחום הגדרה של משתנה פונקציות מבוא לשפת סי 4 - תירגול 8 3 שימוש בפונקציה הכרזה על הפונקציה הגדרה של פונקציה 5 המבנה של פונקציה: בסוגריים: רשימת הפרמטרים )מופרדים בפסיקים ביניהם(. גוף הפונקציה ההוראה return משמשת לקביעת הערך המוחזר על-ידי הפונקציה. double delta(double a, double b) double result = a b; if (result < 0) result = -result; return result; 6 שם הפונקציה הטיפוס המוחזר ע"י הפונקציה מופיע לפני שמה. משתנה מקומי: קיים רק בתוך הפונקציה, בזמן שהפונקציה פועלת. "נשכח" לאחר סיום פעולת הפונקציה. double delta(double a, double b); int () שימוש בפונקציה double a, b, abs_diff; scanf("%lf%lf", &a, &b); absolutedifference = delta(a, b); printf("%f", absolutedifference); הגדרת הפונקציה double delta(double a, double b) double result = a b; if (result < 0) result = -result; return result; 1
הכרזה של פונקציה פעולת RETURN אם פונקציה לא הוגדרה או לא הוכרזה לפני הקריאה אליה, הקומפיילר ייתן שגיאה. פתרון 1: מגדירים את הפונקציה מעל הפונקציה הראשונה שקוראת אליה. פתרון 2: מכריזים על הפונקציה מעל הפונקציה הראשונה שקוראת אליה. את הפונקציה מגדירים למטה יותר בתוכנית. איך מכריזים? double delta(double a, double b); פעולת return מסיימת את הרצת הפונקציה הנוכחית. ממשיכים את ההרצה מהמקום בו קראנו לפונקציה. הערך שהועבר ל- return הוא הערך שיוחזר לקורא. מוחזר תמיד עותק של הערך, אפילו אם נכתוב שם של משתנה. כאשר הפונקציה לא מחזירה ערך )טיפוס מוחזר,)void return לא מקבלת ערך )וגם לא הכרחית לכתיבה(. אם בתוך לולאה או מבנה switch מתבצעת פעולת return במקום,break הרצת הפונקציה נגמרת )ולא רק של המבנה!(. חייבים ";" בסוף השורה אין צורך לרשום את שמות המשתנים: double delta(double, double); 7 8 קריאה לפונקציה על מנת לקרוא לפונקציה, יש לתת ערך לכל פרמטר )לפי הסדר(. ניתן, אך לא הכרחי, להשתמש בערך המוחזר. כל פונקציה יכולה לקרוא לכל פונקציה. לדוגמה: ערכי הפרמטרים result = delta(c/2.0, d) * 50; הערך המוחזר 9 חישוב הפרמטרים בקריאה לפונקציה ראשית, יש לחשב את כל הפרמטרים המועברים לפונקציה. למשל, כדי לחשב את הפונקציה הבאה: delta(tan(cos(0.2)), sin(1.3)) יש לחשב ראשית את כל ערכי הפרמטרים... ראשית, נחשב את.tan(cos(0.2)) 0.98. לשם כך, יש לחשב קודם את.cos(0.2) התוצאה: כעת, נחשב את.tan(0.98) התוצאה: 1.49. שנית, נחשב את.sin(1.3) התוצאה: 0.96. וכעת סוף-סוף נוכל לחשב את (0.96.delta(1.49, התוצאה: 0.53. 10 פרמטרים וערך מוחזר מה לא בסדר בפונקציות? double min(int a, int b) if (a > b) return a; void print_value(int m) printf("value=%d\n", m); return m; int find_divisor(int num) int j = num / 2; for ( ; j>1; j--) if (num % j == 0) if (j == 1) else return j; מהם הפרמטרים והערך המוחזר? int gcd(int n, int m); double sin(double number); void print_account(int id, double cash); double get_time(void); void print_table(); int get_temperature(today); ניתן לכתוב גם : double get_time(); 11 12 2
קריאה נכונה לפונקציה מהלך ריצה של תוכנית double delta(double a, double b) double result = a b; if (result < 0) result = -result; return result; int () double x, y; scanf("%f",&x); scanf("%f",&y); d = delta(x, y); printf("the difference is %f\n", d); איזו מן הקריאות לא חוקיות? מדוע? int gcd(int n, int m); void print_value(int num); int dist(double, double); char get_letter(void); j = gcd(j, j); result = print_value(i+1); dist(2.2, 1.5); הפונקציות: printf("input: %c\n", get_letter(k)); get_letter; הסוגריים "מסבירים" לקומפיילר כי מדובר בפונקציה. אחרת זה שם של משתנה 13 1. מתחילים בפונקצית. 5.2. קריאה מריצים את לפונקצית פונקצית,scanf כדי לקבל,delta() ערך ל- x. כאשר הפרמטר a 3.מקבל כנ"ל, 2.1 עבור y. ופרמטר b מקבל 1.5.4. קוראים לפונקצית.delta 5. הפרמטרים מקבלים את 6. פעולת return מחזירה הערכים שהועברו להם מהצד עותק של הערך שיש ב- result. הקורא. בפועל מתבצע: return 0.6 6. הפונקציה מחזירה את ערכו של.result ל- d. ההרצה הוכנס את המוחזר מתחילים הערך.1.7 מפונקצית () 2.8. קריאה מפעילים את לפונקצית הפונקציה,scanf() 3. 4. אשר.printf קריאה דואגת נוספת לפונקצית להכניס את לפונקצית,delta() 7.9. עם הערך ריצת,scanf() הערך ערכים 0.62.1 אשר 2.1 התוכניתו- מוצב במשתנה 1.5 מכניסהx. בתוך את מסתיימת עם 8. הערך סיום בפועל, משתנה קריאהd 1.5 ריצת הקריאה לפונקצית במשתנה y הפונקציה נראית: printf() () את מפונקציתהתוצאה. delta( אשר, 1.5 יוצאים 2.1 מדפיסה..9) ע"י return 0 14 חישוב הפרמטרים בקריאה לפונקציה דוגמה: מספר סימטרי מה קרה בעצם כאשר חישבנו?delta(x,y) x y d 2.1 1.5 0.6? d = delta(x, delta(2.1, 0.6; y); y); 1.5); בשפת C, מעבירים ערכים לפרמטרים ולא את המשתנים עצמם! 15 16 דוגמה מספר סימטרי מספר סימטרי - פתרון int symmetric(int x) /* assuming that x is not negative */ int reverse=0, tmp=x; if (x < 10) /* a single digit */ return 1; while (tmp > 0) reverse = reverse*10 + tmp%10; tmp /= 10; return (x==reverse); נגדיר מספר להיות מספר סימטרי אם מתקיים ש: המספר בעל ספרה אחת או הספרות שנמצאות במרחקים שווים מאמצע המספר זהות. לדוגמה: 3 הוא מספר סימטרי, כיוון שהוא בעל ספרה אחת. 12321 הוא מספר סימטרי. אמצע המספר הוא הספרה 3, וספרות שנמצאות במרחקים שווים מהאמצע זהות. 1771 גם הוא מספר סימטרי. כתוב פונקציה: (x int symmetric(int המקבלת מספר שלם חיובי, ומחזירה 1 אם המספר סימטרי, אחרת 0. 17 18 3
דע מאין באת ולאן אתה הולך מחסנית הקריאות התוכנית מתחילה בפונקציה. f. מפעילה פונקציה אחרת, נניח g. מפעילה פונקציה אחרת, נניח f וכן הלאה... כיצד יודעת התוכנית לאן לחזור כשכל פונקציה מסתיימת? למשל, כש- g מסתיימת, כיצד יודעת התוכנית לחזור ל- f ולא ל-? 19 20 דע מאין באת ולאן אתה הולך המחסנית למשל, הנה קטע תוכנית: int delta_age; int delta_height; delta_age = delta(age1, age2); delta_height = delta(height1, height2); הפונקציה delta נקראת כאן פעמיים. בכל פעם, לאחר שהפונקציה מסתיימת, התוכנית ממשיכה ממקום אחר. איך זה קורה? במהלך ריצת התוכנית, המערכת מנהלת מחסנית של קריאות. המחסנית מנוהלת בשיטת Last-In-First-Out )ומכאן שמה(. על המחסנית נשמרים הנתונים הבאים: לאן לחזור לאחר סיום הפונקציה. ערכי הפרמטרים שהועברו לפונקציה. משתנים מקומיים של הפונקציה. 21 22 מקסימום מבין שני מספרים מקסימום מבין 4 מספרים הפונקציה הבאה תחזיר את המקסימום מבין שני הערכים שלה: double (double a, double b) if (a > b) return a; return b; כיצד ניתן לכתוב פונקציה המחשבת את המקסימום מבין 4 ערכים? ניתן לממש את ע"י שימוש ב- : double (double a, double b, double c, double d) double temp1 = (a, b); double temp2 = (c, d); double max = (temp1, temp2); return max; שימו לב בגוף הפונקציה אין שום השוואה ישירה בין הפרמטרים...! 23 24 4
מקסימום בין 8 מספרים המחסנית בזמן הריצה ואיך נממש מציאת מקסימום מבין 8 מספרים? double (double a, double b, double c, double d, double e, double f, double g, double h) double temp1 = (a, b, c, d); double temp2 = (e, f, g, h); double max = (temp1, temp2); return max; שימו לב השתמשנו גם ב- וגם ב-. איך נראית המחסנית כשמבוצעת הקריאה? מתוך (1,8,4,5,) קוראים ל- תחילת ריצת משווה בין 4 הפרמטרים הראשונים ע"י קריאה ל- משווה בין 2 הפרמטרים הראשונים params: 1,8 משווה בין 2 הפרמטרים הבאים מקבל את התוצאה של הקריאה ל- הראשונה params: 4,5 25 26 המחסנית בזמן הריצה )המשך( המחסנית בזמן הריצה )המשך( params: params: 1,3 params: params: 9,3 params: מקבלת את params: 8,5 התוצאה של הקריאה ל- השנייה משווה בין 2 תוצאות הביניים סיימה לחשב את המקסימום בין 4 המספרים מקבלת את תוצאת השוואה של הרביעה הראשונה. משווה בין 4 הפרמטרים הבאים ע"י קריאה ל- params: משווה בין 2 הפרמטרים הראשונים משווה בין 2 תוצאות הביניים משווה בין 2 תוצאות הביניים מקבל את התוצאה של הקריאה ל- הראשונה משווה בין 2 הפרמטרים הבאים params: מקבל את התוצאה של הקריאה ל- השנייה משווה בין 4 הפרמטרים הבאים ע"י קריאה ל- params: params: 9,6 params: - תירגול 8 28 27 מבוא לשפת סי המחסנית בזמן הריצה )המשך( כאן קוראת ישירות ל-. בעזרת המחסנית, תמיד ידעה לאן לחזור, למרות שהיא נקראת לעיתים מתוך ולעיתים מתוך. העברת פרמטרים ע"י ערך )call by value( משווה בין 2 תוצאות הביניים params: 9,8 חוזרים ל- Max8 מחזירה את התוצאה ל- 29 30 5
call by value פרמטרים אקטואליים ופורמליים הפרמטרים שהפונקציה מקבלת )כפי שהם מופיעים בהגדרת הפונקציה( נקראים פרמטרים פורמליים. מבחינת הפונקציה, הפרמטרים הפורמליים הם משתנים לכל דבר. הם מוכרים בתוך הפונקציה בלבד והם מאותחלים מחדש עם כל קריאה אליה. הערכים המועברים לפרמטרים )כפי שהם מופיעים בכל נקודה של קריאה לפונקציה( נקראים פרמטרים אקטואליים. היות שהפרמטרים האקטואליים הם תמיד ערכים, שיטת הקריאה הזו לפונקציה נקראת call by value בהמשך נכיר שיטת קריאה נוספת. הפרמטר האקטואלי הוא תמיד ערך! הפונקציה לא יכולה לשנות פרמטר אקטואלי. 31 32 פרמטרים אקטואליים ופורמליים מהם ערכי המשתנים לאחר קריאה ל- max? קריאה ל- max מפונקצית : פונקצית :max int max(int a, int b) int res; if (a > b) res = a; a = b; else res = b; b = a; return res; int x = 7, y = 8, d; d = max(x, y); int a = 7, y = 8, d; d = max(a, y); int a = 7, b = 8, d; d = max(a, b); int a = 7, b = 8, res; res = max(a, b); פרמטרים פורמליים פרמטרים אקטואליים תחום הגדרה של משתנה 33 34 הגדרת משתנים בתוך בלוק מדוע "משתנים מקומיים"? ניתן להגדיר משתנים בתחילת כל בלוק. מה זה בלוק? גוף פונקציה כל רצף פקודות בתוך סוגריים מסולסלים:. משתנה קיים ונגיש מתחילת הבלוק בו הוא מוגדר ועד סוף הבלוק. המשתנה אינו נגיש מחוץ לבלוק. במקרה של כמה כניסות לבלוק )למשל בתוך לולאה(: בכל כניסה המשתנה "מוגדר" מחדש. במקרה של שמות זהים לכמה משתנים, המהדר יתייחס תמיד למשתנה שהוגדר בבלוק הפנימי ביותר )ביחס לנקודת הגישה למשתנה (. משתנים המוגדרים בתוך בלוק )כולל בתחילת פונקציה( נקראים מקומיים. הם מקומיים לבלוק שהגדיר אותם! משתנה מקומי אינו קיים מחוץ לבלוק שבו הוא הוגדר. לכן לא ניתן להשתמש בהם מחוץ לבלוק. 35 36 6
"המשתנה אינו נגיש מחוץ לבלוק" "בכל כניסה 'מוגדר' המשתנה מחדש" int (void) int a; int i; for (i = 0; i < 10; i++) int temp = 0; temp++; a = temp; printf("%d", a); int (void) int a = 10; if (a > 0) int temp = 20; printf("%d", temp); חידה: מה תדפיס התוכנית הבאה? מה תדפיס התוכנית הבאה? 37 38 "המהדר יתייחס תמיד למשתנה שהוגדר בבלוק הפנימי ביותר" int (void) int a = 10; if (a > 0) int a = 0; printf("%d\n", a); printf("%d", a); מה תדפיס התוכנית הבאה? "המהדר יתייחס תמיד למשתנה שהוגדר בבלוק הפנימי ביותר" משתנה מבלוק פנימי מסתיר משתנה מבלוק חיצוני שיש לו את אותו שם. תכונה זו נקראת הסתרה. קוד המשתמש בהסתרה עשוי להיות מבלבל. לכן, מומלץ להימנע מכך. 39 40 הסתרה בעזרת טיפוס שונה הסתרה באותו הבלוק? במקרה של שני משתנים בעלי אותו שם )משתנה "חיצוני" ומשתנה "פנימי" יותר(, אין חובה שלשניהם יהיה את אותו טיפוס. למשל: האם לדעתכם ניתן להסתיר משתנה, בעזרת משתנה המוגדר מאוחר יותר בתוך אותו הבלוק )לא בבלוק פנימי(? למשל, מה תדפיס התוכנית הבאה? int (void) int a = 10; int b = 5; int c = 13; int a = 12; /* Hide the first definition of a */ printf("%d", a); int (void) int a = 10; if (a > 0) double a = 0.0; printf("%f\n", a); printf("%d", a); 41 42 7
משתנים "מחוץ לבלוק"? משתנים גלובליים מול לוקאליים ניתן, בשפת C, להגדיר משתנים שאינם בתוך אף בלוק. המשתנים הללו מוגדרים מחוץ לכל פונקציה שהיא. הם נקראים משתנים גלובליים, משום שהם נגישים בכל מקום בתוכנית )מנקודת ההגדרה בקוד "ומטה"(. לעומתם, משתנים מקומיים נקראים בלעז "לוקאליים". משתנה גלובלי בא לידי קיום עם תחילת התוכנית, והוא קיים עד סופה. ערך שנכתב אליו נשמר עד תום התוכנית, גם אם יצאנו מהפונקציה שבצעה את הכתיבה. משתנה לוקאלי בעל אותו שם יכול להסתיר משתנה גלובלי. ברוב המקרים ניתן )ועדיף!( להשתמש במשתנים מקומיים. באופן כללי, שימוש במשתנים גלובליים ללא הצדקה מיוחדת נחשב לתכנות גרוע. 43 44 דוגמה: שימוש במשתנים גלובליים מה תדפיס התוכנית הבאה? int lights_on = 0; /* Global variable */ void flip_lights(void) lights_on =!lights_on; void print_light_status(void) if (lights_on) printf("lights are on."); else printf ("Lights are off."); int (void) int lights_on = 1; /* Local variable */ flip_lights(); print_light_status(); 45 8