שפת C/איגודים

מתוך ויקיספר, אוסף הספרים והמדריכים החופשי
קפיצה לניווט קפיצה לחיפוש

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


Edit-undo.svg

שקול לדלג על נושא זה

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



מהו איגוד?[עריכה]

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

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

דוגמה לשימוש באיגוד[עריכה]

להלן תוכנית קצרה המשתמשת באיגוד:

#include <stdio.h>
#include <string.h>


union foo
{
  int a_number;
  
  char a_string[20];
};


int main()
{
  union foo f;
  
  f.a_number = 3; 
  printf("%d\n", f.a_number);

  strcpy(f.a_string, "hello");
  printf("%s\n", f.a_string);

  return 0;
}


ראשית, מגדירים טיפוס חדש, foo:

union foo
{
  int a_number;
  
  char a_string[20];
};

הטיפוס יכול להכיל בכל יחידת זמן, מספר a_number, או מחרוזת a_string.

בתוך הפונקציה main, מגדירים משתנה מטיפוס foo:

union foo f;

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

  
f.a_number = 3; 
printf("%d\n", f.a_number);

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

  
strcpy(f.a_string, "hello");
printf("%s\n", f.a_string);

איגודים וזיכרון המחשב[עריכה]

נתבונן בקטע הקוד הבא:

union foo
{
  short int c;
  
  int m;
};

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

100%

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

100%

הסכנה באיגודים[עריכה]

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

 
strcpy(f.a_string, "hello");

printf("%d\n", f.a_number);

השורה הראשונה מעתיקה מחרוזת לשדה a_string של f, והשורה הבאה משתמשת בערך השדה a_number של a. מהו ערך זה? השורה הראשונה דרסה כל ערך קודם שהיה במשתנה, והוא מכיל כעת זבל.

איגודים כשדות מבנים[עריכה]

איגודים, ככל טיפוס אחר, יכולים להיות טיפוסי השדות של מבנים. כך, לדוגמה, בקוד הבא:

union a
{
  int b;
 
  char c;
};

struct d
{
  float f;

  union a e;
};

e הוא שדה של struct d, וטיפוסו הוא union a.


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

union foo
{
  int a_number;
 
  char a_string[20];
};

struct foo
{
  int is_number;

  union foo f;
};

השדה is_number מתאר האם השדה f מכיל כרגע מספר או מחרוזת.


הפרק הקודם:
ניהול זיכרון דינאמי
איגודים הפרק הבא:
פלט וקלט קבצים