לוגו אתר Fresh          
 
 
  אפשרות תפריט  ראשי     אפשרות תפריט  צ'אט     אפשרות תפריט  מבזקים     אפשרות תפריט  צור קשר     חץ שמאלה ‎print ‎"Hello World!"; if‎ ‎not rules.‎know ‎then rules.‎read(); חץ ימינה  

לך אחורה   לובי הפורומים > מחשבים > תכנות ובניית אתרים
שמור לעצמך קישור לדף זה באתרי שמירת קישורים חברתיים
תגובה
 
כלי אשכול חפש באשכול זה



  #10  
ישן 19-10-2007, 11:48
  Dark Knight Dark Knight אינו מחובר  
 
חבר מתאריך: 30.07.05
הודעות: 949
שלח הודעה דרך ICQ אל Dark Knight
בתגובה להודעה מספר 1 שנכתבה על ידי IsraHack שמתחילה ב "[C] מצביעים."

המ.. את עניין המצביעים צריך להסביר מהבסיס כדי שאפשר יהיה להבין.
אני לא יודע כמה אתה מכיר את הידע הבסיסי אז אני פשוט אתחיל מההתחלה:

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

הפתרון הוא הקצאה דינאמית של זיכרון:
מוסיפים לקוח - מקצים זיכרון בשבילו.
מוחקים לקוח - משחררים את הזיכרון שהוקצה בשבילו.

בצורה כזו, התוכנה שלך תמיד משתמשת בדיוק בכמות הזיכרון שהיא זקוקה לו.
לא פחות, אבל גם לא יותר.

מקווה שעזרתי,
יום נעים.
_____________________________________
חתימתכם הוסרה כיוון שלא עמדה בחוקי האתר. לפרטים נוספים לחצו כאן. תוכלו לקבל עזרה להתאמת החתימה לחוקים בפורום חתימות וצלמיות.

תגובה ללא ציטוט תגובה עם ציטוט חזרה לפורום
  #13  
ישן 19-10-2007, 20:18
  eXeCuTeR+ eXeCuTeR+ אינו מחובר  
 
חבר מתאריך: 19.10.07
הודעות: 65
בתגובה להודעה מספר 12 שנכתבה על ידי Dark Knight שמתחילה ב "[QUOTE] אז כעיקרון השימוש של..."

ציטוט:
במקור נכתב על ידי Dark Knight
אני לא חושב שהקביעה הזו היא מדוייקת.
אחד השימושים של מצביעים הוא להעביר עצם לפונק' (עצם בניגוד לערך).
יחד עם זאת, לא ניתן להתעלם משימושים חשובים אחרים של מצביעים כשבונים טיפוסי נתונים ושימוש בהקצאות זיכרון דינאמיות.
דוגמא אחת לחוסר דיוק של המסקנה שלך היא טיפוס של "מצביע לפונקציה". זה כעקרון ממש לא מיועד להעברה של פרמטר לפונק' בשיטה של byRef...


אז ממה שהבנתי, זה דבר כזה,
מצביעים הם בשביל:
1. יעילות הקוד - סדר מסויים (לדוגמא, מצביעים לפונקציות)
2. הקצאת זכרון דינאמי.
3. העברה לפי ייחוס.
אני צודק?

שאלה נוספת,
שבונים טיפוסי נתונים? מה מצביעים עוזרים לי במצב כזה?
אני יכול ליצור טיפוס נתונים בלי מצביעים.


אגב,
מה הכוונה בלהעביר עצם? מהו עצם?


*אני זה IsraHack, החלפתי משתמש כדי לשנות את הכינוי לכינוי שלי :ם*

נערך לאחרונה ע"י eXeCuTeR+ בתאריך 19-10-2007 בשעה 20:28.
תגובה ללא ציטוט תגובה עם ציטוט חזרה לפורום
  #14  
ישן 19-10-2007, 22:40
  Dark Knight Dark Knight אינו מחובר  
 
