3.3 - פוינטרים - פתרון
📘 פתרון תרגול – פוינטרים, מחרוזות, מערכים ו־scanf¶
1. קלט מספר שלם מהמשתמש¶
#include <stdio.h>
int main() {
int a;
printf("Enter a number:\n");
scanf("%d", &a);
printf("The number you entered is: %d\n", a);
return 0;
}
2. קלט אות מהמשתמש, הדפסת ערך ה־ASCII שלה¶
#include <stdio.h>
int main() {
char c;
printf("Enter a letter:\n");
scanf(" %c", &c); // note the space before %c to skip whitespace characters
printf("The ASCII value of %c is: %d\n", c, c);
return 0;
}
3. הדפסת מחרוזת וכתובת בזיכרון¶
#include <stdio.h>
int main() {
char* str = "hello";
printf("The string: %s\n", str);
printf("Address in memory: %p\n", str);
return 0;
}
4. קלט של שלושה מספרים לתוך מערך¶
#include <stdio.h>
int main() {
int arr[3];
printf("Enter three numbers:\n");
scanf("%d", &arr[0]);
scanf("%d", &arr[1]);
scanf("%d", &arr[2]);
printf("The numbers are: %d, %d, %d\n", arr[0], arr[1], arr[2]);
return 0;
}
5. שימוש בפוינטר לגישה למערך¶
#include <stdio.h>
int main() {
int arr[3] = {10, 20, 30};
int* p = arr;
printf("arr[0] = %d, *(p+0) = %d\n", arr[0], *(p + 0));
printf("arr[1] = %d, *(p+1) = %d\n", arr[1], *(p + 1));
printf("arr[2] = %d, *(p+2) = %d\n", arr[2], *(p + 2));
return 0;
}
6. קלט מחרוזת מהמשתמש¶
#include <stdio.h>
int main() {
char name[100];
printf("Enter a word (string without spaces):\n");
scanf("%s", name);
printf("You entered the string: %s\n", name);
return 0;
}
7. הדפסת argc ו־argv¶
#include <stdio.h>
int main(int argc, char** argv) {
printf("Number of arguments: %d\n", argc);
printf("Program name: %s\n", argv[0]);
if (argc > 1) {
printf("First argument: %s\n", argv[1]);
}
return 0;
}
8. הדפסת התו הראשון, השני והשלישי של הארגומנט הראשון¶
#include <stdio.h>
int main(int argc, char** argv) {
if (argc > 1) {
char* p = argv[1];
printf("First character: %c\n", p[0]);
printf("Second character: %c\n", p[1]);
printf("Third character: %c\n", p[2]);
} else {
printf("No first argument was entered\n");
}
return 0;
}
💡 תרגיל בונוס – שימוש ב־scanf עם כמה סוגים¶
#include <stdio.h>
int main() {
char name[100];
int age;
int fav;
printf("Enter name:\n");
scanf("%s", name);
printf("Enter age:\n");
scanf("%d", &age);
printf("Enter favorite number:\n");
scanf("%d", &fav);
printf("Hello %s, you are %d years old and your favorite number is %d\n", name, age, fav);
return 0;
}
Let's take the following code:
#include <stdio.h>
int main() {
char name[100];
int age;
int fav;
printf("Enter name:\n");
scanf("%s", name);
printf("Enter age:\n");
scanf("%d", &age);
printf("Enter favorite number:\n");
scanf("%d", &fav);
printf("Hello %s, you are %d years old and your favorite number is %d\n", name, age, fav);
return 0;
}
Let's assume we compiled with:
שימו לב ש-g הוא דגל debug בgcc, ונותן לנו עוד מידע בקובץ האובייקט שיכול לשמש אותנו שאנחנו חוקרים את התוכנה עם objdump.
ונשתמש ב־objdump -d bonus > bonus.asm כדי לראות את קוד האסמבלי שנוצר.
חלק מהאסמבלי שיתקבל (מפושט לצורך הסבר):¶
push ebp
mov ebp, esp
sub esp, 0x70 ; allocates space for name, age, fav (100 bytes + 4 + 4)
lea eax, [ebp-0x64] ; address of name
push eax
push offset .LC0 ; format "%s"
call scanf
lea eax, [ebp-0x4] ; address of age
push eax
push offset .LC1 ; format "%d"
call scanf
lea eax, [ebp-0x8] ; address of fav
push eax
push offset .LC2 ; format "%d"
call scanf
lea eax, [ebp-0x64] ; name
mov edx, [ebp-0x4] ; age
mov ecx, [ebp-0x8] ; fav
push ecx
push edx
push eax
push offset .LC3 ; format "Hello %s, you are %d..."
call printf
💡 הסבר שלב-שלב:¶
-
sub esp, 0x70: מקצה מקום ל־char name[100]ול־2 משתנים בגודל 4 בתים כל אחד (סה"כ 112 בתים = 0x70). -
כל
lea eax, [ebp-offset]– מחשבת את הכתובת של משתנה מקומי בזיכרון. -
push offset .LCx– שולח לפונקציה את מחרוזת הפורמט (כמו "%s", "%d"...). -
push eax– שולח את כתובת המשתנה לפונקציה. -
call scanf/call printf– קריאה לפונקציית ספרייה שמוגדרת בלינקר. -
ההדפסה הסופית עושה שימוש ב־
pushעבור כל משתנה לפי הסדר ש־printfדורש.
סיכום:¶
-
הקוד ב־C מקצה משתנים בזיכרון ה־Stack.
-
כל קריאה ל־
scanfאוprintfמתבצעת על ידי שימוש ב־pushשל פרמטרים ו־call. -
הכתובות לזיכרון מקומי מחושבות יחסית ל־
ebp– כך המעבד ניגש למשתנים בפונקציה. -
הקוד שמתקבל תואם בצורה מלאה את ההרצאות הקודמות על Stack, קריאות לפונקציה, שימוש ב־lea וכו'.