ברוכים הבאים ליחידה "יסודות C: אופרטורים ובקרת זרימה" בקורס "מעבדה בתכנות מערכות" (20465). יחידה זו היא אבן יסוד בהבנת שפת C, שכן היא עוסקת במנגנונים הבסיסיים ביותר לבניית לוגיקה בתוכנה: כיצד לבצע פעולות על נתונים וכיצד לשלוט על סדר הביצוע של פקודות. שליטה באופרטורים, משפטי תנאי ולולאות חיונית לכתיבת קוד יעיל, קריא ונכון.
אופרטורים ב-C: הכלים לטיפול בנתונים
אופרטורים הם סמלים המורים למחשב לבצע פעולה מסוימת על אחד או יותר אופרנדים (ערכים או משתנים). שפת C עשירה באופרטורים, וחשוב להכיר את סוגיהם ואת סדר הקדימויות שלהם.
אופרטורים חשבוניים
מבצעים פעולות מתמטיות בסיסיות: חיבור (+), חיסור (-), כפל (*), חילוק (/), שארית (%). כוללים גם אופרטורי הגדלה (++) והקטנה (--) בקדם (prefix) או באחור (postfix).
אופרטורים יחסיים
משווים בין שני אופרנדים ומחזירים ערך בוליאני (אמת/שקר, ב-C מיוצג ע"י 1/0). שווה (==), שונה (!=), קטן מ-(<), גדול מ-(>), קטן או שווה (<=), גדול או שווה (>=).
אופרטורים לוגיים
משלבים ביטויים בוליאניים ומחזירים ערך בוליאני. וגם לוגי (&&), או לוגי (||), שלילה לוגית (!). חשוב להבין את עקרון "קיצור המעגל" (short-circuit evaluation).
אופרטורים ביט-ביט
מבצעים פעולות על רמת הסיביות של מספרים שלמים. וביט-ביט (&), או ביט-ביט (|), XOR ביט-ביט (^), היפוך סיביות (~), הזזה שמאלה (<<), הזזה ימינה (>>).
בקרת זרימה: משפטי תנאי
משפטי תנאי מאפשרים לתוכנית לקבל החלטות ולבצע בלוקי קוד שונים בהתאם למצב הנתונים.
if, else if, else
הצורה הגמישה ביותר לקבלת החלטות. מאפשרת לבדוק מגוון רחב של תנאים, כולל טווחים וביטויים לוגיים מורכבים. ניתן לשרשר מספר רב של else if.
switch
מתאים למצבים בהם יש לבחור מבין מספר ערכים בדידים של משתנה (שלם או תו). כל case מייצג ערך אפשרי, וחשוב להשתמש ב-break כדי למנוע "נפילה" (fall-through) ל-case הבא. default מטפל במקרים שלא תואמים לאף case.
if ובלולאות.בקרת זרימה: לולאות
לולאות מאפשרות לבצע בלוק קוד שוב ושוב, כל עוד תנאי מסוים מתקיים או מספר מוגדר של פעמים.
for
אידיאלית למצבים שבהם מספר האיטרציות ידוע מראש. כוללת שלושה חלקים: אתחול, תנאי סיום, ועדכון המונה, כולם מרוכזים בשורת ההגדרה של הלולאה.
while
מתאימה למצבים שבהם מספר האיטרציות אינו ידוע מראש, והלולאה ממשיכה כל עוד תנאי מסוים מתקיים. התנאי נבדק בתחילת כל איטרציה; אם הוא שקר, הלולאה לא תתבצע כלל.
do-while
דומה ל-while, אך התנאי נבדק בסוף כל איטרציה. משמעות הדבר היא שהקוד בתוך הלולאה יתבצע לפחות פעם אחת, גם אם התנאי שקר מלכתחילה.
פקודות break ו-continue מאפשרות לשלוט בזרימת הלולאה: break יוצא מהלולאה כליל, ו-continue מדלג לשלב הבא של הלולאה (בדיקת התנאי או עדכון המונה).
המרות טיפוסים (Type Conversions)
המרת טיפוסים היא תהליך שינוי טיפוס הנתונים של משתנה או ביטוי. ישנם שני סוגים עיקריים:
(type)expression. לדוגמה: (double)sum / count.double ל-int) יגרמו לאיבוד מידע (כמו החלק השברי). כמו כן, חילוק של שני מספרים שלמים ב-C תמיד יחזיר מספר שלם, גם אם התוצאה האמיתית היא שברית. לדוגמה, 5 / 2 ייתן 2, לא 2.5. כדי לקבל תוצאה מדויקת, יש לבצע המרה מפורשת לאחד האופרנדים: (double)5 / 2. שימו לב גם לבעיות שעלולות לצוץ בהשוואות בין טיפוסים עם סימן (signed) וללא סימן (unsigned).שאלות לדיון
- מתי תעדיפו להשתמש במבנה
if-else if-elseעל פניswitch, ולהיפך? תנו דוגמה לכל מקרה. - הסבירו את ההבדל העיקרי בין לולאת
whileללולאתdo-while. באיזה תרחיש לולאתdo-whileתהיה עדיפה? - מהי קדימות אופרטורים ומדוע היא כה חשובה בתכנות C? תנו דוגמה לביטוי שבו אי הבנה של קדימות תוביל לתוצאה שגויה.
- תארו תרחיש שבו המרת טיפוסים מרומזת עלולה להוביל לתוצאות בלתי צפויות או לשגיאות לוגיות. כיצד ניתן למנוע זאת באמצעות המרה מפורשת?
נקודות לתשובת מודל
if-else if-elseמולswitch:if-elseגמיש יותר, מתאים לבדיקת טווחים, ביטויים לוגיים מורכבים או טיפוסי נתונים שאינם שלמים.switchיעיל וקריא יותר לבדיקת ערכים בדידים של משתנה שלם/תו, אך דורשbreakלמניעת fall-through.whileמולdo-while:whileבודק את התנאי לפני ביצוע הלולאה (0 או יותר איטרציות).do-whileמבצע את הלולאה לפחות פעם אחת לפני בדיקת התנאי (1 או יותר איטרציות).do-whileעדיף כאשר אנו רוצים להבטיח ביצוע ראשוני, למשל בקריאת קלט מהמשתמש שחייב להתבצע לפחות פעם אחת.- קדימות אופרטורים: קובעת את סדר הפעולות בביטוי. חיונית לקבלת תוצאות נכונות. לדוגמה, בביטוי
a + b * c, הכפל יתבצע לפני החיבור. אי הבנה עלולה לגרום ל-(a + b) * cבמקוםa + (b * c). - בעיות בהמרות מרומזות: חילוק שלמים (
int / int) תמיד יקצץ את החלק השברי. לדוגמה,int x = 7; int y = 3; double result = x / y;ייתן2.0ולא2.333.... כדי לתקן:double result = (double)x / y;. בעיות נוספות עלולות לצוץ בהשוואות בין signed ל-unsigned, כאשר ערכים שליליים ב-signed הופכים לערכים חיוביים גדולים ב-unsigned.