آموزش Union ها در زبان C و تفاوت آنها با ساختارها

مقدمه

با سلام خدمت همه شما دوستان. در این جلسه قصد داریم در مورد  Union (یونیون ها)ها صحبت کنیم. این که چطور میشه اونها رو تعریف کرد. چطور میشه به اعضاشون دسترسی پیدا کرد و این که یونیون ها چه تفاوت هایی با Structure ها دارن. ما قبلا دو جلسه در مورد ساختارها صحبت کردیم که پیشنهاد میکنم برای فهم بهتر این مطلب ، ابتدا اون جلسات رو بخونید:

ایجاد union و دسترسی به اعضای آن

نحوه ایجاد یک Union :

union ها دقیقا مشابه با ساختارها تعریف میشن. تنها تفاوتشون اینه که به جای واژه struct باید از واژه union استفاده کرد. همین. در مثال زیر ما یک union تعریف کردیم .

union car
{
  char name[50];
  int price;
};

در مثال بالا ما یک union با نام car ایجاد کردیم که این union دارای دو پارامتر با نام های name و price هست که اولی از جنس char و دومی از جنس int هست. همونطور که میبینید ، تعریف union دقیقا مشابه با struct هست و تنها کافی است به جای struct ، از union استفاده کنیم.

نحوه ایجاد عنصرهای یک union :

همونطور که ساختارها دارای یک سری اعضا یا عنصر بودند ، union ها هم میتونن شامل یک سری عناصر باشن.  تعریف عناصر یونیون ها دقیقا مشابه با تعریف عناصر structure ها هست. مثال زیر رو ببینید:

union car
{
  char name[50];
  int price;
} car1, car2, *car3;

در مثال بالا ما سه عنصر با نام های car1 و car2 و اشاره گر car3 تعریف کردیم. البته میشه به شیوه زیر هم این سه عنصر رو تعریف کرد:

union car
{
  char name[50];
  int price;
};

int main()
{
  union car car1, car2, *car3;
  return 0;
}

همونطور که میبینید هیچ تفاوتی بین تعریف عناصر یونیون و عناصر struct وجود نداره.

دسترسی عناصر یک union به پارامترهای آن:

دیگه زیاد دور از ذهن نیست اگه بگیم دسترسی عناصر  یک یونیون  به پارامترهاشون دقیقا مشابه  ساختارها است. مثلا فرض کنید ما میخوایم به پارامتر price از عنصر car ، دسترسی پیدا کنیم. در این صورت از دستور زیر استفاده خواهیم کرد:

car1.price

به همین ترتیب ، اگه بخواید به پارامتر price از عنصر car3 که یک اشاره گر هست دسترسی پیدا کنید میتونید از دو روش زیر استفاده کنید:

(*car3).price
or;
car3->price

یعنی هم میتونید اشاره گر car3 رو درون یک پرانتز بذارید و هم میتونید از عملگر “<-” استفاده کنید.

تفاوت بین union ها و ساختارها :

اما با همه این شباهت ها ، یونیون ها و ساختار ها چه تفاوتی با هم دیگه دارند؟

درک تفاوت بین این دو ، یک موضوع مهم هست. اجازه بدید که این موضوع رو با یک مثال بررسی کنیم:

#include <stdio.h>
union unionJob
{
   //defining a union
   char name[32];
   float salary;
   int workerNo;
} uJob;

struct structJob
{
   char name[32];
   float salary;
   int workerNo;
} sJob;

int main()
{
   printf("size of union = %d", sizeof(uJob));
   printf("\nsize of structure = %d", sizeof(sJob));
   return 0;
}

 

نیازی به توضیح نیست ولی برای تکمیل بحث کد بالا رو توضیح خواهیم داد:

در کد بالا ابتدا یک یونیون و ساختار تعریف شده است که هر دوی اینها دارای پارامترهای یکسانی هستند. هر کدام نیز دارای یک عنصر می باشند که عنصر مربوط به یونیون با نام ujob و عنصر مربوط به structure با نام sJob مشخص شده. سپس در تابع main ، از برنامه خواسته شده است تا میزان اندازه های مصرفی مربوط به sJob و uJob در خروجی چاپ شود. اگر این کد را کامپایل و سپس اجرا کنید ، نتیجه به صورت زیر خواهد بود:

size of union = 32
size of structure = 40

همونطور که میبینید ، حافظه بیشتری به structure اختصاص داده شده است. پس خواهیم داشت:

تفاوت 1 : structure ها فضای بیشتری نسبت به union ها مصرف میکنند.

بذارید این موضوع رو دقیق تر بررسی کنیم. مقدار حافظه ای که برای ساختار sJob  مورد نیاز هست برابر میشه با جمع حافظه هایی که برای هر کدوم از پارامترهای اون عنصر مورد نیاز هست. ما برام نام( که از جنس آرایه است) به 32 بایت فضا نیاز داریم. برای حقوق ( که از جنس float است ) به 4 بایت نیاز داریم و برای متغیر worker_no که از جنس Int هست هم به 4 بایت فضا نیاز داریم. پس در مجموع به 40 بایت فضا نیاز است.

فضای مصرفی مورد نیاز برای structure ها 

اما میزان حافظه ای که یک union مصرف میکنه برابر ماکزیمم حافظه ای مصرفی پارامترهای اون هست. مثلا در اینجا آرایه بیشتر از همه حافظه مصرف میکنه ( 32 بایت) پس حافظه مصرفی کل union هم برابر همون 32 بایت میشه به عبارت ساده تر در Union ، یک فضای اشتراکی برای تمامی پارامترها در نظر گرفته میشه.

فضای مورد نیاز برای union ها

تفاوت 2 : در هر لحظه تنها میشه به یکی از اعضای (پارامترهای) یک union دسترسی داشت.

در ساختارها ما میتونیم به همه اعضا (پارامترهای) در هر زمانی که بخوایم دسترسی داشته باشیم ، اما در union ها در هر لحظه تنها میشه به یکی از اعضا ( پارامترهای) دسترسی داشت و بقیه پارامترها مقدار درستی رو در خودشون ذخیره نخواهد کرد. بذارید برای روشن شدن این موضوع یک مثال رو با همدیگه بررسی کنیم:

#include <stdio.h>
union job
{
   char name[32];
   float salary;
   int workerNo;
} job1;

int main()
{
   printf("Enter name:\n");
   scanf("%s", &job1.name);

   printf("Enter salary: \n");
   scanf("%f", &job1.salary);

   printf("Displaying\nName :%s\n", job1.name);
   printf("Salary: %.1f", job1.salary);

   return 0;
}

توضیح خط به خط برنامه:

خطوط 3 تا 7 : تعریف یک union با نام job و یک عنصر از آن T با نام job1 . این یونیون دارای سه پارامتر با نام های Name ، salary و workerNo می باشد.

خطوط 11 و 12: در این خطوط ابتدا عبارت” Enter name ”  در خروجی چاپ شده است و سپس از کاربر خواسته شده تا نامی را وارد کند و پس از وارد کردن نام ، آن را در job1.Name قرار می دهد.

خطوط 13 و 14: در این خطوط ابتدا عبارت” Enter salary ”  در خروجی چاپ شده است و سپس از کاربر خواسته شده تا عدد مورد نظر برای حقوق  را وارد کند و پس از وارد کردن عدد ، آن را در job1.salary قرار می دهد.

در خطوط 17 و 18 نیز ، مقادیر job1.name و job1.salary در خروجی نشان داده شده است.

نتیجه این کد پس از کامپایل و اجرا به صورت زیر می باشد:

Enter name 
Hillary
Enter salary
1234.23
Displaying
Name: f%Bary   
Salary: 1234.2

همونطور که مشاهده میکنید ، مقدار عبارت job1.name ارزشی نداره و یک چیز بی ربط در خروجی چاپ شده است و تنها مقدار job1.salary صحیح است. البته میشه این موضوع رو کمی دقیق تر هم تحلیل کرد:

در ابتدای برنامه که کاربر، مقدار Hillary را وارد میکند ، این مقدار در job1.name ذخیره می شود و سایر پارامترهای عنصر job1 ارزشی ندارند و مقادیر بی ربط را در خود ذخیره کرده اند.  سپس کاربرد حقوق را وارد میکند و مقدار حقوق در job1.salary قرار میگیرد و دوباره سایر پارامترهای عنصر job1 ارزشی ندارند و دارای مقادیر بی ربط می باشند. به همین علت در خروجی مقدار job1.salary درست نشان داده شده است ولی job1.name عبارت بی ربطی را نشان می دهد.

ارسال union ها به توابع:

ارسال یونیون ها به توابع هم دقیقا مشابه با ارسال ساختارها به توابع می باشد و هیچ تفاوتی ندارند. برای طولانی نشدن این جلسه ، از صحبت در مورد آن خودداری میکنیم ولی میتوانید از لینک زیر به جلسه مربوط به ارسال ساختارها به توابع ، این موضوع رو مطالعه کنید.

خوب دوستان. این جلسه هم به پایان رسید. مثل همیشه میتونید ما رو در اینستاگرام و یا تلگرام دنبال کنید:

اشتراک گذاری:
مطالب زیر را حتما بخوانید

7 دیدگاه

به گفتگوی ما بپیوندید و دیدگاه خود را با ما در میان بگذارید.

  • با سلام

    ممنونم از مطلب بسیار خوبتون.
    فقط یک سوال برای من پیش اومد.
    استفاده از union ها به جای define کردن معمولی متغیرها و یا struct، تنها به دلیل استفاده کمتر حافظه هستش یا دلایل دیگه ای هم داره ؟
    راستش من یکم تو کاربرد union و Struct دچار شک شدم. هنوز نتونستم درک کنم که واقعا چرا باید از اینا استفاده کنیم وقتی میتونیم راحت متغیرهارو Define کنیم ؟
    من تازه برنامه نویسی C رو شروع کردم و یک مبتدی هستم. ممنون میشم جواب بدین.

    با تشکر

    • با سلام.
      نکته اول : همانطور که خود شما هم به آن اشاره کرده اید استفاده کمتر از حافظه است. این موضوع شاید در نگاه اول که قصد داریم برنامه های ساده ای بنویسیم از اهمیت چندانی برخوردار نباشد ولی در برنامه های پیچیده تر و در کدهای بهینه تر حتما استفاده می شود. به همین علت است که کدهای پیچیده تر حتما موارد این چنینی را برای بهینگی دارند.
      نکته دوم :‌ بله شما میتوانید متغیرها را define کنید. ولی واقعیت این است که هدف شما از برنامه نویسی چیست و چقدر قصد دارید در آپ پیشرفت کنید. صرفا میخواهید یک برنامه بنویسید که کار کند یا میخواهید به صورت حرفه ای برنامه نویسی کنید و برنامه های دقیق و بهینه بنویسید. در صورتی که قصد برنامه نویسی حرفه ای دارید نیاز است تا این موارد حتما رعایت شوند.
      نکته سوم : union ها در واقع کمی شباهت به برنامه نویسی شی گرا دارند. در زبان های دیگر شی گرایی وجود دارد که البته در C وجود ندارد. اگر در C با مفهوم union ها وساختارها خوب آشنا شوید در سایر زبان های نیز میتوانید بهتر مفهوم شی گرایی را درک کنید.
      موفق باشید.

دیدگاهتان را بنویسید