ברוכים הבאים ליחידת הלימוד "טיפוסי נתונים מורכבים" בקורס "מעבדה בתכנות מערכות" (20465). ביחידה זו נצלול לעומק היכולת של שפת C לבנות טיפוסי נתונים מותאמים אישית, מעבר לטיפוסים הפרימיטיביים המוכרים. הבנה יסודית של מבנים, איחודים, שדות סיביות ו-typedef חיונית לפיתוח מערכות מורכבות, ניהול זיכרון יעיל ותקשורת עם חומרה.
מבנים (Structs): אבני הבניין של נתונים מורכבים
מבנים הם הדרך העיקרית ב-C לאגד מספר משתנים, שיכולים להיות מטיפוסים שונים, תחת שם אחד. הם מאפשרים לנו לייצג ישויות מורכבות מהעולם האמיתי באופן קוהרנטי.
הצהרה, אתחול וגישה
- הצהרה: מגדירים את מבנה הנתונים, אך לא מקצים זיכרון.
struct Point { int x; int y; }; - הקצאה ואתחול:
struct Point p1 = {10, 20}; // אתחול בעת הגדרה struct Point p2; p2.x = 5; p2.y = 15; - גישה לחברים:
- באמצעות אופרטור הנקודה (
.) עבור משתני מבנה רגילים. - באמצעות אופרטור החץ (
->) עבור מצביעים למבנים.struct Point *ptr_p1 = &p1; printf("p1.x = %d, p1.y = %d\n", p1.x, ptr_p1->y);
- באמצעות אופרטור הנקודה (
- ריפוד (Padding): המהדר עשוי להוסיף בתים "ריקים" בין חברי המבנה כדי ליישר אותם לגבולות זיכרון מסוימים, מה שמשפר את ביצועי הגישה אך מגדיל את גודל המבנה.
איחודים (Unions): חסכון בזיכרון וגמישות
איחודים דומים למבנים בכך שהם מאגדים חברים מטיפוסים שונים, אך יש הבדל קריטי באופן אחסונם בזיכרון: כל החברים באיחוד חולקים את אותו אזור זיכרון. המשמעות היא שרק אחד מהחברים יכול להכיל ערך תקף בכל רגע נתון.
מבנה (Struct)
כל החברים במבנה מקבלים מקום זיכרון נפרד משלהם. גודל המבנה הוא סכום גדלי החברים (בתוספת ריפוד).
איחוד (Union)
כל החברים באיחוד חולקים את אותו מקום זיכרון. גודל האיחוד הוא כגודל החבר הגדול ביותר.
שימושים ודגשים
- חיסכון בזיכרון: שימושי כאשר יש לנו נתונים שיכולים להיות מכמה סוגים שונים, אך לעולם לא יהיו פעילים בו זמנית (לדוגמה, נתונים של צורה גיאומטרית שיכולה להיות עיגול, מלבן או משולש).
- גישה לחברים: כמו במבנים, באמצעות
.או->.
enum או int) המציין איזה חבר באיחוד פעיל.שדות סיביות (Bit Fields): שליטה ברמת הסיביות
שדות סיביות מאפשרים לנו להקצות מספר ספציפי של סיביות (bits) לחברים בתוך מבנה או איחוד. זהו כלי חזק לאופטימיזציה של זיכרון ולעבודה עם חומרה ברמה נמוכה.
תחביר ושימושים
- תחביר:
type member_name : width;כאשרwidthהוא מספר הסיביות.struct Flags { unsigned int is_valid : 1; unsigned int has_error : 1; unsigned int status_code : 3; // 0-7 }; - חיסכון בזיכרון: במקום להשתמש בבתים שלמים עבור דגלים (flags) או ערכים קטנים, ניתן לארוז אותם לסיביות בודדות או קבוצות קטנות של סיביות.
- תקשורת חומרה: שימושי מאוד בעבודה עם רגיסטרים של חומרה, שבהם כל סיבית או קבוצת סיביות קטנה מייצגת פונקציונליות ספציפית.
- מגבלות:
- ניידות (Portability): אופן האריזה של שדות סיביות תלוי מהדר וארכיטקטורה.
- לא ניתן לקחת כתובת של שדה סיביות (אין לו כתובת זיכרון עצמאית).
- התנהגות חתימה (signed/unsigned) יכולה להיות מבלבלת. עדיף להשתמש ב-
unsigned int.
הגדרת טיפוסים (typedef): פשטות וקריאות קוד
typedef מאפשר לנו ליצור כינויים (aliases) לטיפוסי נתונים קיימים. זה לא יוצר טיפוס חדש, אלא רק שם חלופי, אך הוא משפר משמעותית את קריאות הקוד, מפשט הצהרות מורכבות ומסייע בניידות.
יתרונות ודוגמאות
- פישוט הצהרות מורכבות: במיוחד עבור מצביעים לפונקציות או מבנים מקוננים.
// בלי typedef struct Node { int data; struct Node *next; }; // עם typedef typedef struct Node { int data; struct Node *next; } Node; // מעכשיו אפשר להשתמש ב-Node במקום struct Node // דוגמה נוספת: מצביע לפונקציה typedef int (*OperationFunc)(int, int); // OperationFunc add_op = &add; (כאשר add היא פונקציה מתאימה) - שיפור קריאות הקוד: שמות טיפוסים מותאמים אישית יכולים להפוך את הקוד לברור ומובן יותר.
- ניידות (Portability): ניתן להגדיר טיפוסי נתונים באופן שישתנה בהתאם לפלטפורמה, מבלי לשנות את הקוד הראשי. לדוגמה,
typedef unsigned long long uint64_t;.
שאלות לדיון
- הסבר מתי ולמה נבחר להשתמש במבנה (struct) לעומת איחוד (union). תאר מצב בו שניהם נחוצים יחד.
- כיצד שדות סיביות (bit fields) תורמים לאופטימיזציה של זיכרון, ומהם השיקולים שיש לקחת בחשבון בעת השימוש בהם?
- תן דוגמה למצב בו השימוש ב-
typedefמשפר משמעותית את קריאות הקוד או את ניידותו, והסבר מדוע. - תאר את ההבדל בייצוג בזיכרון בין מבנה המכיל שלושה שדות מסוג
intלבין איחוד המכיל שלושה שדות מסוגint.
נקודות לתשובת מודל
- Struct vs. Union: מבנה מאגד נתונים קשורים אך שונים, כשכל חבר תופס מקום משלו. איחוד מאגד נתונים שונים שחולקים את אותו מקום זיכרון, ורק אחד פעיל בכל רגע. נמצאתם טעות או שחסר משהו?