|
09-01-2009, 16:27
|
|
|
חבר מתאריך: 22.06.06
הודעות: 87
|
|
בתגובה להודעה מספר 1 שנכתבה על ידי dorM שמתחילה ב "איך התוכנה יודעת לזהות את סוג המשתנה? char, int וכד'"
ניראה שאתה שואל פה שתי שאלות שונות, אפילו יותר במבט אחר (או שלא הבנתי נכון)
כשאתה שולח מידע, הכל הוא string, למרות זאת, עד כמה שאני זוכר מישהו כתב ספרייה מיוחדת בשביל לשלוח דברים אחרים, כמו struct int long double וכ' (וגם הראו לינק אליה פה, חפש (: )
בקשר לזיכרון, זה הסיוט של כל מי שכתב disassembler לדעת לזהות מידע נכון, struct וכ', משתמשים בכל שיטות, חלק מהן זה heuristics ועוד כל מיני שאני לא ממש יודע עליהן.
מצד שני - זה גם מה שכל כך טוב, אין תבנית מסויימת,
לכן התשובה הפשוטה ביותר היא שאין דרך לדעת - צריך לדעת לנחש, להכיר את הקומפיילר וכך להבין איך הוא מייצג את המידע, צריך לקרוא internals docs (לדוג', של ׂGCC ) כדי להבין . יש הרבה מאוד מאמרים (חפש בגוגל ) על pattern matching ,
אם אין לך מה לעשות, תנסה לייצג מבנה מסויים ותראה איך כל קומפיילר מייצג אותו באסמבלר, זה ממש מגניב עם IDE (:
בקשר לתכנית, זה כבר משהו אחר לגמרי, כל פעם שאתה מקמפל תכנית, כלומר, לפני שהיא הופכת לexecutable היא עוברת הרבה מאוד שלבי ביניים, ופה זו הולכת להיות חפירה ענקית, אז אם לא בא לך לקרוא את זה ורק את התשובה הקצרה, תדלג על הפיסקה הזו
אני אקח את gcc כי אני משתמש בו הכי הרבה, אבל זה נכון כמעט לכל קומפיילר אחר שיצא לי להתקל בו (קלאסים כמו cl mingw וכ' וכ' וכ' )
ברגע שאתה מקמפל תכנית, הדבר הראשון שקורה הוא שתכנית ביניים שניקראת lexer עוברת על כל הקוד שלך ומתחילה לזהות מילות קישור, ומבנים
(לדוג'
קוד:
#include <stdio.h>
/* This is just a comment */
// another comment!
int main(int argc, char *argv[] )
{
printf("A function was called! with %d args!\n",argc);
return 0;
}
)
ומעביר כל מידע שהוא יכול לטבלה, כל מילה מקבלת שם, ומספר, לדוג' argc
תקבל שם argc ומספר 1, יכולים להיות לה עוד פרמטרים שיווספו בעתיד, אבל זה הכל בינתיים.
במידה ואין שום שגיאה תחבירית (כי זה התפקיד של הlexer), הוא יוצר עץ זמני, שעם הזמן יקבל עוד ועוד צורה שבסופו של דבר ניקרא Abstract Syntax Tree , בכל שלב של הקימפול יווספו לו עוד ועוד פרמטרים חיוניים, כמו שמוסיפים לטבלה, למעשה, יש קומפיילרים שמעדיפים לא להוציא exectuable אלא AST , בגדול זה אותו הדבר.
השלב הבא שעומד לקרות הוא syntax analysis/parsing , שבעצם הופך ביטויים בעץ, או בקוד שלך ליותר קריאים, השלב הזה הוא אחד השלבים הכי קריטיים בקימפול, כל זאת אל תשכח, לפני בכלל שהתחלנו להוציא כל קוד שהוא אנחנו עדיין לא סיימנו לעבור על הקוד , בשלב הזה העץ שלך יקבל צורה הרבה יותר פרימטיבית ומובנת, הרבה יותר מידע יתווסף (לדוג', הסוג של המשתנים, איזה תכונות יש להם (public static protected, extern וכ' וכ' וכ' ) כמו כן כל המידע הזה ישמר בתוך הטבלה שאמרתי עליה מקודם שהיא
זמנית, השם האמיתי שלה הוא למעשה Symbol Table
בשלב הזה כל ה#define וה#include כבר מופו, אנחנו יודעים שהפונקציות קיימות ובכלל אנחנו יודעים שהקוד תקין ( תחבירית ), בשלב הזה הקומפיילר יתחיל שלב חדש, ובעצם יעביר את כל הקוד שלך לשפת ביניים ( Intermediate language ) , לרוב לשפה שדומה מאוד לאסמבלי, תלוי בקומפיילר, ובמפתח אני מניח, יש אנשים שמעדיפים לשלב על השלב הזה, בגדול, כל השלב הזה מתבסס על העץ שבנית מקודם והוא בעצם מתרגם אותו רק לדבר יותר נוח, יש שימשיכו את השלב הזה עוד יותר לאופטימיזציה (ועל זה אני לא אפרט כי זו חפירה מטורפת לדעתי ואני ממש לא בקיא בנושא הזה ) ואולי אפילו יכתבו שכבה מתחת, כלומר, Virtual Machine (כמו שיש לpython, perl, ruby, וכ' וכ' וכ'), אבל זה כבר שיקול של המתכנת, אחרי שעברנו את כל השלבים האלו הגענו לשלב האחרון - יצירת קוד אמיתי, אחרי שהכל באמת פועל ואין שום שגיאה - הקומפיילר יתחיל להעביר שורות מקוד הביניים לקוד אסמבלי אמיתי של הארכיטקטורה שלך (x86, amd, sparc, וכ' וכ' ) יקרא ללינקר (בהנחה שאנחנו מדברים על dynamic linking ), ויחבר את כל הספריות שלך (stdio, stdlibh) ויוציא exectuable ! ׁוככה הקומפיילר יודע איך משתנה קיים או לא (:
אם הגעת לפה זה אומר שאין לך ממש ראש לקרוא את כל החפירה הזו, אז בקצרה - בכל פעם שאתה מצהיר על משתנה הוא נשמר בטבלה ובכל שלב בקימפול נוסף לטבלה הזו עוד ועוד מידע על המשתנה, תלוי בשפה ובמתכנת איזה מידע, לרוב זה הסוג, (int, וכ' ), מספר שלו בטבלה (בשביל לאפשר שליפה מהירה זה לרוב hash table
|
|