הקצאה דינמית של מבנים
חלק מהיחידה לוגיקה וזרימת תוכנית במסלול ה-C של Coddy. שיעור 48 מתוך 63.
עד כה למדת איך ליצור מבנים במחסנית — הם קיימים בתחום שבו הם מוגדרים, ומתנקים אוטומטית כשהתחום הזה מסתיים. אבל מה אם צריך שמבנה ימשיך להתקיים מעבר לתחום המקורי שלו, או כשלא ידוע בזמן הקומפילציה כמה מבנים תצטרך?
כאן נכנסת לתמונה הקצאה דינמית. אפשר להשתמש ב־malloc() כדי ליצור מבנים בערימה, בדיוק כפי שהיית עושה עבור כל סוג נתונים אחר. ההבדל העיקרי הוא שמבנים שהוקצו באופן דינמי נשארים בזיכרון עד שמשחררים אותם במפורש באמצעות free().
הנה התחביר להקצאה דינמית של מבנה:
struct Point *ptr = malloc(sizeof(struct Point));פעולה זו מקצה מספיק זיכרון בערימה כדי להכיל מבנה Point אחד ומחזירה מצביע לזיכרון הזה. לאחר מכן אפשר להשתמש באופרטור החץ כדי לגשת לחברי המבנה ולשנות אותם:
ptr->x = 10;
ptr->y = 20;זכור תמיד לבדוק אם malloc() הצליחה (החזירה ערך שאינו NULL) ולקרוא ל-free(ptr) כשתסיים להשתמש במבנה, כדי למנוע דליפות זיכרון. הטכניקה הזו חיונית לבניית תוכניות גמישות שיכולות ליצור ולנהל מבני נתונים בזמן ריצה.
אתגר
קלצרו תוכנית C שמדגימה הקצאה דינמית של מבנים באמצעות malloc(). התוכנית שלכם צריכה:
- להגדיר
structבשםCarעם השדות הבאים:- מספר שלם
yearלאחסון שנת הייצור - מערך תווים
brandבגודל 20 לאחסון מותג המכונית - מערך תווים
modelבגודל 25 לאחסון דגם המכונית - מספר ממשי מסוג float בשם
priceלאחסון מחיר המכונית - מספר שלם
mileageלאחסון הקילומטראז׳ של המכונית
- מספר שלם
- בפונקציה
main, להכריז על מצביע למבנהCarבשםcarPtr - להשתמש ב-
malloc()כדי להקצות באופן דינמי זיכרון למבנהCarאחד, ולהציב את הכתובת שהוחזרה בתוךcarPtr - לבדוק אם הקצאת הזיכרון הצליחה:
- אם
carPtrהואNULL, להדפיסMemory allocation failedולצאת מהתוכנית - אם ההקצאה הצליחה, להדפיס
Memory allocation successful
- אם
- לקרוא את ערכי הקלט הבאים ולשייך אותם לשדות המבנה באמצעות אופרטור החץ:
- לקרוא מספר שלם עבור השנה ולשייך אותו באמצעות
carPtr->year - לקרוא מחרוזת עבור המותג ולשייך אותה באמצעות
carPtr->brand - לקרוא מחרוזת עבור הדגם ולשייך אותה באמצעות
carPtr->model - לקרוא מספר ממשי מסוג float עבור המחיר ולשייך אותו באמצעות
carPtr->price - לקרוא מספר שלם עבור הקילומטראז׳ ולשייך אותו באמצעות
carPtr->mileage
- לקרוא מספר שלם עבור השנה ולשייך אותו באמצעות
- לאחר קריאת כל הערכים, לבצע את החישובים הבאים באמצעות אופרטור החץ:
- לחשב את גיל המכונית על ידי חיסור השנה מ-2024:
int age = 2024 - carPtr->year; - לחשב את שיעור ירידת הערך לפי הגיל: אם הגיל גדול מ-10, להגדיר את
depreciationכ-0.6, אחרת להגדיר אותו כ-0.8 - לחשב את הערך הנוכחי:
carPtr->price * depreciation
- לחשב את גיל המכונית על ידי חיסור השנה מ-2024:
- להדפיס את פרטי המכונית באמצעות אופרטור החץ, בפורמט המדויק הבא:
Car Information:Year: [year]Brand: [brand]Model: [model]Original Price: [price]Mileage: [mileage]Age: [age] yearsCurrent Value: [current_value]
- לשחרר את הזיכרון שהוקצה באופן דינמי באמצעות
free(carPtr) - להדפיס
Memory freed successfully
האתגר הזה בודק את הבנתכם בהקצאה דינמית של מבנים באמצעות malloc(), בבדיקת כישלון בהקצאה, בגישה לשדות מבנה דרך מצביעים באמצעות אופרטור החץ, ובשחרור תקין של זיכרון שהוקצה באמצעות free(). תתרגלו את מחזור החיים המלא של ניהול זיכרון דינמי עם מבנים.
נסו בעצמכם
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// TODO: להגדיר כאן את המבנה Car
int main() {
// TODO: להצהיר על מצביע למבנה Car בשם carPtr
// TODO: להשתמש ב-malloc() כדי להקצות זיכרון למבנה Car אחד
// TODO: לבדוק אם הקצאת הזיכרון הצליחה
// לקרוא את ערכי הקלט
int year, mileage;
char brand[20], model[25];
float price;
scanf("%d", &year);
scanf("%s", brand);
scanf("%s", model);
scanf("%f", &price);
scanf("%d", &mileage);
// TODO: להציב את ערכי הקלט בשדות המבנה באמצעות אופרטור החץ
// TODO: לחשב את הגיל, ירידת הערך והערך הנוכחי
// TODO: להדפיס את פרטי הרכב בפורמט הנדרש
// TODO: לשחרר את הזיכרון שהוקצה ולהדפיס הודעת הצלחה
return 0;
}השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה לוגיקה וזרימת תוכנית
1יסודות המצביעים
מהו מצביע?הכרזה על מצביעיםאופרטור לקיחת הכתובת (&)אופרטור ביטול ההפניה (*)מצביעי NULLסיכום: יסודות המצביעים2מצביעים ומערכים
שמות מערכים כמצביעיםאיברי מערך ומצביעיםאריתמטיקה של מצביעיםהשוואת מצביעיםסיכום: מעבר על מערך באמצעות מצביעים5מצביעים ופונקציות
העברה לפי ערךהעברת מצביעים לפונקציותשינוי משתנים באמצעות מצביעיםדוגמה קלאסית: החלפההעברת מערכים לפונקציותחזרה: ארגומנטים של מצביעי פונקציה8מבנים ומצביעים
מצביעים למבניםאופרטור החץ (->)העברת מבנים לפי ערךהעברת מצביעים למבניםהקצאה דינמית של מבניםסיכום: שינוי מבנה באמצעות מצביע3מערכי תווים ומחרוזות
מחרוזות כמערכי charתו הסיום nullקלט מחרוזת באמצעות scanfשימוש ב־strlen()שימוש ב־strcpy()שימוש ב־strcat()שימוש ב־strcmp()סיכום: פונקציות מחרוזת בסיסיותתרגלו בעצמכם: קומפיילר C אונליין