חבר מתאריך: 30.07.05
הודעות: 949
שלח הודעה דרך ICQ אל Dark Knight
בתגובה להודעה מספר 13 שנכתבה על ידי eXeCuTeR+ שמתחילה ב "[QUOTE=Dark Knight]אני לא..."

הכוונה שלי לעצם, היא התייחסות למשתנה עצמו, ולא רק לערך שלו - כלומר, ערך + הכתובת בה הוא מאוכסן.


אני אתן לך דוגמא לשימוש במצביעים עבור טיפוס נתונים שחרשתי עליו הרבה - רשימה מקושרת חד-כיוונית.

מה היא רשימה מקושרת חד-כיוונית?

תחשוב על שרשרת בה כל חוליה מחוברת ל-2 חוליות אחרות ורק אליהן - זה רשימה מקושרת, ב"חור" של כל טבעת יש נתון, ואפשר לעבור מחוליה לחוליה.
החד-כיווניות אומר שאפשר ללכת רק בכיוון אחד של השרשרת הזו.

איך זה נראה במחשב? struct שמחזיק במידע, ובחולייה הבאה.
לכל חולייה כזו (קוראים לזה node) ניתן להסתכל במידע, וניתן לעבור לחולייה הבאה.
מה היא החולייה הבאה? מצביע ל-node אחר!

זה יראה כך מבחינת הקוד:
קוד PHP:
 typedef struct node_tNode;
typedef voidElement;

struct node_t {
     
Element data;
     
Node next;



מה קורה פה?
קודם כל - הרשימה שאני רוצה לבנות היא רשימה כללית - כלומר רשימה שיכולה להחזיק כל טיפוס שבעולם, ולא רק רשימה שמיועדת ל-int או ל-double.
לכן אלמנט מידע ברשימה שלי הוא - void* - כלומר מצביע ל-void (שזה בעצם כל דבר שבעולם) ואז המשתמש ברשימה ידע לעשות המרה למה שהוא רוצה, לדוגמא:
קוד PHP:
 //Assume we have Node current
myData = *((int*)current->data); 

כלומר ממירים את void* ל-int* ואז לוקחים את הערך.
זה יוצר כלליות של הרשימה.

והשדה NEXT? יכיל NULL אם אין איבר הבא, או את כתובת ה-node הבא ברשימה.
זה טיפוס של חולייה.


יחד עם זאת, רשימה מקושרת יהיה טיפוס מורכב קצת יותר.
נרצה שרשימה כזו תדע לטפל בעצמה, ולכן נגדיר כמה דברים:
נזדקק ל-3 פונק' לפחות כדי למלא ולתחזק את הרשימה שלנו בלי דליפות זיכרון:
  • פונק' שתדע להעתיק אובייקט (כדי לשמור של המידע ולא את העצם עצמו).
  • פונק' שתדע לשחרר את האובייקט שבנינו (כדי למנוע דליפות זכרון. יתכן ש-free לא יספיק)
  • פונק' שתדע להשוות בין 2 אובייקטים (לצורך חיפוש ברשימה, לדוגמא)
  • אפשר גם פונק' נוספות שידעו להדפיס, או לעשות פעולות במידע... מה שתרצה
פונק' כאלו, הן לא חלק מהטיפוס של הרשימה המקושרת החד-כיוונית הכללית, כי היא כללית.
הפונק' הזו צריכה להיות מסופקת ע"י המשתמש, ובלבד שהיא תקיים תנאים מסויימית של פעולה:

קוד PHP:
 typedef Element (*CopyFuntion)(Element);
typedef void (*FreeFunction)(Element);
typedef bool (*CompareFunction)(ElementElement); 

3 טיפוסים אלו הם טיפוסים של פויינטרים לפונק'
CopyFunction היא פונק' שמקבלת ELEMENT ומחזירה ELEMENT שהוא העתק שלו.
FreeFunction היא פונק' שמקבלת אלמנט ומשחררת אותו (לא מחזירה כלום, כמו FREE)
CompareFunction היא פונק' שמקבלת 2 אלמנטים ומשווה ביניהם.

עכשיו לטיפוס של הרשימה המקושרת:

קוד PHP:
 typedef struct {
   
Node top;
   
Node current// For iteration
   
CopyFunction cpyFunc// Copy function
   
FreeFunction freeFunc;  // Free function
   
CompareFunction cmpFunc//Compare Function
LinkedList


יש לנו זיכרון של ראש הרשימה, ואיטרטור פשוט בשם current שבעזרתו אפשר לעבור על הרשימה.
בנוסף יש לנו את 3 הפונק' שהן לשימוש פנימי במהלך מימוש פונק' המנשק של הטיפוס רשימה מקושרת.

כמו שאתה רואה, כל הטיפוס של הרשימה מכיל 5 מצביעים ושום דבר נוסף.
גם החולייה של הרשימה מכילה 2 מצביעים בסה"כ.

זה מאפשר לנו כלליות מוחלטת של הרשימה - לא מעניין אותה על מה היא מצביעה, בתנאי שהלקוח עשה את מה שצריך.

אז מה אנחנו מסיקים? עם מצביעים אפשר לעשות המון דברים - זו היא חוליה מקשרת בין שפת המכונה לבין השפה העילית:
1) מאפשרת כלליות בעזרת void*
2) נזקקת לצורך הקצאת זיכרון
3) מחוייב כדי להעביר לפונק' את העצם עצמו - ולא עותק של הערך שלו.

