רשימות מקושרות - בעיה עם פונקציה

Ron 417

New member
רשימות מקושרות - בעיה עם פונקציה

הבעיה היא: יש פונקציה ראשונה Species* insert_species שעובדת עם הראש Species *head ,ופונקציה שניה (שהיא מורצת מתוך insert_species) עובדת עם הראש Genus *head_g . הפונק' הראשונה קוראת לשניה עם head_g , ובסופה של הפונקציה השניה ישנה שורה return head_g ,בסופה של הראשונה מופיע return head. לפי מה שאני מבין מהדיבאגר ,כאשר התוכנית חוזרת ל main ,רק head חוזר עם ערך חדש .... זה נראה משהו כזה:
void main () { Species *head=NULL; Genus *head_genus=NULL; } Species* insert_species(Species *head,Genus *head_g,char common[], char latin[],char section[],int pop,double risk,genus_name[]) { if (!head) { mallocing and copying the "latin,section,pop,risk" into "head" head_g=Create_genus(head_g,genus_name,head); } return head; }​
אני עובד עם פונקציה ראשית Species* insert_species ופונקציה משנית Genus* Create_genus . יש למישהו רעיון מה יכולה להיות הסיבה שלמרות שאני משנה את הערכים של ההקצאה , ה head_g עדיין חוזר ל main עם bad ptr can evaluate .?.תודה.
 

Maha Vailo

New member
מצביע למצביע...

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

Ron 417

New member
כן ,הבנתי את זה

עכשיו כבר שעה שאני מנסה לתרגם את הפונקציה שאני נעזר בה עבור head_g ,לפונקציה עבור פוינטר ולפוינטר ,אבל לא מצליח. אז שאני אבין ,מה שצריך לעשות זה להפוך ל Genus **head_g ? אני משתמש בפונקציה שנקראת
Genus* create_genus(Genus *head,char name[],Species *spec)​
והיא עובדת רק עם פוינטר אחד. יש דרך שאני אוכל למשל לעבוד עם פוינטר אחד עבור head_g ,ואז להשתמש עם משתנה נוסף שיהיה פוינטר לפוינטר ,והוא יקבל את הערך של head_g ? או שאין מנוס ואני אהיה חייב לתרגם את הפונקציה הנ''ל ל ptr2ptr ? תודה
 

Maha Vailo

New member
לא בדיוק הבנתי

אבל כעיקרון אם כבר כתבת את הפונקציה המקורית זו לא אמורה להיות בעיה לשנות אותה. למשל תשנה את השם של הפרמטר (למשל headX**) ותיצור משתנה מקומי Genus *head. עכשיו הפונקציה נשארת אותו הדבר, רק שצריך עוד להוסיף איפשהו את z (*headX) = head. כל זה כמובן בהנחה שהמצביע הכפול שהפונקציה מקבלת, לא מצביע סתם לזבל (כלומר הוא הכתובת של מצביע קיים)
 

Ron 417

New member
אז ששנינו נבין

מה שאני רוצה לעשות זה דבר כזה : ש head_g יקבל את מה שהוא צריך מ create_genus ,כך שבפעם הבאה שהפונקציה הראשית תרוץ {ותזמן את create_genus שוב} מה שהיה ב head_g יהיה בראש והשאר ישורשר. השאלה היא אם אני צריך לעשות malloc למשתנה עם ה ** ? עוד דבר ,איך בדיוק נראה הזימון לפונקציה מ main עבור המשתנה עם ** ? אם הזימון עבור * אחת היה
head=insert_species(x,head_genus,y,common,latin,section,pop,risk);​
אז עכשיו אני צריך לשים עוד * לפני ה head_genus ? המון תודה
 

Maha Vailo

New member
דוגמא

void main(){ Type* ptr; f(&ptr); } void f(Type** ptrptr){ (*ptrptr) = malloc(sizeof(Type)); } ושיטה יותר נורמלית לפי דעתי, וגם תעזור לך אם הפונקציה כבר כתובה עם מצביע יחיד void f(Type** ptrptr){ Type* temp; temp = malloc(...); ... (*ptrptr) = temp; }​
בקשר לשרשור האיברים אחד לשני, זה כבר תלוי איך אתה מממש.
 

