שפת C/פעולות חשבוניות

דף זה מסביר את פעולות החשבון הבסיסיות שהן חלק מהשפה. שפת C קטנה מאד, וכוללת את פעולות החשבון הבסיסיות ביותר (כמו חיבור או כפל). פעולות חשבוניות מתקדמות יותר, לדוגמה פעולות טריגונומטריות, אינן חלק מהשפה אלא שייכות לספריות השפה.


הפעולות הבסיסיות

עריכה
int x = 4;
int y = 2;

/* Prints 4 + 2 = 6 */
printf("%d + %d = %d\n", x, y, x + y);

/* Prints 4 - 2 = 2 */
printf("%d - %d = %d\n", x, y, x - y);

/* Prints 4 * 2 = 8 */
printf("%d * %d = %d\n", x, y, x * y);

/* Prints 4 / 2 = 2 */
printf("%d / %d = %d\n", x, y, x / y);

/* Prints 4 % 2 = 0 */
printf("%d /% %d = %d\n", x, y, x % y);


אפשר לבצע פעולות חשבוניות על מספרים, משתנים, או כל שילוב של משתנים ומספרים:

int x = 2, y = 3;

/* Prints 13 */
printf("%d\n", x + y + 3 + 5);

סדר פעולות החשבון

עריכה

סדר פעולות החשבון בשפת C הוא המקובל באלגברה בסיסית, ולכן כפל (*), לדוגמה, מבוצע לפני חיבור (+). השורה הבאה, לדוגמה, תדפיס 17:

printf("%d\n", 2 + 3 * 5);

בדיוק כבאלגברה בסיסית, ניתן להשתמש בסוגריים כדי לציין סדר פעולות שונה. השורה הבאה, לדוגמה, תדפיס 25:

printf("%d\n", (2 + 3) * 5);

השמת ערכים

עריכה

השמה ואתחול

עריכה

כפי שראינו במשתנים, אפשר להשתמש בסימן = להשמה ואתחול.

int x = 2, y = 3;

int z = x + y + 5;

חשוב להבין מה קורה כאן בשורה השניה. ראשית מעריכים את הביטוי x + y + 5 (ערכו כאן 10). משימים ערך זה למשתנה z. אפשר גם להשים למשתנה ערך חדש שתלוי בערכו הקודם. נתבונן לדוגמה בשורה

x = x + 2;

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

סימני קיצור בהשמה עצמית

עריכה

כפי שראינו:

x = x + 2;

משמעו השמה עצמית של x לערכו הקודם ועוד משהו (2 במקרה זה). בפועל, סוג זה של השמה עצמית נפוץ מאד. שפת C ידועה בקצרנותה הרבה לביטויים נפוצים. אפשר לכתוב את הביטוי הקודם גם באופן הבא, הקצר יותר:

x += 2;

המשמעות כאן זהה לחלוטין: מעריכים את x + 2, ומשימים את הערך לx.

שפת C כוללת סימונים מקוצרים להשמות עצמיות לכל חמש פעולות החשבון הבסיסיות:

/* x = x + 1 */
x += 1;

/* y = y - 3 */
y -= 3;

/* z = z * 8 */
z *= 8;

/* w = w / 4 */
w /= 4;

/* p = p % 2 */
p %= 2;

הגדלה עצמית והקטנה עצמית

עריכה

הגדלה עצמית

עריכה

נניח שאנו רוצים לקדם את x ב1. כבר ראינו שאפשר לרשום זאת כך:

x = x + 1

או, באופן קצר יותר, כך:

x += 1;

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

x++;

או כך:

++x;

(נעמוד על ההבדלים בין שתי הצורות בחלק הבא - הגדלה עצמית והקטנה עצמית לכתחילה ובדיעבד.)

הקטנה עצמית

עריכה

באותו אופן כהגדלה עצמית, ניתן להוריד 1 מערך משתנה כך:

x--;

או כך:

--x;

הגדלה עצמית והקטנה עצמית לכתחילה ובדיעבד

עריכה

לעתים, קיים ביטוי בו משתנה הן מקודם והן מוערך. נניח, לדוגמה, שx מכיל את הערך 3, ונתבונן בשורה:

z = x++;

שני דברים מתבצעים כאן:

  • x מקודם ב-1
  • z מקבל ערך כלשהו

השאלה היא, אבל, מה קודם למה. אם קודם x מקודם, אז בסיום השורה z יכיל את הערך 4. מצד שני, אם קודם z מקבל ערך, אז בסיום השורה z יכיל את הערך 3 (כי x קודם ל-4 רק אחרי שz קיבל את ערכו הקודם). לצורך כך מכילה שפת C הן הגדלה עצמית לכתחילה, והן הגדלה עצמית בדיעבד. משמעות הגדלה עצמית בדיעבד (post-increment) היא 'הערך את x ורק אז קדם אותו'.