מקווה שהדוגמא הזו עזרה לך קצת להבין את המצביעים.
_____________________________________
חתימתכם הוסרה כיוון שלא עמדה בחוקי האתר. לפרטים נוספים לחצו כאן. תוכלו לקבל עזרה להתאמת החתימה לחוקים בפורום חתימות וצלמיות.

תגובה ללא ציטוט תגובה עם ציטוט חזרה לפורום
  #16  
ישן 19-10-2007, 23:32
  Dark Knight Dark Knight אינו מחובר  
 
חבר מתאריך: 30.07.05
הודעות: 949
שלח הודעה דרך ICQ אל Dark Knight
בתגובה להודעה מספר 15 שנכתבה על ידי eXeCuTeR+ שמתחילה ב "במשפטים האחרונים..."

איך תוכל לעשות טיפוס רשימה שיוכל להחזיק מידע מכל סוג בלי מצביעים?
void* מחזיק כתובת, ורק כתובת - בלי מידע הטיפוס המתאים.
לכן אתה יכול לשים בכתובת הזו כל סוג של מידע.

באותו הטיפוס שהגדרתי מעלה, ניתן לשמור גם רשימה של int, גם רשימה של double, גם רשימה של struct person, בתנאי שאני אספק לרשימה את 3 הפונק' המתאימות.

יתרה מזאת: איך תגדיל או תקטין את הרשימה בלי הקצאות דינאמיות?
אולי לא הסברתי כמו שצריך, אבל הכנסת איבר לרשימת LIFO יראה כך:

קוד PHP:
 typedef enum SUCCESSOUT_OF_MEMBAD_ARGUMENTFAIL Result;