Ron 417

New member
אוקיי, משהו מוזר ..

head_g מקבל את מה שצריך והכל טוב ויפה ,אח''כ headX_g מקבל את הערך של head_g ,חוזר ל main אבל עדיין אין ערך . משהו כזה :
Species* insert_species(Species *head,Genus **headX_g,Class *head_c,char common[], char latin[],char section[],int pop,double risk) { head_g=create_genus(head_g,genus_name,head); headX_g=(Genus**)malloc(sizeof(Genus*)); *headX_g=head_g; return head; } void main () { Species *head=NULL; Genus **head_genus=NULL; head=insert_species(x,head_genus,y,common,latin,section,pop,risk);​
אבל שחוזרים ל main עדיין יש bad ptr ........ ? יש לך רעיון למה ?
 

Maha Vailo

New member
תסתכל שוב בדוגמא

אין לך מצביע כפול בפונקציה הראשית, אלא מצביע הרגיל, אבל כשאתה שולח אותו כפרמטר, אתה שולח את הכתובת לאותו מצביע
Species *head=NULL; Genus *head_genus=NULL; head=insert_species(x, &head_genus , y, common, latin, section, pop, risk);​
 

Ron 417

New member
כן כן שמתי לב

הצלחתי לתקן את זה ועכשיו זה עובד יופי ! שכחתי לגמרי שפוינטר לפוינטר אמור להיות מזומן עם ה & . הרבה תודות !!!! ממש . שאני אבין בכלליות ,מתי אני אמור להשתמש בפוינטר לפוינטר ? אם למשל אני מבצע שינויים בהצבעות של שדה *next או *up , בתוך פונקציה X שאותה זימנה פונקציה Y : אם הצלחת לקבל תמונה כללית על מה שניסיתי לעשות , אז בפונקציה שמזומנת אחרונה {כלומר פונק' שמזמנת פונק' שגם היא מזמנת ...} אני מבצע שינוי שדה הצבעה של שדה *up למשל ,שהכוונה להצבעה של הרשימה הנוכחית לרשימה למעלה ..אז גם פה יהיה צורך ב P2P ?
 

Maha Vailo

New member
רק כאשר אתה משנה את המצביע עצמו

סתם כלל אצבע, אם אתה מקבל פרמטר מצביע (כלומר יש בו כוכבית) ואיפשהו בתוך הפונקציה אתה מתייחס אליו בלי כוכבית, אז יש סיכוי טוב מאוד שכדאי להשתמש במצביע כפול, כי בעצם בלי הכוכבית, הוא סתם מספר שלם שהוא משתנה מקומי של הפונקציה. למשל אצלך הmalloc מחזיר ערך שאתה מציב אותו במצביע בלי הכוכבית. אם אתה רוצה לשנות את ה next* שיצביע למקום אחר בפונקציה פנימית, אז זה גם דורש מצביע כפול. אם לעומת זאת השינוי הוא בערך אליו הוא מצביע, למשל אם הוא מצביע למספר שלם ואתה רוצה לשנות את המספר, אז השימוש רגיל - כלומר מצביע אחד. זה כמו שאם אתה רוצה שפונקציה פנימית תשנה משתנה (למשל int) כלשהו שיש לך אז היא תקבל מצביע לאותו משתנה (int*) . אם אתה רוצה שהפונקציה הפנימית תשנה מצביע, אז היא תקבל כפרמטר מצביע למצביע. אם אתה רוצה שהיא תחזיר מערך דו מימדי חדש כאחד הפרמטרים, אז היא תקבל מצביע למצביע למצביע... וכך הלאה באינדוקציה...
 

Ron 417

New member
אנדוקציה של פוינטרים ...

לא נשמע נחמד . אחד שלומד איתי אומר כבר חודש '' אני רואה כל היום כוכבים '' ... הבנתי את ההסבר. שוב תודה
 
למעלה