משפט if
סינטקס והגדרות
משפט if יאפשר לתוכנית שלנו להחליט האם לבצע או לדלג על קטע קוד מסוים בהתאם להאם תנאי כלשהו מתקיים. מספר דוגמאות לתנאים שימושיים בתכנות:
- בדיקת סיסמא - נבקש מהמשתמש להכניס סיסמא. אם הכניס אחת נכונה, ניתן לו גישה למחשב - אחרת נמנע ממנו להכנס.
- בדיקת נתונים - אם מישהו מבקש להזמין באינטרנט 4 מגני מסך, נבדוק מול הנתונים האם יש מספיק לתת לו ובמידה וכן נאשר את המשלוח שלו.
- צ’אט - במידה ואדם א’ רוצה לשלוח הודעה לאדם ב’, נבודק האם אדם ב’ לא חסם את אדם א’, ובמידה ולא נאשר את שליחת ההודעה.
לפני שנגדיר את הסינטקס בצורה מדויקת, נבחן דוגמא אינטואיטיבית:
#include <stdio.h>
#define PASSWORD 7521
int main() {
int password;
printf("Please enter your password\n");
scanf("%d", &password);
if (password == PASSWORD) {
printf("Access granted!\n");
} else {
printf("Access denied!\n");
}
}
ניתן להבין בצורה אינטואיטיבית מה קורה כאן - אנחנו מבקשים מהמשתמש סיסמא. אם הסיסמא שלו נכונה, אנחנו מדפיסים הודעת הצלחה - אחרת, הודעת שגיאה. לאחר ה-if מופיע תנאי כלשהו.
Note
נזכיר כי הביטוי
password == PASSWORDהוא מסוג בוליאני - ערכו יכול להיותtrueאוfalse, ולכן הוא מתאים לשמש תנאי שיתכן ומתקיים ויתכן ולא.
כעת בצורה מדויקת יותר - תנאים ממומשים ע“י מילת המפתח if, כאשר הסינטקס לכתיבת משפט if הוא:
if (boolean statement) {
// Execute only if statement is true
}
// Continue execution here
נפרק את המרכיבים:
- המילה
ifמתחילה את המשפט ולאחריה סוגריים רגילים(). - בתור הסוגריים נמצא ביטוי בוליאני כלשהו - כמו שראינו בהרצאה הקודמת, ביטוי בוליאני יכול לקבל את הערכים
trueאוfalseבלבד. - כאשר הביטוי הבוליאני הוא
true, נעבור לביצוע בלוק הקוד בתוך הסוגריים המסולסלים. אחרת, נקפוץ מיד לאחר קצה הבלוק.
Note
ב-C ובשפות אחרות קיימות מספר מילות מפתח (keywords) בהן לא ניתן להשתמש לאף צורך חוץ ממה שהשפה מגדירה.
ifהיא מילת מפתח כזו - לא ניתן להגדיר משתנה בשםint if = 5למשל.
לדוגמא - נבדוק האם יש מספיק מצרכים להכין עוגה:
#include <stdio.h>
#include <stdbool.h>
int main() {
int eggs_in_stock = 3;
int eggs_needed;
printf("Enter number of needed eggs\n");
scanf("%d", &eggs_needed);
if(eggs_in_stock >= eggs_needed) {
printf("You can make cake!\n");
}
}
Note
אין צורך להגדיר משתנה בוליאני
bool can_make_cake = eggs_in_stock >= eggs_needed- הקומפיילר מבצע casting לבוליאני באופן אוטומטי לכל ביטוי שנכתוב בתוך משפטif.
Lazy Evaluation
נזכיר כי בחישוב ביטוי בוליאני ניתן לדלג על חלק מהחישובים במידה והביטוי כבר ידוע, כמו במקרים x && false ו-x || true.
נבחן כדוגמא תוכנית שבוחנת עבור מספר כלשהו \(n\) האם המספר \(\frac{1}{n}\) קטן מ-\(\frac{1}{2}\):
#include <stdio.h>
#define UPPER_BOUND 0.5
int main() {
int n;
scanf("%d", &n);
if (n != 0) {
if ((1 / n) < UPPER_BOUND) {
printf("1 / %d is less than %.2lf\n", n, UPPER_BOUND);
}
}
}
נשים לב לבדיקה האם \(n\) אינו אפס על מנת להגן על התוכנית מחלוקה ב-0. ניתן להשתמש ב-lazy evaluation על מנת לפשט את התוכנית, ולכתוב במקום:
#include <stdio.h>
#define UPPER_BOUND 0.5
int main() {
int n;
scanf("%d", &n);
if (n != 0 && (1 / n) < UPPER_BOUND) {
printf("1 / %d is less than %.2lf\n", n, UPPER_BOUND);
}
}
במידה ו-n < 0, מגננון ה-lazy evaluation נכנס לפעולה ולכן החלק השני בתנאי לא יתבצע - ההגנה מתבצעת כך או כך.
כתיבה ללא סוגריים מסולסלים
השפה מאפשרת לכתוב משפט if ללא סוגריים מסולסלים, כך:
// Code before
if(eggs_in_stock >= eggs_needed)
printf("You can make cake!");
// Do more stuff after
למעשה, השפה מתייחסת לשורה הזו בתור בלוק בפני עצמה. הבעיה עם צורת הכתיבה הזו שהיא יכולה להוביל לקוד שנראה תקין אבל הוא שגוי לחלוטין - למשל בדוגמא הבאה:
if(eggs_in_stock >= eggs_needed)
printf("You can make cake!\n");
printf("Cake for everybody!");
// Do other stuff
ניתן לחשוב בגלל ההזחות שההדפסה השנייה תקרא רק אם יש מספיק ביצים - זה לא נכון! כפי שראינו, הקומפיילר אינו קורא בכלל את את ההזחות והרווחים שאנחנו מכניסים לקוד, ולכן במקרה זה אם תנאי ה-if מתקיים תתבצע ההדפסה הראשונה, אבל גם אם לא - מיד לאחר מכן תתבצע ההדפסה השנייה. למעשה, קטע הקוד הזה שקול לחלוטין לבא:
if(eggs_in_stock >= eggs_needed) {
printf("You can make cake!\n")
}
printf("Cake for everybody!")
// Do other stuff
זוהי שגיאה מאוד נפוצה - הרבה פעמים ניתן לכתוב משפט ללא סוגריים מסולסלים, ולאחר מכן להוסיף עוד שורה ולא לשים לב שאין אותם. לכן מומלץ מאוד תמיד להשתמש בהן, גם אם הבלוק שבתוך ה-if מכיל שורה אחת בלבד.