המרת סוג (casting)
ראינו כי הערך 65 מפורש ע״י printf בצורה אחת כאשר הוא מודפס כמשתנה int, ואחרת כאשר הוא מודפס כמשתנה char. השאלה הנדרשת היא למה, והאם ניתן להמיר בין שתי ההצגות? למשל, האם ניתן לקחת משתנה int ולפרש מחדש את הבייטים בזכרון שלו בתור char? מסתבר שבאופן כללי כן, והמנגנון שמבצע זאת נקרא casting.
כפי שאמרנו בהתחלה, משתנה מורכב משני חלקים - ערך, שהוא המספר שכתוב בבתים בזכרון, וטיפוס, שהוא הוראה לקומפיילר כיצד לפרש את הערך. ראינו כי הערך 65 מפורש בצורה אחת ע“י הקומפיילר כאשר הוא נקרא כמשתנה int, ובצורה אחרת כאשר הוא נכתב כמשתנה char. לכן ניתן לשאול - האם אפשר להמיר ערך בין טיפוסים שונים? למשל, האם ניתן לקחת משתנה int ולפרש מחדש את הזכרון שלו בתור char?
התשובה היא כן, והמנגנון לבצע זאת נקרא casting. מצבעים זאת כך:
#include <stdio.h>
#include <stdbool.h>
int main() {
int x = 65;
float x_as_float = (float)x;
char x_as_char = (char)x;
bool x_as_bool = (bool)x;
printf("As int: %d\n", x);
printf("As float: %f\n", x_as_float);
printf("As char: %c\n", x_as_char);
printf("As bool: %d\n", x_as_bool);
}
כאשר נתון משתנה x, הביטוי (type1)x הוא מטיפוס type1 וערכו נקבע לפי ההמרה שמבצע הקומפיילר בין הטיפוסים השונים.
חוקי casting
ל-casting בין טיפוסים משתנים שונים יש חוקים רבים. נזכיר את העיקריים שבהם:
#include <stdbool.h>
#include <stdio.h>
int main(void) {
int i = 42;
float f = 3.75f;
double d = 3.1415926535;
char c = 'A';
bool b = true;
printf("=== Integer and floating point ===\n");
printf("int -> float: %d -> %.2f\n", i, (float)i);
printf("int -> double: %d -> %.5f\n", i, (double)i);
printf("float -> int: %.2f -> %d\n", f, (int)f);
printf("double -> int: %.5f -> %d\n", d, (int)d);
printf("double -> float: %.10f -> %.5f\n", d, (float)d);
printf("\n=== Truncation ===\n");
printf("(int) 3.99 = %d\n", (int)3.99);
printf("(int) 3.01 = %d\n", (int)3.01);
printf("(int) -3.99 = %d\n", (int)-3.99);
printf("\n=== Characters and integers ===\n");
printf("char -> int: '%c' -> %d\n", c, (int)c);
printf("int -> char: %d -> '%c'\n", 66, (char)66);
printf("\n=== Boolean conversions ===\n");
printf("bool -> int: false -> %d\n", (int)false);
printf("bool -> int: true -> %d\n", (int)true);
printf("int -> bool: 0 -> %d\n", (bool)0);
printf("int -> bool: 1 -> %d\n", (bool)1);
printf("int -> bool: 42 -> %d\n", (bool)42);
printf("int -> bool: -7 -> %d\n", (bool)-7);
printf("\n=== Floating point and boolean ===\n");
printf("float -> bool: 0.0 -> %d\n", (bool)0.0f);
printf("float -> bool: 3.14 -> %d\n", (bool)3.14f);
printf("float -> bool: -2.5 -> %d\n", (bool)-2.5f);
printf("bool -> float: false -> %.1f\n", (float)false);
printf("bool -> float: true -> %.1f\n", (float)true);
printf("\n=== A conversion during arithmetic ===\n");
int x = 5;
int y = 2;
printf("5 / 2 = %d\n", x / y);
printf("(float)5 / 2 = %.2f\n", (float)x / y);
return 0;
}
נסכם:
1. משתנה `int` המומר למשתנה `float/double` מומר בדרך כלל בצורה מדויקת עבור ערכים קטנים מספיק.
1. משתנה `float/double` המומר ל-`int` מעוגל כלפי מטה ולכן אינו מומר בצורה מדויקת.
1. משתנה `char` המומר ל-`int` הופך לערך ה-ASCII של התו - נסביר בפירוט מיד!
1. כל משתנה המומר ל-`bool` שאינו `0` (או `0.0` עבור משתני `double/float`) הוא `true`, ואם `0` בדיוק הוא `false`.
**הערה:* הקומפיילר מבצע במקרים מסוימים casting גם בלי שנבקש ממנו מפורשות. למשל, ההצהרות הנ״ל:
```c
int x = 5;
float y = x;
שקולות ל-:
int x = 5;
float y = (float)x;
או במשפט if שנפגוש בקרוב - כל ערך הניתן לו מומר אוטומטית ל-bool:
int x = 5;
if (x) { ... }
שקול ל-:
int x = 5;
if ((bool)x) { ... }