Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Studies

מאפייני פונקציות

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

חזרה מפונקציה

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

#include <stdbool.h>
#include <stdio.h>
bool is_even(int n) { return n % 2 == 0; }
int main() {
    int n;
    scanf("%d", &n);
    if (is_even(n)) {
        printf("%d is even!", n);
    }
}

אין מניעה שההערך שהפונקציה לא יהיה משתנה אלא ביטוי כלשהו לחישוב. במקרה זה הפונקציה הנקראת is_even החזירה ערך והפונקציה הקוראת main קראה אותו ועשתה בו שימוש.

Early Return

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

#define IS_NEGATIVE -1
#define IS_ZERO 0
#define BETWEEN_0_AND_100 1
#define LARGER_THAN_100 2
int classify_number(int n) {
    if (n < 0) {
        return IS_NEGATIVE;
    } else if (n == 0) {
        return IS_ZERO;
    } else if (n <= 100) {
        return BETWEEN_0_AND_100;
    } else {
        return LARGER_THAN_100;
    }
}

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

#define IS_NEGATIVE -1
#define IS_ZERO 0
#define BETWEEN_0_AND_100 1
#define LARGER_THAN_100 2
int classify_number(int n) {
    if (n < 0) {
        return IS_NEGATIVE;
    }
    if (n == 0) {
        return IS_ZERO;
    }
    if (n <= 100) {
        return BETWEEN_0_AND_100;
    }
    return LARGER_THAN_100;
}

נבחן את הביצוע שלה:

  1. בהתחלה אנחנו בודקים אם המספר שלילי ואם כן חוזרים.
  2. ב-if השני אנחנו בודקים האם המספר שווה ל-0 ואם כן חוזרים. נשים לב שאין צורך ל-else if - אם הגענו ל-if השני, מובטח שלא נכנסו ב-if הראשון, אחרת היינו מבצעים return.
  3. ב-if השלישי אנחנו בודקים האם n <= 100. נשים לב שאין צורך לכתוב ביטוי מורכב מהצורה n > 0 && n <= 100 - אם הגענו ל-if השלישי, מובטח שלא נכנסנו ל-if הראשון והשני, כלומר n גם לא שלילי וגם לא 0.
  4. לבסוף אנחנו מחזירים LARGER_THAN_100 - משיקולים דומים, אין צורך לבדוק האם n > 100 - אם הגענו לשורה האחרונה, מובטח כי לא נכנסו לאף if קודם.

צורת כתיבה זו נקראת early return והיא יכולה להיות מאוד אלגנטית ונוחה ביחס לאלטרנטיבות - למשל, ניתן לדמיין סיטואציה בה הפונקציה לעיל נכתבה כך:

int classify_number(int n) {
    if(n >= 0) {
        if(n == 0) {
            return IS_ZERO;
        } else {
            if(n <= 100) {
                return BETWEEN_0_AND_100;
            } else {
                return LARGER_THAN_100;
            }
        }
    } else {
        return IS_NEGATIVE;
    }
}

כתיבת בלוקים בתוך בלוקים נקראת קינון (nesting) - באופן כללי כדאי להגביל את הקינון ככל שאפשר על מנת “להחזיק בראש” פחות תנאים ובלוקים פתוחים. ההבדל בין כתיבה עם early return לעומת כתיבה עם nesting היא בהיפוך התנאים - שימו לב לכך!

הטיפוס void

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

#include <stdio.h>
#define ADULT_AGE 18
#define MAX_AGE 120

void print_age(int age) {
    if (age < 0 || age > MAX_AGE) {
        printf("Invalid age: %d\n", age);
        return;
    }
    if (age < ADULT_AGE) {
        printf("Child: %d\n", age);
        return;
    }
    if (age < MAX_AGE) {
        printf("Adult: %d\n", age);
        return;
    }
}

int main(void) {
    for (int i = 0; i < 120; i++) {
        print_age(i);
    }
}

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

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