DCSIMG
Headaches with Prefix and Temporary Variables - Liran Chen's Blog

Liran Chen's Blog

.Net Internals, Debugging, Multithreading - and More!

Headaches with Prefix and Temporary Variables

לפני לא פחות מ-5 שנים, Luca Bolognese כתב פוסט שעסק בשאלה שעלתה ב-C# User Group. השאלה היתה מה יהיה הערך של x, בסוף ביצוע קטע הקוד הבא:

int x = 3;

x += x++;


אם אנחנו זוכרים את ההבדל בין Postfix ו-Prefix, אז לא צריכה להיות יותר מדי בעיה להבין שהתוצאה תהיה בסוף 6 מאחר ואין משמעות ל++ האחרון. כך שלמעשה, ניתן לפשט את הביטוי הזה ל: x = x + x, ועדיין נקבל את אותה ההתנהגות (שימו לב שזה המקרה ב-#C. ב-CPP למשל, אין הגדרה אמיתית לגבי מה הביטוי הזה צריך להחזיר).
אם כך, זאת נקודת הפתיחה שלנו. הרשתי לעצמי לקחת צעד אחד קדימה ולכתוב את שורת הקוד המאוד קריאה וברורה הזאת:

int x = 10;

x = --x + x + --x;


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

מה שמבלבל בביטוי הזה, הוא שאנחנו כל הזמן צריכים לעקוב היכן בזכרון נשמרים הערכים במהלך החישוב. האם הנתון נמצא במשתנה המקורי? האם ברג'יסטר של המעבד? או אולי בכלל במקום אחר? מה שחשוב לשים לב אליו כאן הוא שבמהלך החישוב אנחנו למעשה מקצים int נוסף שישב על ה-stack, וישמור את "תוצאת הביניים" של החישוב.
בצעד הראשון, אנחנו מורידים ב-1 את ערכו של x, ומעדכנים את המשתנה בזכרון, כלומר ברגע זה x=9. לאחר מכן, אנחנו מחברים את x ב-x. את התוצאה של החישוב הזה אנחנו למעשה נשמור במשתנה זמני נוסף שיוקצה במיוחד למטרה הזאת על ה-stack. שימו לב לא להתבלבל, אנחנו לא מעדכנים את ערכו של x במקרה הזה. לאחר מכן, אנחנו מורידים שוב ב-1 את ערכו של x (עכשיו ל-8), ואז מחברים אותו למשתנה הזמני ממקודם (שערכו 18). את התוצאה של החישוב האחרון הזה, נשמור בתוך x. כך קיבלנו בסופו של דבר את התוצאה 26.

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

00000019  mov  dword ptr [ebp-4],0Ah   // x = 10

00000020  dec  dword ptr [ebp-4]       // x = 9

00000023  mov  eax, dword ptr [ebp-4]  // x = 9, eax = 9

00000026  add  eax, dword ptr [ebp-4]  // x = 9, eax = 18

00000029  mov  dword ptr [ebp-8],eax   // x = 9, eax = 18, temp = 18

0000002c  dec  dword ptr [ebp-4]       // x = 8, eax = 18, temp = 18

0000002f  mov  eax, dword ptr [ebp-8]  // x = 8, eax = 18, temp = 18

00000032  add  dword ptr [ebp-4],eax   // x = 26

שלח תגובה

(שדה חובה)  

(שדה חובה)  

(אופציונלי)

(שדה חובה) 

Please add 2 and 4 and type the answer here:


Enter the numbers above: