3.8 סקופ הרצאה
מה זה Scope?¶
בכל תוכנית שאנחנו כותבים, יש לנו משתנים. אבל משתנה לא "חי" בכל מקום בתוכנית. הוא חי באיזור מסוים – וזה בדיוק ה־Scope שלו – התחום שבו המשתנה מוגדר ונגיש.
יש שלושה סוגים עיקריים של Scope:
-
סקופ מקומי (Local Scope) – בתוך פונקציה או בלוק קוד.
-
סקופ גלובלי (Global Scope) – משתנים שמוגדרים מחוץ לכל פונקציה.
-
סקופ סטטי (Static Scope) – תלוי במילת המפתח
static. (נדבר בהמשך)
משתנים מקומיים¶
כאן המשתנה x נוצר בתוך הפונקציה hello. הוא קיים רק בזמן הריצה של הפונקציה.
משתנים לוקלים מוגדרים בתוך המחסנית של הפונקציה, (הstack). ולכן- אחרי שהפונקציה מסיימת לרוץ איי אפשר לקרוא להם או להשתמש בהם (עשינו להם pop).
ברגע שהפונקציה מסתיימת – הוא נמחק מהזיכרון. אי אפשר לגשת אליו מבחוץ:
משתנים גלובליים¶
המשתנה counter מוגדר מחוץ לכל פונקציה – ולכן הוא גלובלי. המשמעות היא שהוא קיים לכל אורך זמן ריצת התוכנית – ושהוא נגיש מכל פונקציה בקובץ.
משתנים גלובלים מוגדרים בdata segement, כך שהם ניגשים כל הזמן מכל מקום- והם מוגדרים מראש, כך שהם תמיד קיימים שם.
זה נוח – אבל גם מסוכן:
- כל פונקציה יכולה לשנות אותו בטעות.
- קשה לעקוב מי משנה אותו – מה שמקשה על לפתור באגים.
מילת המפתח static¶
המילה "static" אומרת דברים שונים, תלוי באיפה היא נמצאת.
static בתוך פונקציה¶
במקרה הזה, למרות ש־x מוגדר בתוך הפונקציה, הוא לא נמחק כשהפונקציה מסתיימת. במקום זה, הוא נשמר בזיכרון בין הקריאות לפונקציה. כלומר – הוא זוכר את הערך שלו!
למעשה כמעט אף פעם לא משתמשים בזה.
static מחוץ לפונקציה¶
אם נכתוב static מחוץ לפונקציה, אז המשתנה הוא גלובלי רק בקובץ הנוכחי. כלומר – קובץ C אחר שמקושר לקוד הזה לא יוכל לגשת אליו.
כלומר, אם קובץ אחר יעשה include לheader שמגדיר אותו- לא יתבצע לינקוג'! (linking)
מילת המפתח extern¶
מה קורה אם אנחנו רוצים להשתמש במשתנה שנמצא בקובץ אחר?
המילה extern אומרת לקומפיילר: "המשתנה הזה קיים איפשהו אחר – אל תדאג, הלינקר ימצא אותו בזמן לינקוג' ".
זה שימושי כשעובדים עם כמה קבצים שמרכיבים מערכת אחת.
סיכום¶
| סוג | היכן מוגדר? | אורך חיים | גישה |
|---|---|---|---|
int x = 0; בתוך פונקציה |
מקומי | נמחק בסוף הפונקציה | רק בתוך הפונקציה |
static int x = 0; בפונקציה |
מקומי קבוע | נשמר בין קריאות | רק בתוך הפונקציה |
int x = 0; מחוץ לפונקציה |
גלובלי | לאורך כל זמן הריצה | נגיש מכל קובץ (אם מוצהר extern) |
static int x = 0; מחוץ לפונקציה |
גלובלי פרטי | לאורך זמן הריצה | נגיש רק באותו קובץ |
extern int x; |
הצהרה בלבד | לא מקצה זיכרון | מאפשר שימוש במשתנה חיצוני |
חלק א' – Inline Assembly¶
מה זה Inline Assembly?¶
הInline Assembly הוא כתיבה של פקודות Assembly בתוך קובץ C, כחלק מהקוד. זה שימושי כשאנחנו רוצים לבצע פעולות ברמת חומרה שלא זמינות לנו ב־C רגילה, כמו קריאה לפקודות מיוחדות של המעבד, שליטה ברגיסטרים, ביצוע פסיקות, ועוד.
דוגמה בסיסית (GCC – AT&T Syntax)¶
#include <stdio.h>
int main() {
int result;
asm("movl $5, %eax;"
"movl $3, %ebx;"
"addl %ebx, %eax;"
"movl %eax, %0;"
: "=r" (result) // output
: // input
: "%eax", "%ebx" // clobbered registers
);
printf("Result: %d\n", result);
return 0;
}
פירוק:
-
"movl $5, %eax;"– פקודות האסמבלי (AT&T syntax). -
: "=r" (result)– הגדרה של פלט: אומר שהערך שלeaxיוכנס ל־result. -
:– כאן אפשר להגדיר קלט (לא השתמשנו כאן). -
: "%eax", "%ebx"– רישום של רגיסטרים שהשימוש בהם שובר ערכים קודמים.
יתרונות של Inline Assembly¶
- מאפשר גישה לרגיסטרים ולפקודות חומרה.
- ביצועים גבוהים במקרים מסוימים.
- מאפשר לכתוב קוד ברמת מערכת (למשל פסיקות ב־real mode).
חסרונות¶
- קשה לתחזוקה.
- תלוי בקומפיילר (תחביר שונה בין GCC ל־MSVC).
חלק ב' – קימפול פרויקט עם קבצי .c ו־.asm¶
נניח שיש לנו פרויקט כזה:
שלב 1 – כתיבת קובץ Assembly (Intel Syntax)¶
נניח ויש לנו את הקוד אסמבלי הבא:
קראית הglobal מכריזה על print_hello כגלובלי, כלומר- בזמן לינקוג', הלינקר ידע שהפונקציה גלובלית ואפשר להשתמש בה בקבצי אסמבלי או C אחרים.
קראית הsection .text מגדירה section (סגמנט של protected mode) מסוג קוד (שזה .text במערכות הפעלה מודרנית)
שלב 2 – כתיבת קובץ C¶
נכתוב קוד C שמשתמש בפונקציה שמוגדרת באסמבלי, נעשה זאת באמצעות קריאת extern, שאומרת לקמופיילר שהלינקר (בזמן ליקוג') ידע לחבר בין print_hello לקוד.
שלב 3 – קומפילציה עם nasm ו־gcc¶
פקודת האסמבלר (אסמבלר nasm שמקמפל גם 32 ביט)
-
-f elf32 – פורמט מתאים ללינקינג עם GCC של 32 ביט.מקמפל לקובץ אובייקט בשם "tools.o"
פקודת GCC שמקמפל את main.c לקובץ אובייקט בשם "main.o"
-
-m32 – GCC מקמפל לקוד 32 ביט (ב־x86).
פקודת gcc שעושה לינקינג (linking) (לינקוג') בין שני קבצי האוייבקט לקובץ הרצה אחד.