Menu

משתנים ב-C++: הצהרה, אתחול ו-scope

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

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

מה זה משתנה

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

להצהרה יש שלושה חלקים: הטיפוס, השם, ו(כמעט תמיד) ערך התחלתי.

קראו את int age = 30; כ"ליצור int בשם age ולשים בו 30". הנקודה-פסיק מסיימת את המשפט, בדיוק כמו שהדף על הערות הראה שכל משפט מסתיים. שימו לב ש-isActive הודפס כ-1: bool מוצג כברירת מחדל כ-1/0, משהו שהדף על טיפוסי נתונים מרחיב עליו.

אתחול מול השמה

השניים נראים דומים אבל הם פעולות שונות, וההבדל הוא אחד הרעיונות החשובים ביותר בדף הזה.

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

אפשר להצהיר בלי לאתחל ולהציב ערך מאוחר יותר, אבל היזהרו:

זה עובד כי הצבנו ערך ב-score לפני שקראנו אותו. הגרסה המסוכנת היא לקרוא אותו קודם, ועליה בסעיף הבא.

מלכודת המשתנה הלא מאותחל

זו המלכודת הקלאסית של C++ שלא קיימת בהרבה שפות אחרות. קריאה של משתנה מקומי שהוצהר אבל אף פעם לא קיבל ערך היא undefined behavior: המשתנה מחזיק את הבתים שבמקרה היו בזיכרון הזה.

int score;             // לא מאותחל
std::cout << score;    // התנהגות לא מוגדרת: מדפיס זבל, או "עובד", או קורס

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

  • תמיד אתחלו בהצהרה. int score = 0; לא עולה כלום ומעלים את הבעיה כולה.
  • הפעילו אזהרות. קומפילציה עם -Wall -Wextra גורמת למהדר לסמן הרבה קריאות לא מאותחלות לפני שהן נושכות אתכם.

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

סגנונות אתחול: =, () ו-{}

C++ נותנת כמה דרכים לכתוב ערך התחלתי. ברוב המקרים הן עושות אותו דבר, אבל לאתחול עם סוגריים מסולסלים יש תכונת בטיחות נוספת ששווה להכיר.

הסיבה לבחור ב-{} היא שהוא דוחה המרות narrowing, כלומר השמות שהיו מאבדות נתונים בשקט. עם =, ערך עשרוני נקטע בשקט; עם {}, המהדר עוצר אתכם:

int x = 3.9;    // מתקמפל: x הופך בשקט ל-3 (ה-.9 נזרק)
int y{3.9};     // שגיאת קומפילציה: narrowing מ-double ל-int אסור

לתפוס את זה בזמן קומפילציה זה בדיוק מה שרוצים. הרגל מודרני נפוץ הוא להשתמש ב-{} כברירת מחדל בשביל הבדיקה הנוספת הזו, ולחזור ל-= רק כשאתחול העתקה קריא יותר.

כללי שמות ומוסכמות

C++ אוכפת כמה כללים נוקשים, ומעליהם כולם מוסיפים מוסכמות. הכללים: שם יכול להכיל אותיות, ספרות ו-_; הוא לא יכול להתחיל בספרה; הוא לא יכול להיות מילה שמורה (כמו int או return); והוא רגיש לאותיות גדולות וקטנות (age ו-Age הם שני משתנים שונים). הימנעו משמות שמתחילים בקו תחתון ואחריו אות גדולה, או שמכילים שני קווים תחתונים ברצף: הם שמורים למימוש.

המוסכמות שרוב קוד ה-C++ עוקב אחריהן:

  • משתנים נכתבים ב-snake_case או ב-camelCase: בחרו אחד והיו עקביים, item_count או itemCount.
  • שמות צריכים לתאר את הערך: count, לא c; user_email, לא x.

שמות ברורים הם לא קישוט: הם הדרך שבה אתם בעתיד תקראו את הקוד. total_price = item_count * price_per_item מסביר את עצמו בצורה ש-t = c * p אף פעם לא יעשה.

Scope של משתנים

משתנה קיים רק בתוך הבלוק, ה-{ ... }, שבו הוא הוצהר, והוא מושמד בסוגר המסולסל הסוגר. זה ה-scope (טווח ההכרה) שלו. משתנה שהוצהר בתוך לולאה או בלוק if לא נראה מחוץ לו:

גם i וגם square שייכים ללולאה ונעלמים כשהיא מסתיימת; total הוצהר בבלוק החיצוני, אז הוא שורד. בלוק פנימי יכול גם להסתיר (shadow) שם מבלוק חיצוני: משתנה חדש עם אותו שם מסתיר זמנית את החיצוני עד שהבלוק הפנימי נסגר. זה מקור נפוץ לבלבול, אז הימנעו משימוש חוזר בשמות ב-scopes מקוננים.

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

הבא בתור: טיפוסי נתונים

כל משתנה בדף הזה התחיל עם טיפוס: int, double, string, bool. הדף הבא מפרק לעומק את טיפוסי הנתונים של C++: הטיפוסים הבסיסיים והגדלים שלהם, מספרים שלמים signed מול unsigned, float מול double, char, ואיך לבחור את הטיפוס הנכון למשימה.

שאלות נפוצות

איך מצהירים על משתנה ב-C++?

כותבים את הטיפוס, אחריו שם, ואפשר גם לתת ערך: int age = 30;. הטיפוס (int) קבוע לכל חיי המשתנה; השם (age) הוא הדרך להתייחס אליו. אפשר להצהיר בלי ערך, int age;, אבל במשתנה מקומי זה משאיר בו זבל עד שתציבו ערך, אז תמיד אתחלו בנקודת ההצהרה.

מה ההבדל בין אתחול להשמה ב-C++?

אתחול נותן למשתנה את הערך הראשון שלו כחלק מההצהרה: int x = 5; או int x{5};. השמה משנה את הערך של משתנה שכבר קיים: x = 7;. ההבחנה חשובה כי קריאה של משתנה מקומי שהוצהר אבל אף פעם לא אותחל היא התנהגות לא מוגדרת.

מה קורה אם משתמשים במשתנה לא מאותחל ב-C++?

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

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל