19-10-2007, 12:48
|
|
|
חבר מתאריך: 30.07.05
הודעות: 949
|
|
המ.. את עניין המצביעים צריך להסביר מהבסיס כדי שאפשר יהיה להבין.
אני לא יודע כמה אתה מכיר את הידע הבסיסי אז אני פשוט אתחיל מההתחלה:
בצורה מופשטת למדי, המחשב שלך בנוי מ-4 חלקים עיקריים:
1) התקני קלט (מקלדת, עכבר, מיקרופון...)
2) התקני פלט (מסך, מדפסת, רמקולים...)
3) יע"מ (יחידת עיבוד מרכזית) או באנגלית CPU (central proccessing unit) או בקיצור המעבד.
4) הזיכרון (כונן קשיח, RAM)
הזיכרון שבו אנו מעוניינים בעיקר הוא זיכרון ה-RAM (Random Access Memory) שבו משתמשות התוכניות בזמן הריצה, ועליו נרחיב.
ניתן להסתכל על הזיכרון כעל מטריצה ענקית. כל תא במטריצה הזו יכול להחזיק ערך לוגי: 1 או 0.
תא בודד יכול לשמור אולם מידע בסיסי כמו למשל TRUE או FALSE, אבל למעשה מי שתכנן את המחשב הבין שאין כ"כ טעם בקריאה של סיבית בודדת.
מהזיכרון אנו קוראים ביחידות שנקראות ביתים (bytes) שהם למעשה אוסף של 8 ביטים (bits).
למעשה מה שאנו מקבלים מהזיכרון הוא רצף (סדור) של סיביות. כאשר אנחנו מקבלים אותם, אנו יכולים לתרגם אותם בצורות שונות:
הרצף 10001100 יכול להיות מתורגם כ: 140, 116-, או כתו: 'Œ' [משהו לא מאד מוגדר...]
את הצורה שבה המספר הבינארי יתורגם קובע טיפוס הנתונים בתוכנית שלך.
לכל בית בזיכרון יש כתובת מוגדרת ויחידה אשר הוא מזוהה איתה בצורה חד-חד-ערכית.
לכל מידע בזיכרון ניתן לגשת בצורה ישירה, ע"י הכתובת של הזיכרון שמכיל אותה.
כשאתה יוצר משתנה בתוכנית שלך, לדוגמא מסוג INT, המהדר (קומפיילר) שלך יוצר הקצאה של 4 בתים בזיכרון, את התכובת של הראשון מבינהם הוא זוכר, ומאותו רגע, המידע הנ"ל מיוחס למשתנה שלך, לדוגמא count.
כשאתה כותב count בתוכנית שלך, מה שקורה בפועל הוא שקוד המכונה ניגש ל-4 הבתים של המשתנה שלך, קורא את המידע שלהם, ומתרגם אותו בשיטת המשלים ל-2 (טיפול במספרים שלמים).
כל המשתנים שיש לך בתוכנית - יושבים בזיכרון. גם הקוד של התוכנית שלך עצמה - יושב בעצם בזיכרון.
לכל תא בזיכרון - יש כתובת, ולכן יש ב-C אופרטור & שיודע לשלוף את הכתובת של המשתנה הספציפי שלך.
לדוגמא הפונק' SCANF מקבלת כתובות של משתנים שאליהם היא כותבת את המידע:
קוד:
scanf("%d", &number);
למעשה הפונק' לא מקבלת את ערך המשתנה number אלא את הכתובת שלו.
כשאתה כותב את התוכנית שלך ומשתמש במשתנים שהגדרת, למעשה קוד המכונה המתקבל לא מודע בכלל לשמות הללו. בקוד המכונה, הכל מתורגם לכתובות שלהם ממש בזיכרון.
==== עד כאן ההקדמה, עכשיו לשאלה שלך ====
כל המשתנים בתוכנית הם למעשה מידע שמשמש את המהדר שלך, ולא את קוד המכונה עצמו.
כאשר אתה מגדיר int num; המהדר יודע להקצות מקום ל-num על המחסנית, ויודע שבכל פעם שהוא נתקל ב-num מעכשיו, הוא צריך לטפל בו כמספר שלם.
אבל לפעמים אנחנו לא רוצים רק את הערך ש-num שומר, אלא דווקא את הכתובת שלו!
מקרים כאלו הם למשל כשאנו מציבים ערך לתוך num.
כשאנו עושים: num=15; למעשה לא מעניין אותנו הערך הקודם של num, אלא רק הכתובת שלו בזיכרון.
כמובן שהמהדר יודע לטפל בזה לבד, אבל מה קורה כשאנו משתמשים בפונק'?
צורת הפעולה של הפונק' היא פשוטה:
1) תעתיק את הערך של כל המשתנים שקיבלת.
2) בצע את פעולת הפונק'
3) שחרר את כל המשאבים שלקחת בשביל 1.
אבל מה אם נרצה לבנות פונק' swap שמקבלת 2 משתנים ומחליפה את הערכים שלהם?
בכלים הרגילים שלנו, אין לנו שום אפשרות לעשות זאת!
אם נעתיק את הערך של המשתנים, ואז נחליף ביניהם - כל מה שנעשה יהיה להחליף את הערכים במשתנים הזמניים שהקצתה הפונק' ואילו הערכים במשתנים המקוריים לא ישתנו.
לצורך ביצוע הפונק' swap, אנו רוצים לשלוח את המשתנים עצמם ולא את ההעתק או הערך שלהם.
המשתנה עצמו מזוהה רק ע"י דבר אחד: הכתובת שלו בזיכרון - וזה בדיוק מה שאנו צריכים לשלוח אל הפונק' swap.
מצביע הוא פשוט כתובת בזיכרון.
למה עוד נשתמש במצביעים?
א) אם יש לך טיפוס נתונים גדול (struct) לא תרצה ליצור העתק שלו כשתשלח אותו לפונקציה... שלח את העצם עצמו(הכתובת שלו)...
ב) אם אתה רוצה שהעצם ישתנה במהלך הפונק'
ג) הקצאות דינאמיות של זיכרון.
בשביל מה אנחנו צריכים הקצאות דינאמיות?!
כי לא תמיד ניתן לדעת כמה זיכרון התוכנית שלנו תצרוך באמת:
למשל אנחנו רוצים לבנות תוכנית ניהול לחברת כוח אדם.
בחברה כזו יש מספר עובדים, מספר אנשים שמחפשים עבודה, ומספר של מעסיקות שאיתן החברה עובדת.
כמה מכל אחד? לא יודע!!!
כשאתה בונה תוכנה, אתה (בדרך כלל) לא תתאים אותה ללקוח אחד ומסויים.
יתרה מזאת: יתכן שהחברה שקנתה את התוכנה גודלת - ומה אז?
מצד אחד, אפשר להתפשר ולהגדיר למשל מערך לקוחות בגודל 10000...
אבל אז יש 2 בעיות:
1) חברה קטנה עם 100 לקוחות ומחשבים חלשים שלא תוכל להריץ תוכנה שזקוקה לכמות מטורפת כזו של זיכרון
2) חברה בין-לאומית עם מאות אלפי לקוחות שלא תוכל להשתמש בתוכנה שלך כי אין לה מספיק מקום לאכסן את כולם.
הפתרון הוא הקצאה דינאמית של זיכרון:
מוסיפים לקוח - מקצים זיכרון בשבילו.
מוחקים לקוח - משחררים את הזיכרון שהוקצה בשבילו.
בצורה כזו, התוכנה שלך תמיד משתמשת בדיוק בכמות הזיכרון שהיא זקוקה לו.
לא פחות, אבל גם לא יותר.
מקווה שעזרתי,
יום נעים.
_____________________________________
חתימתכם הוסרה כיוון שלא עמדה בחוקי האתר. לפרטים נוספים לחצו כאן. תוכלו לקבל עזרה להתאמת החתימה לחוקים בפורום חתימות וצלמיות.
|