3.4 בקרת זרימה פתרון
תרגיל 1¶
כתבו תוכנית שמבקשת מהמשתמש להכניס מספר, ומדפיסה אם הוא חיובי, שלילי או אפס.
#include <stdio.h>
int main() {
int num;
printf("הכנס מספר: ");
scanf("%d", &num);
if (num > 0) {
printf("המספר חיובי\n");
} else if (num < 0) {
printf("המספר שלילי\n");
} else {
printf("המספר הוא אפס\n");
}
return 0;
}
תרגיל 2¶
כתבו תוכנית שמדפיסה את המספרים מ־10 עד 1 בספירה לאחור.
תרגיל 3¶
כתבו תוכנית שמבקשת מהמשתמש להכניס מספר, ואז מדפיסה את הטבלה הכפולה שלו (מ־1 עד 10).
#include <stdio.h>
int main() {
int num;
printf("הכנס מספר: ");
scanf("%d", &num);
int i = 1;
while (i <= 10) {
printf("%d x %d = %d\n", num, i, num * i);
i++;
}
return 0;
}
תרגיל 4¶
כתבו לולאה שסופרת עד 100, אבל מדפיסה רק את המספרים שמתחלקים ב־7.
#include <stdio.h>
int main() {
int i = 1;
while (i <= 100) {
if (i % 7 == 0) {
printf("%d\n", i);
}
i++;
}
return 0;
}
מצוין! הנה הסבר ברור ומדויק לקוד האסמבלי שנוצר עבור סעיף 4 (הדפסת כל המספרים בין 1 ל־100 שמתחלקים ב־7), לאחר קימפול עם GCC ואובייקט objdump -d:
סעיף 5 – הסבר על קוד האסמבלי¶
נניח שהקוד הבא קומפל ונוצר עבורו אסמבלי דומה לזה:
main:
push %rbp
mov %rsp, %rbp
mov $1, -4(%rbp) ; i = 1
.loop:
cmp $100, -4(%rbp) ; while (i <= 100)
jg .end ; אם i > 100 -> קפוץ לסיום
mov -4(%rbp), %eax ; eax = i
mov $7, %edx
xor %edx, %edx
div %edx ; eax / 7 -> rdx = שארית
cmp $0, %edx ; בדוק אם שארית 0
jne .skip ; אם לא מתחלק ב־7, דלג
; כאן תתבצע קריאה ל־printf עם i כפרמטר
.skip:
add $1, -4(%rbp) ; i++
jmp .loop
.end:
mov $0, %eax
pop %rbp
ret
הסבר שלב אחר שלב¶
| שלב בתוכנית C | אסמבלי | הסבר |
|---|---|---|
int i = 1; |
mov $1, -4(%rbp) |
שמור את המספר 1 במשתנה מקומי בזכרון (i) |
while (i <= 100) |
cmp $100, -4(%rbp)``jg .end |
השווה את i ל־100, אם עבר – צא מהלולאה |
if (i % 7 == 0) |
mov -4(%rbp), %eax``div %edx``cmp $0, %edx |
חלק את i ב־7, בדוק אם שארית היא אפס |
printf(...) |
קריאה לפונקציה חיצונית | תתבצע רק אם המספר מתחלק ב־7 |
i++; |
add $1, -4(%rbp) |
הגדל את i ב־1 |
while חוזר ל־התחלה |
jmp .loop |
חזור לתחילת הלולאה לבדיקה נוספת |
סיום התוכנית (return 0) |
mov $0, %eax``ret |
החזר ערך 0 מהפונקציה וסיים |
הערות מעניינות:¶
-
-4(%rbp)הוא המקום בזיכרון שמוקצה למשתנה המקומיi. -
divמשתמש תמיד ב־eaxכמספר שמחלקים אותו, ומחזיר את השארית ב־edx– משם נוכל לדעת אםi % 7 == 0. -
השימוש ב־
jne .skipמאפשר לדלג על ההדפסה אםiלא מתחלק ב־7. -
retהוא סוף של כל פונקציה – הוא מחזיר את הבקרה למי שקרא לפונקצייתmain.