10.6 - varargs מתחת למכסה המנוע - תרגול
התרגילים בשיעור הזה מניחים סביבת x86-64 על לינוקס. אם אתם עובדים על מכונת arm64 (למשל מק עם Apple Silicon), הריצו אותם בתוך מכונה וירטואלית או קונטיינר של x86-64, אחרת המבנה של
va_listיהיה שונה לגמרי.
תרגיל 1 - חיזוי מיקום הארגומנטים¶
נתונה הפונקציה והקריאה הבאות:
ענו על השאלות הבאות על הנייר, לפני שאתם מריצים משהו:
- באיזה רגיסטר יעבור כל אחד מששת הארגומנטים?
- מה יהיה הערך של
alבקריאה הזו? - מה יהיה הערך ההתחלתי של
gp_offsetושלfp_offsetאחריva_start(ap, count)? - אחרי שקראנו
va_arg(ap, int)פעם אחת, מה יהיהgp_offset?
אחר כך אמתו את עצמכם: כתבו את הקוד, קמפלו עם gcc -O0 -S, ובדקו את הפרולוג ואת האתחול של ap.
תרגיל 2 - להסתכל על va_list מבפנים¶
כתוב תוכנית שמדפיסה את השדות של va_list לפני ואחרי כל קריאה ל-va_arg, כדי לראות במו עיניכם את המונים מתקדמים.
הגדירו בעצמכם מבנה שתואם ל-va_list של x86-64, ועשו cast אליו:
typedef struct {
unsigned int gp_offset;
unsigned int fp_offset;
void *overflow_arg_area;
void *reg_save_area;
} my_va_list;
הפונקציה שלכם צריכה לקבל מספר משתנה של ארגומנטים שלמים, ולהדפיס אחרי כל va_arg את gp_offset הנוכחי.
דוגמה להרצה:
initial: gp_offset=8 fp_offset=48
after #1: gp_offset=16 value=10
after #2: gp_offset=24 value=20
after #3: gp_offset=32 value=30
תרגיל 3 - למה צריך va_copy¶
כתוב פונקציה void print_normalized(int count, ...) שמקבלת count מספרים שלמים ומדפיסה כל אחד מהם כאחוז מהמקסימום שברשימה.
הקושי: כדי לחשב אחוזים צריך קודם למצוא את המקסימום - כלומר צריך לעבור על הארגומנטים פעמיים.
נסו קודם לעבור פעמיים על אותו va_list בלי va_copy, וראו מה קורה. אחר כך תקנו עם va_copy.
דוגמה להרצה: