لغة سي و متحكماتمقالات

لغة سي المضمّنة: Struct و Union

في هذا المقال سنتحدث عن بنى معطيات متقدمة في لغة C وهي struct و union. إن استيعاب كيفية التعامل مع أنواع المعطيات هذه أساسي في برمجة C بشكل عام، لكنها أكثر أهمية في برمجة المتحكمات والبرمجة المضمنة embedded programming لأنها تعطي المطور إمكانيات تسهل كتابة البرامج المضمنة بشكل خاص. في الجزء الثاني سنتحدث عن محاذير الاستخدام وبعض الأمثلة العملية.

البنى structures أو struct

يمكن النظر إلى البنية structure على أنها وسيلة لتجميع عدد من المتحولات ذات أنواع مختلفة في كيان واحد يبدأ بعنوان واحد في الذاكرة. يشار إلى البنية في لغة C بـ struct:

struct example_struct

  {

     unsigned int member1;

     float member2;

     char* member3;

  };

ملاحظة: انتبه للفاصلة المنقوطة بنهاية التعريف

تسمى المتحولات المختلفة داخل البنية بعناصر البنية أو members.  البنية struct بحد ذاتها هي نوع من أنواع المتحولات ولكنها تختلف عن غيرها بأنها متحولات مخصصة custom فبعد أن يتم تعريف الـ struct يمكن عندها انشاء متحولات من نوع الـ struct الجديدة:

struct example_struct foo;

struct example_struct bar = {4,1.4,”hello world”};

يمكنك أيضاً التصريح declare وتعريف متحول define من نوع strcut في آن واحد كما يلي:

struct example_struct
   {
      unsigned int member1;
      float member2;
      char* member3;
   } foo, bar;

يمكن الآن الوصول لأجزاء البنية members  باستخدام النقطة “.” في المتحولات العادية :

struct example_struct foo;

foo.member1=2;

أما في حالة المؤشرات فنستخدم “<-“

struct example_struct *foo;

foo->member1=2;

ملاحظة:الـ struct  نفسها عبارة عن قالب template فقط ولا يقوم الـ compiler بتخصيص أي جزء من الذاكرة عند تعريف البنية struct  وإنما يقوم بذلك فقط عند تعريف متحول من نوع الـ struct الجديدة:

struct example_struct foo;

(استخدام كلمة struct هنا إلزامي و سنبين طريقة للتخلص منها لاحقاً في التدوينة)

أحياناً لا نحتاج إلا لعدد معين من البتات لتخزين القيم في عناصر البنية، وفي هذه الحالة يمكن استخدام حقول البت bit fields والتي صممت من أجل تخفيض مساحة التخزين المطلوبة إلى حدها الأدنى ويمكن تعريف حقول البت هذه باستخدام عناصر من أعداد صحيحة int ويتم تحديد عدد البتات باستخدام “:” كما يلي:

struct example_struct

  {

     int element_a : 1;

     int element_b : 3;

     int element_c : 2=0;

  };

في المثال السابق element_a بطول بت واحد و element_b بطول ثلاث بتات وelement_c بطول 2 بت وبقيمة افتراضية 0.

على الرغم أن استخدام الـbit fields ليس موصى به لأنه يعد غير مرجعي standard بين المترجمات compilers المختلفة حيث يمكن أن يختلف طريقة التعامل معه ولكنه يبقى أحد التقنيات المستخدمة ولكن يجب أن تكون مراعياً لهذه الجزئيّة.

الـ union

في بعض الأحيان نحتاج لمتحول نستطيع النفاذ له بطرق (أنواع معطيات) مختلفة ولكن في بمساحة تخزينيّة واحدة أي دون وجود حاجة لتكرار مساحة التخزين وهنا يأتي دور الـ union.

إن طريقة تعريف الـ union مشابهة لطريقة تعريف البنية struct

Union union_tag

  {

     unsigned int member1;

     Float member2;

     char* member3;

  } union_name;

أيضا طريقة الوصول لقيمة أحد العناصر مشابه تماما للبنية

union union_name foo;

foo.member1=2

وفي حالة المؤشرات

struct union_name *foo;

foo->member1=2;

الفارق الأساسي بين النوعين هو طريقة تخزين البيانات فالـ union يحجز مكانا في الذاكرة بمقدار أطول عنصر من عناصر الـ union على عكس الـ struct والتي تحجز بمقدار مجموع جميع العناصر.

struct vs union

قد لايكون استخدام الـ union كبيرا في الأنظمة البرمجية إلا أن استخدامه مهم جداً في الأنظمة المضمنة فهو غالباً مايستخدم كنوع من الذاكرة المؤقتة (مسودة) لتخزين قيم ذات أنواع مختلفة في مكان الذاكرة ذاته عندما لا نحتاج استخدام اثنين من هذه القيم في الوقت ذاته والاستخدام بهذا الشكل يساعد في تخفيض الذاكرة التي يحتاجها البرنامج وهذا من أهم العوامل في البرمجة المضمنة كما هو معروف.

union {

int as_int;

long as_long;

char as_char;

short as_short;

} scratch_pad;

الاستخدام الرائج الآخر هو تسهيل استخراج قيم أصغر من قيمة كلية أكبر كما في المثال

union {

uint32 val; // Timer Register 32-bit-long

struct {

uint8 Three;

uint8 Two;

uint8 One;

uint8 Zero;

} Bytes;

} TMR;

في المثال السابق يمكن الوصول للقيمة الكلية كـ32 بت دفعة واحدة TMR.val أو باستخدام أجزاء أصغر TMR.Bytes.Zero.

استخدام التعليمة typedef مع struct أو union

تسمح لغة C بإعطاء اسم بديل  جديد بناءً على أنواع المتحولات المعروفة باستخدام التعليمة typedef فمثلاً

typedef unsigned long ulong;

 

هنا قمنا بتعريف اسم بديل alias هو ulong بدلاًعن النوع unsigned long والآن يمكننا استخدام هذا الـ alias لتعريف متحول باسم var  له النوع unsigned long كما يلي:

ulong var;

 

يعالج ال compiler هذه التعليمة بينما التعليمة #define المشابهة لعملها فيتعامل معها المعالج البدائي preprocessor كما تحدثنا في مقالة سابقة بشكل تفصيلي.

نفس الفكرة يمكن تطبيقها مع struct أو union. سنأخذ مثال على struct:

typedef struct
{
 type member_a;
 type member_b;
 type member_c;
} sname ;

الآن يمكن استخدام sname لتعريف متحولات s1,s2 من نوع الـ struct السابق كما يلي:

sname s1,s2;

بنفس الطريقة تماما يمكن استخدام الـ typedef مع الـ union.

المراجع

Nour Taweel

مهندسة حواسيب من جامعة دمشق ومختصة في مجال برمجة الموبايل وتطبيقاته ولها اهتمامات أخرى في لغات برمجة عديدة. نور هي المسؤولة التقنية في الموقع

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

هذا الموقع يستخدم Akismet للحدّ من التعليقات المزعجة والغير مرغوبة. تعرّف على كيفية معالجة بيانات تعليقك.

زر الذهاب إلى الأعلى