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 רגילה, כמו קריאה לפקודות מיוחדות של המעבד, שליטה ברגיסטרים, ביצוע interrupts, ועוד.
דוגמה בסיסית (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¶
- מאפשר גישה לרגיסטרים ולפקודות חומרה.
- ביצועים גבוהים במקרים מסוימים.
- מאפשר לכתוב קוד ברמת מערכת (למשל interrupts ב-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) (לינקוג') בין שני קבצי האוייבקט לקובץ הרצה אחד.