x++

לעומת זאת, משמעות הגדלה עצמית לכתחילה (pre-increment) היא 'קדם את x והערך את התוצאה'.

++x
הדבר דומה להקטנה עצמית בדיעבד (post-decrement)
x--

לעומת הקטנה עצמית לכתחילה (pre-decrement)

--x

פעולות חשבוניות על שלמים ונקודות צפות

עריכה

אלגברה וחישובים שלמים

עריכה

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

int x = 3, y = 8;

printf("The average is %f\n", (x + y) / 2);

אם נהדר ונריץ את הקוד, נראה שהממוצע המודפס הוא 5.0000, ולא 5.5 = 2 / (3 + 8) כפי שהיינו מצפים בצורה אלגברית.

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

  1. (x + y) פועלת על מספרים שלמים (שכן הן x והן y הם שלמים), ולכן התוצאה מסוג שלם.
  2. ‎(x + y)/2 פועלת על שלמים (כבר ראינו ש-(x + y) היא שלם, ו-2 הוא שלם), ולכן התוצאה מסוג שלם.
  3. המספר השלם 5 מומר למספר הצף 5.0000 ומודפס.

המרות

עריכה

לעתים אנו מחזיקים ערך, נניח 3, המיוצג כטיפוס כלשהו (נניח char), אך משום מה יש להמיר ערך זה לטיפוס אחר (נניח int). מה קורה במקרה זה? הדבר תלוי בשאלה האם מידע אובד פוטנציאלית, או לא.

המרות מרחיבות

עריכה

נתבונן בקטע הקוד הבא:

char x = 3;

int y = x;

בשורה הראשונה מושם הערך 3 למשתנה x, שהוא מסוג תו. השורה הבאה משימה את ערכו של x לתוך המשתנה y, שהוא מסוג שלם. האם מידע יכול לאבוד כאן? לא, מפני שתחום הערכים שיכול להכיל int כולל את תחום הערכים שיכול להכיל char. המרה זו נטולת בעיות, מפני שאנו ממירים ערך במשתנה בעל תחום קטן, במשתנה בעל תחום רחב יותר.

באותו אופן, ובדיוק מאותה סיבה, אין בעיה בהמרה מint לfloat, לדוגמה.

המרות מצרות

עריכה

נתבונן בקטע הקוד הבא:

int x = 3;

char y = x;

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


חישובים מעורבים

עריכה

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

int x = 2, y = 3;

printf("The average is %f\n", (x + y) / 2.0);

קטע קוד זה מדפיס שהממוצע הוא 2.5, כנדרש. היות ש-2.0 הוא משתנה נקודה צפה, המהדר יבצע המרה מרחיבה כך ש-x + y יהיה נקודה צפה, על אף שהן x והן y שלמים (ועל כן סכומם אמור היה להיות שלם).

דוגמה: המרה בין סוגי מעלות שונים

עריכה

להלן דוגמה פשוטה מאד, שבה נשתמש גם בלולאות ופונקציות.

נניח שc מייצג טמפרטורה נתונה במעלות בשיטת Celsius, ואנו רוצים למצוא את f, המעלות בשיטת Fahrenheit. על פי נוסחה ידועה, f הוא 9 / 5 * c + 32. נניח גם שהדיוק אינו חשוב לנו במיוחד, ואנו מוכנים לעבוד במספרים שלמים (על אף שגיאת העיגול). להלן תכנית המקבלת כקלט מעלה בCelsius , ומדפיסה אותו בFahrenheit:

#include <stdio.h>

int main()
{
  double c, f;
  
  printf("Enter degrees in Celsius: ");
  scanf("%lf", &c);
  
  f = 9 / 5 * c + 32;
  
  printf("This is %lf in Fahrenheit\n", f);

  return 0;
}

להפתעתנו (או לא), התוכנית פשוט תפלוט תמיד את c + 32, שהיא שגיאה שחורגת בהרבה מסתם שגיאת עיגול. מדוע הדבר קורה? ראינו בפעולות חשבוניות על שלמים ונקודות צפות שכל פעולה על טיפוסים שלמים מניבה תמיד תוצאה מסוג שלם. 9 / 5, לכן, מתורגם ל-1, ולכן מקבלים 1 * c + 32 בפועל.

נוכל לתקן זאת על ידי כך שנחליף את 9 / 5 ב-1.8, שהוא מספר נקודה צפה:

  f = 1.8 * c + 32;

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


הפרק הקודם:
פלט וקלט
פעולות חשבוניות
תרגילים
הפרק הבא:
ביטויים בוליאניים ותנאים