Result ListAddItem(LinkedList lstElement elm) {
    if ((
lst == NULL) || (elm == NULL)) {
         return 
BAD_ARGUMENT// Return - No need for else
    
}
    else { 
// just an excuse to open block (standard C-89)
         
Element copy lst->cpyFunc(elm); // Create a copy of elem
         
Node item NULL// Don't leave un-initilized vars, ESPECIALLY POINTERS!
         
if (copy == NULL) return OUT_OF_MEM// Return - No need for else
         
if ((item = (Node)malloc(sizeof(struct node_t))) == NULL) { // Always check success of allocations
               
lst->freeFunc(copy); //Release resources
               
return OUT_OF_MEM// Return - No need for else
         
}
         
item->data copy// The new node remmembers the new data
         
item->next lst->top// We insert the new element to the top - create link
         
lst->top =  item// Replace the top of the list with the new link.
         
return SUCCESS;
    }



כמו שאתה יכול לראות, אני מוסיף איברים חדשים לרשימה, לאחר שהקצאתי זיכרון חדש בשבילם.
הקצאות דינאמיות עובדות רק עם מצביעים - כי malloc מחזירה את הכתובת של הבית הראשון שהוקצה לטיפוס.
עם כתובת זו אני עושה שימוש כרצוני.

בפונק' ההוספה יש 2 הקצאות דינאמיות - אחת גלוייה והשנייה סמוייה:
גלוייה: ה-malloc שיוצר node חדש.
סמוייה: השימוש ב-cpyFunc - הפונק' הזו יוצרת הקצאה של של זיכרון בשביל להעתיק את האלמנט.
שים לב שבגלל זה, במקרה שנכשלת ההקצאה אני מחזיר OUT_OF_MEM בשני המקרים, ובמקרה המאוחר אני גם משחרר את הזיכרון שהוקצה.
_____________________________________
חתימתכם הוסרה כיוון שלא עמדה בחוקי האתר. לפרטים נוספים לחצו כאן. תוכלו לקבל עזרה להתאמת החתימה לחוקים בפורום חתימות וצלמיות.

תגובה ללא ציטוט תגובה עם ציטוט חזרה לפורום
  #18  
ישן 20-10-2007, 10:56
  Dark Knight Dark Knight אינו מחובר  
 
חבר מתאריך: 30.07.05
הודעות: 949
שלח הודעה דרך ICQ אל Dark Knight
בתגובה להודעה מספר 17 שנכתבה על ידי eXeCuTeR+ שמתחילה ב "קצת קשה להבין את הקודים שאתה..."

טוב, אולי בחרתי נושא קצת מורכב מדי לדוגמאות ראשוניות...
בוא נחזור קצת אחורה וננסה לדבר קצת על הקצאות דינאמיות מההתחלה.
אני מתחיל לחשוב שיש לך את אותה הבעיה שהייתה לי בתחילת הדרך עם ההקצאות הדינאמיות - שאתה פשוט לא מבין במה שונה זיכרון מוקצה מזיכרון רגיל.

השימוש הכי פשוט ובסיסי בזיכרון דינאמי הוא חוסר בזבוז בזיכרון. אם עד היום כשהיית מתבקש לכתוב תוכנית שקולטת מחרוזת ומדפיסה אותה למסך - היית מגדיר מערך תווים בגודל מסויים (טיפוסי הוא 256 תווים) ומשתמש ב-scanf לתוכו.

אבל מה הוא בעצם מערך?
כמו שאתה בוודאי יודע, מערך הוא גוש זיכרון [חשוב לשים לב שכל המערך יושב כגוש יחיד בזיכרון וכל התאים בו מסודרים בזיכרון כפי שהם מסודרים במערך].
מה שאתה (אולי) לא יודע, הוא שהמימוש של מערך ב-C הוא פשוט מצביע לתחילת המערך.
כשאתה כותב
קוד:
char str[MAX_LEN];

מה שקורה הוא שהמהדר מקצה MAX_LEN תאים של CHAR ברצף על המחסנית, ומגדיר את str להיות (char*).
ואכן לטיפוסי מצביע קיים אופרטור [num] - זה הוא אופרטור בשם "אינדקס" והוא אומר: מהכתובת הנוכחית בזיכרון, תתקדם num גדלים של הטיפוס של המצביע, ותקרא את הערך בנקודה.
זו אגב אחת הסיבות ששימוש במערך הוא מסוכן - אתה יכול לכתוב גם str[3000] וזה לא יתן שגיאת קומפילציה, ואולי אפילו לא שגיאת זמן-ריצה, אבל זה יתן לך לגשת לזיכרון שאין לך מושג מה רשום בו... יתכן שזה ישלח אותך לאיזור בזיכרון שבו רשומות הפקודות שלך, ואתה עלול לדרוס אותן...
לא רעיון טוב

אם מערך הוא בעצם מצביע, אז ניתן להשתמש במצביע כבמערך.
לדוגמא אתה רוצה לקלוט 5 מחרוזות ולהדפיס אותן, בבזבוז מינימאלי של זיכרון. איך תעשה זאת?
נגדיר מחרוזת עזר בגודל 255 שאליה נקלוט כל פעם מחרוזת, לאחר מכן נקצה את הגודל המתאים ונעתיק אותה:
(נניח לצורך פשטות שבתוכנית שלנו, הקצאות זיכרון דינאמיות לא נכשלות)
קוד PHP:
 #include <stdio.h>
#include <string.h>

#define MAX_LEN 256
#define STR_NUM 5

int main(void) {
     
charstrings[STR_NUM]; // פה נשמור את המחרוזות
     
char buffer[MAX_LEN]; // לכאן נקלוט את המחרוזות
     
int i 0;

     for (
0i<STR_NUM; ++i) {
           
printf("Please enter the string: \n"); // הדפסה של הוראה למשתמש
           
scanf("%s"buffer); // קריאה של הקלט לתוך מחרוזת העזר
           
strings[i] = (char*)malloc(strlen(buffer)*sizeof(char) +1); //+1 for the \0 in the end
           // הפקודה למעלה מקצה בדיוק את הזיכרון הדרוש למחרוזת שנקלטה
           
strcpy(bufferstrings[i]);
     }
     for (
0STR_NUM; ++i
           
printf("%d) %s\n"istrings[i]); // Print the strings
   
     // שחרור של משאבי המערכת שהקצאנו
     
for (0STR_NUM; ++ifree(strings[i]);
 
     return 
0;


אם כן, כמו שאפשר לראות בדוגמא הזו, ניתן להשתמש בהקצאת זיכרון דינאמית כדי להתאים מערכים לגודל המדוייק שאתה זקוק לו.

אבל לא רק לצורך דיוק בשימוש במשאבי המערכת ניתן להשתמש בהקצאות זיכרון דינאמיות.
למעשה ניתן להשתמש בהקצאות זיכרון דינאמיות גם כדי לבקש זיכרון נוסף שלא קשור למערכים.
בוא נממש עכשיו שימוש מאד פשוט ברשימה המקושרת שדיברנו עליה קודם:

אנו רוצים לכתוב תוכנית שתקבל מספרים חיוביים (כמות לא מוגבלת שלהם) ובפעם הראשונה שהיא תקבל מספר שלילי היא תדפיס את הרשימה של המספרים החיוביים בסדר ההפוך לזה שהיא קיבלה אותם.
לצורך זה, נשתמש בעקרונות של הרשימה המקושרת, ורק נתאים אותם קצת לצורך המדוייק שלנו (נפשט את הדוגמא).
בדוגמא הזו, אני מקווה להראות לך כיצד ניתן להקצות עוד ועוד זיכרון, בעזרת רק 2 מצביעים.
גם פה, לצורך פשטות, נניח שהקצאות הזיכרון לא נכשלות [בתוכנית אמיתית צריך תמיד לבדוק ש-MALLOC לא מחזירה NULL).

קוד PHP:
 /*
קודם כל, נגדיר את האוסף שנקרא חולייה בשביל הרשימה
*/
typedef struct node_tNode// שים לב שחולייה היא בעצם מצביע!
struct node_t {
     
int number// שים לב שהשימוש פה הוא לא כללי, אלא מסויים לבעיה שלנו
     
Node next// פה אין מה לחשוב, מצביע לחוליה הבאה
}

int main(void) {
     
int buffer 0// משתנה הקלט שלנו. אנחנו קולטים מספרים שלמים
     
Node first NULL// במשתנה הזה אנחנו נזכור את החולייה הראשונה
     
Node help NULL// נשתמש במצביע הזה כדי לעבור על הרשימה וליצור חוליות חדשות.

     
printf("Please Enter a number: \n");
     
scanf("%d", &buffer);
     while (
buffer >= 0) {
            
help = (Node)malloc(sizeof(struct node_t)); // יוצרים חולייה חדשה
            
help->number buffer// מכניסים את המספר לחלק המידע של החולייה
            
help->next first// מכניסים את החולייה החדשה לראש הרשימה
            
first help// ועכשיו תחילת הרשימה מצביעה על החולייה החדשה
            
printf("Please Enter a number: \n");
            
scanf("%d", &buffer);
     }
     
// We get here when the first negative number was read.
     // Now we print the list:
     
help first// מצביע העזר שלנו מסתכל עכשיו על תחילת הרשימה
     
while (help != NULL) { // הרשימה מסתיימת כאשר הגענו ל NULL
            
printf("%d\n"help->number); // Print the number
            
help help->next// תעבור לחולייה הבאה
     
}

     
// Release the allocated memory:
     
help first;
     while (
help != NULL) {
            
help first->next;
            
free(first);
            
first help;
     }
     return 
0;



בוא ונראה איך עובדת הרשימה המקושרת בעצם...
נתחיל מכמה סימונים:
תמונה שהועלתה על ידי גולש באתר ולכן אין אנו יכולים לדעת מה היא מכילה

כשהתוכנית שלנו מתחילה, גם FIRST וגם HELP מצביעים ל-NULL (שזה בעצם כלום):
תמונה שהועלתה על ידי גולש באתר ולכן אין אנו יכולים לדעת מה היא מכילה

בכל איטרציה של הלולאה, אנו קולטים מספר לתוך BUFFER, ויוצרים בשביל המספר הזה חולייה חדשה:
תמונה שהועלתה על ידי גולש באתר ולכן אין אנו יכולים לדעת מה היא מכילה
לתוך שדה הנתונים שלה - מכניסים את buffer.

את החולייה החדשה, אנו רוצים לשלב ברשימה שלנו (נניח יש כבר 3 מספרים ברשימה):
תמונה שהועלתה על ידי גולש באתר ולכן אין אנו יכולים לדעת מה היא מכילה
את החולייה החדשה, שמוחזקת כרגע ב-HELP אנו מקשרים לרשימה השלמה שלנו:
את next של החולייה החדשה מכוונים לתחילת הרשימה (חץ כחול) ואז את תחילת הרשימה (FIRST) מכוונים אל החולייה החדשה (חץ אדום).

בסוף התהליך, מתקבלת רשימה עם 4 חוליות:
תמונה שהועלתה על ידי גולש באתר ולכן אין אנו יכולים לדעת מה היא מכילה

כמו שאתה יכול לשים לב, ניתן לחזור על התהליך כמה פעמים שתרצה, עד שיגמר הזיכרון במחשב.
עם 2 מצביעים בסה"כ שהגדרנו בתוכנית הראשית - יצרנו כמות גדולה של מידע חדש.

אם יש לך עוד שאלות, אתה מוזמן לשאול.
_____________________________________
חתימתכם הוסרה כיוון שלא עמדה בחוקי האתר. לפרטים נוספים לחצו כאן. תוכלו לקבל עזרה להתאמת החתימה לחוקים בפורום חתימות וצלמיות.

תגובה ללא ציטוט תגובה עם ציטוט חזרה לפורום
תגובה

כלי אשכול חפש באשכול זה
חפש באשכול זה:

חיפוש מתקדם
מצבי תצוגה דרג אשכול זה
דרג אשכול זה:

מזער את תיבת המידע אפשרויות משלוח הודעות
אתה לא יכול לפתוח אשכולות חדשים
אתה לא יכול להגיב לאשכולות
אתה לא יכול לצרף קבצים
אתה לא יכול לערוך את ההודעות שלך

קוד vB פעיל
קוד [IMG] פעיל
קוד HTML כבוי
מעבר לפורום



כל הזמנים המוצגים בדף זה הם לפי איזור זמן GMT +2. השעה כעת היא 15:17

הדף נוצר ב 0.08 שניות עם 12 שאילתות

הפורום מבוסס על vBulletin, גירסא 3.0.6
כל הזכויות לתוכנת הפורומים שמורות © 2024 - 2000 לחברת Jelsoft Enterprises.
כל הזכויות שמורות ל Fresh.co.il ©

צור קשר | תקנון האתר