בייטים, ביטים והצגת משתנים
כפי שהזכרנו, לאורך ריצה של תוכניות מחשב הן עושות שימוש ביחידה חומרתית הנקראת זכרון, בו נשמר המידע שהתוכנית מכילה. את הזכרון אנחנו מחלקים לחלקים הבאים:
- היחידה הבסיסית ביותר נקראת ביט (bit), או סיבית בעברית. ביט יכול לקבל את הערכים 0 או 1 בלבד - ניתן לחשוב על ביטים כמו מתגים, הנמצאים במצב דלוק או כבוי. בתוכנה נייצג כל ביט כספרה במספר בינארי אחד.
- כל קבוצה של 8 ביטים נקראת בייט (byte), או בית בעברית. כשאנחנו אומרים למשל שלמחשב שלנו יש זכרון של 4 ג׳יגהבייט (4GB), אלו הבייטים עליהם מדברים.
- כל משתנה שנגדיר יתפוס בזכרון כמות מסוימת של בייטים - לדוגמא, משתנה
charתופס בייט אחד, לעומת משתנהintשתופס 4 בייטים. - ניתן לחשוב על הזכרון בתור שורה ארוכה וממוספרת של המון בייטים. המספור שמקבל כל בייט נקרא כתובת. לדוגמא, אם הבייט בכתובת
100מכיל כרגע משתנה כלשהיint x, אנחנו אומרים שהכתובת שלxבזכרון היא100. בהמשך נעשה שימוש מקיף בנושא הכתובות של משתנים בזכרון. רוב המשתנים יתפסו יותר מבייט אחד בזכרון, ובמקרה זה אנחנו אומרים שהכתובת שלהם היא הכתובת של הבייט הראשון שהם נמצאים בו. - הביטים שמרכיבים כל בייט נותנים לו ערך, וביחד כל משתנה מפורש לפי הבייטים שלו. ניקח משתנה
intלמשל שערכו 549:
int x = 549;
ראינו כי היצוג הבינארי של המספר הדצימלי 549 הוא 1000100101. לכן ארבעת הבייטים של המשתנה x יראו כך, עם רווח בין בייט לבייט:
00000000 00000000 00000010 00100101
נסו להמיר בעצמכם את המספר בייט לאחר בייט לספרות דצימליות וראו שאתם מקבלים את התשובה הרצויה!
כלומר: כשאנו כותבים את שורת הקוד int x = 549, הקומפיילר שומר את הבייטים 00000000 00000000 00000010 00100101 במקום שהורנו לו. הבנה עמוקה של כיצד הזכרון נראה היא לא פחות מקריטית להמשך ולכן נשקיע מאמץ רב בבנייה עמוקה שלה.
שאלה: אמרנו שגודל משתנה int הוא 4 בייטים - כלומר, הערך המספרי של משתנה מסוג זה מיוצג ע״י 4 בייטים שהם 32 ביטים. בהתאמה, מהו הערך המקסימלי שמשתנה מסוג זה יכול לקבל?
כתובת של משתנה
דיברנו על כך שהזכרון הוא שורה ארוכה וממוספרת של בייטים, כאשר כל הערך של כל משתנה נכתב לבייטים שהוא נמצא בהם. כתובת של משתנה היא המספר של הבייט הראשון שלו בשורה הארוכה הזו, והיא פשוט מספר שאפשר לקבל בקלות מהמשתנה עצמו:
#include <stdio.h>
int main() {
int x = 5;
char c = 'c';
float f = -53.4;
printf("x address = %p\n", &x);
printf("c address = %p\n", &c);
printf("f address = %p\n", &f);
}
אופרטור ה-& לפני משתנה מחזיר את הכתובת שלו בזכרון. ההדפסה נראת מעט מרתיעה מכיוון שהיא נעשת בבסיס אחר שלא נפגוש בקורס הנקרא הקסדצימלי (hexadecimal), אבל מדובר במספר לכל דבר.
בהמשך נעשה שימוש נרחב באופרטור זה - כעת אנו יודעים שכל משמעותו היא לקיחת הכתובת של המשתנה המדובר בזכרון.
Note
בהמשך כשנדבר על מצביעים נדון בנושא זה בהרחבה - כעת אנו מציגים את אופרטור הכתובת לשלמות בלבד.
sizeof וגדלים של משתנים
השפה מעמידה לרשותנו אופרטור בשם sizeof, שמקבל טיפוס ומחזיר את הגודל שלו בבתים:
#include <stdio.h>
#include <stdbool.h>
int main() {
printf("Size of char: %d bytes\n", sizeof(char));
printf("Size of bool: %d bytes\n", sizeof(bool));
printf("Size of int: %d bytes\n", sizeof(int));
printf("Size of float: %d bytes\n", sizeof(float));
printf("Size of double: %d bytes\n", sizeof(double));
}
Caution
כאשר אנו אומרים ש-
intתופס 4 בתים ו-charתופס בית אחד, זהו המקרה הרווח במחשבים מודרנים. עם זאת, קיימים יוצאי דופן - בפלטפורמות שונות יתכנו גדלים שונים של משתנים. כך או כך, אין צורך לזכור מהו גודל משתנה בשום שלב - המטרה היא להבין כיצד תמונת הזכרון מתנהגת.