Menu

טיפוסי נתונים ב-C: int, float, char, double ועוד

לכל משתנה ב-C יש טיפוס שקובע את הגודל שלו ומה הוא יכול להכיל. הנה כל הטיפוסים הבסיסיים, הגדלים האמיתיים שלהם, signed מול unsigned, sizeof, limits.h, ומה קורה בגלישה.

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

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

רשימת הטיפוסים של C קצרה. המורכבות נמצאת במגדירים.

ארבעת טיפוסי הבסיס

  • char מחזיק בית אחד. משתמשים בו לתווים בודדים ('A'), לבתים של מחרוזת, ולפעמים כמספר שלם זעיר.
  • int הוא סוס העבודה של המספרים השלמים. מוני לולאות, גדלים, מזהים.
  • float ו-double מחזיקים מספרים ממשיים. ל-double יש בערך פי שניים דיוק.
  • void הוא במובן מסוים הטיפוס הרביעי, אבל פירושו "אין ערך": אי אפשר להצהיר על משתנה void, רק להשתמש בו כטיפוס החזרה של פונקציה, כרשימת פרמטרים ריקה או כמצביע בלי טיפוס.

שימו לב לסיומת f ב-3.14f. בלעדיה הערך המילולי הוא double, והשמתו ל-float מאבדת דיוק בשקט. חלק מהקומפיילרים מזהירים על זה.

מגדירים: short, long, unsigned

טיפוסי השלמים הבסיסיים מגיעים עם מגדירים שמשנים את הגודל או את הסימן.

כל טיפוס שלם צריך מציין פורמט משלו: %hd ל-short, %d ל-int, %ld ל-long, %lld ל-long long, %u ל-unsigned. שימוש במציין הלא נכון הוא התנהגות לא מוגדרת, לא שגיאת עיגול.

unsigned מסיר את ביט הסימן ומכפיל את הטווח החיובי. unsigned int מחזיק 0 עד בערך 4.3 מיליארד במקום ממינוס 2.1 עד פלוס 2.1 מיליארד. זו הבחירה הנכונה לדברים שבאמת לא יכולים להיות שליליים, גדלים, ספירות של בתים, תבניות ביטים, ומלכודת לכל דבר שעלול לרדת מתחת לאפס בחיסור.

signed היא ברירת המחדל עבור int, short, long ו-long long, כך שכמעט אף פעם לא כותבים אותה. המקום היחיד שבו זה משנה הוא char: האם char רגיל הוא עם סימן או בלי תלוי מימוש, אז כתבו signed char או unsigned char כשהסימן משנה.

גדלים: מה מקבלים בפועל

תקן C מגדיר מינימום, לא גדלים מדויקים. בכל מכונת Linux, macOS או Windows מודרנית של 64 ביט תראו את זה:

טיפוסגודל טיפוסיטווח טיפוסי
charבית אחדמינוס 128 עד 127 (או 0 עד 255)
short2 בתיםמינוס 32,768 עד 32,767
int4 בתיםמינוס 2,147,483,648 עד 2,147,483,647
long8 בתים (4 ב-Windows)בערך ±9.2 קווינטיליון
long long8 בתיםבערך ±9.2 קווינטיליון
float4 בתיםכ-7 ספרות משמעותיות
double8 בתיםכ-15 ספרות משמעותיות
long double16 בתים (משתנה)יותר מ-double

שורת ה-long היא זו שתופסת אנשים: היא 8 בתים ב-Linux וב-macOS, ו-4 בתים ב-Windows של 64 ביט. קוד שמניח ש-long יכול להחזיק ערך של 64 ביט אינו נייד. השתמשו ב-long long, או בטיפוסים ברוחב מדויק מ-stdint.h (int32_t, uint64_t) כשהגודל הוא חלק מהדרישה.

sizeof: שאלו את הקומפיילר

לעולם אל תנחשו גודל, מדדו אותו:

sizeof הוא אופרטור, לא פונקציה, והוא מחושב בזמן קומפילציה. הוא מחזיר ערך מטיפוס size_t, שמודפס עם %zu.

sizeof(char) מובטח להיות בדיוק 1: זו ההגדרה של בית ב-C. כל השאר נמדד ביחס אליו.

limits.h ו-float.h

הטווחים המדויקים של הקומפיילר שלכם זמינים כקבועים עם שמות:

אלה הערכים שמולם משווים כשצריך לדעת אם פעולה תגלוש. בדיקת if (a > INT_MAX - b) לפני חישוב a + b היא הדרך לזהות גלישה לפני שהיא קורית, וזה חשוב, כי אי אפשר לזהות גלישה של ערך עם סימן אחרי המעשה.

גלישת מספרים שלמים

מה שקורה כשערך חורג מהטווח של הטיפוס שלו תלוי לגמרי בסימן.

גלישה של ערך בלי סימן מוגדרת: הערך מתגלגל מודולו 2^N.

המקרה השני הוא מקור אמיתי לבאגים. לולאה כמו for (unsigned i = n - 1; i >= 0; i--) לעולם לא נגמרת, כי ערך בלי סימן הוא תמיד >= 0.

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

int sum = a + b;
if (sum < a) { /* the compiler may remove this entirely */ }

בדקו לפני, בעזרת הגבולות:

if (b > 0 && a > INT_MAX - b) {
    /* a + b would overflow - handle it */
}

דיוק של נקודה צפה

float ו-double מאחסנים מספרים בבינארי, ולרוב השברים העשרוניים אין צורה בינארית מדויקת, בדיוק כמו של-1/3 אין צורה עשרונית מדויקת.

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

ולעולם אל תשתמשו בנקודה צפה לכסף. אחסנו אגורות כמספר שלם; long long של אגורות מדויק, בעוד ש-double של שקלים לא.

בחירת טיפוס

רשימת החלטות קצרה שמכסה את רוב הקוד:

  • מספרים שלמים: int, אלא אם יש לכם סיבה. זה הטיפוס שהמעבד מטפל בו ביעילות הגבוהה ביותר, והטיפוס שכל כללי החשבון בנויים סביבו.
  • כל דבר גדול מ-2 מיליארד: long long, או int64_t מ-stdint.h.
  • גדלים, אורכים ואינדקסים של מערכים מ-sizeof או מ-strlen: size_t. הוא בלי סימן ומובטח להיות גדול מספיק לכל אובייקט.
  • מספרים עשרוניים: double. השתמשו ב-float רק כדי לחצות את הזיכרון במערכים גדולים או בחומרה משובצת בלי יחידה לדיוק כפול.
  • תווים בודדים ובתים גולמיים: char לטקסט, unsigned char לנתונים בינאריים.
  • אמת/שקר: bool מ-stdbool.h; ראו ערכים בוליאניים ב-C.
  • רוחב ביטים מדויק (פורמטים של קבצים, פרוטוקולי רשת, רגיסטרים של חומרה): stdint.h, כלומר uint8_t, int16_t, uint32_t וכן הלאה.

ערבוב טיפוסים

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

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

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

שאלות נפוצות

מה הם טיפוסי הנתונים הבסיסיים ב-C?

ארבעה טיפוסי בסיס: char לתווים בודדים ולבתים, int למספרים שלמים, float ו-double למספרים עשרוניים. מגדירים משנים את הגודל והסימן שלהם, short, long, long long, signed ו-unsigned, וכך נוצרת הקבוצה המלאה.

כמה בתים יש ב-int ב-C?

כמעט תמיד 4 בתים (32 ביט) במערכות שולחניות ושרתים מודרניים, וזה נותן טווח של בערך מינוס 2.1 מיליארד עד 2.1 מיליארד. התקן מבטיח רק לפחות 2 בתים, ובמיקרו-בקרים של 16 ביט באמת משתמשים ב-2. השתמשו ב-sizeof(int) אם צריך לדעת בוודאות.

מה ההבדל בין float ל-double ב-C?

float הוא 4 בתים עם בערך 7 ספרות עשרוניות משמעותיות; double הוא 8 בתים עם בערך 15. double היא ברירת המחדל לערכים מילוליים של נקודה צפה ולפונקציות מתמטיות, ואלא אם אתם מאחסנים מיליוני ערכים או מכוונים לשבב משובץ, double היא הבחירה הנכונה.

מה קורה כש-int גולש ב-C?

עבור int עם סימן, גלישה היא התנהגות לא מוגדרת: הקומפיילר עשוי להתגלגל, לרוות או להעלים את הבדיקה לגמרי באופטימיזציה. עבור int בלי סימן היא מוגדרת לגמרי: הערך מתגלגל מודולו 2^N, כך ש-UINT_MAX + 1 הוא 0. לעולם אל תסמכו על גלישה של ערך עם סימן.

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

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

להתחיל