وسام البهنسي

مدونة مبرمج معماري

مِحرف واحد بأربعة مَحارف

في لغة C و ++C نستطيع الإعلان عن متغيرات من نوع char، والتي تحمل محرفاً وحيداً بحجم بايت واحد فقط. كما نستطيع كتابة النصوص كسلسلة من المحارف المتتالية. كمثال:

char myChar = 'w'; // محرف وحيد
char anotherChar = '3'; // محرف وحيد آخر
char *text = "welcome"; // نص، سلسلة محارف

 

نلاحظ كيف أن المحرف يتم تحديده بوضعه بين فاصلتين أحاديتين كما في 'w' .  أما النصوص فتوضع بين علامتي تنصيص أو اقتباس "welcome" .  أعقد أن جميع من يبرمجون بلغة ++C يعرفون هذه المعلومات الأساسية. الآن إليكم الكود التالي:

int text = 'why?'; // نص، لكن بدون علامات اقتباس؟

 

جربوا وضع السطر أعلاه في برنامج ++C، وانظروا إن كان سيعتبره المترجم خطأ…

ها؟ ماذا وجدتم؟

لا توجد أية أخطاء ؟!

 

فعلاً! المترجم مصرّ على أن السطر صحيح ولا خطأ فيه. ولكن كيف؟ وما معنى هذا السطر الغريب؟ فما نعرفه أننا نستخدم الفاصلة الأحادية ' فقط عندما نودّ التعبير عن محرف وحيد، لكننا هنا كتبنا أربعة محارف دفعة واحدة! فما القصة؟

 

عندما انضممتُ إلى فريق EA كان مشروعي الأول هو لعبة الهوكي NHL2007، وقد تركز عملي آنذاك في مجال الرسوميات، وقد واجهتُ أسطراً مماثلة للسطر الغريب أعلاه في خضمّ كود اللعبة الشاسع، وتحديداً في القسم المسؤول عن قراءة ملفات الشخصيات في اللعبة، في تركيبة تشبه المثال التالي:

 

int recordID = GetNextRecordIDFromFile(characterFile); // Read from file
switch (recordID)
{
  case 'head':
    ReadHead(characterFile);
    break;
 
  case 'indx':
    ReadIndex(characterFile);
    break;
 
  case 'data':
    ReadData(characterFile);
    break;
}

 

أصبتُ بالدهشة من مرأى الكود، وطفقتُ أسأل زملائي المبرمجين عن صلاحيته. وقد أصيبوا هم أيضاً بالدهشة عندما رأوه، ولم يستطع أي منهم فهمه... باستثناء شخص واحد: سيرجي سافشنكو، والذي يشغل حالياً منصب التوجيه التقني للاستديو (CTO). وقد كان الجواب كالآتي:

 

هذه التركيبة تعني متغير عددي بسعة أربعة بايتات، حيث كل بايت يحمل قيمة محرف من المحارف الأربعة المكتوبة في جدول آسكي. أي أن 'head' هي طريقة أخرى للتعبير عن الرقم 1751474532. أما 'indx' فهي تقابل الرقم 1768842360 وهكذا.

 

يمكننا استخدام هذه التركيبة لترميز البيانات لكن بطريقة مقروءة. ففي المثال أعلاه، ملف المجسم يحتوي عدة أقسام، كالترويسة والفهرس والبيانات الأخرى. ونرمز لكل قسم بأربعة بايتات تخبرنا بنوعه. قيمة هذه البايتات تساوي إما 'head' أو 'indx' أو 'data' طبقاً لنوعية القسم.

 

آمل أن تستفيدوا من هذه الحيلة في برامجكم وملفاتكم الخاصة.

 

في النهاية أستعير المثل الشامي القائل: عشنا وشفنا ابتسامة

 

والسلام عليكم ورحمة الله

التعليقات (6) -

  • عبد اللطيف حاجي علي

    08/03/2010 06:34:59 م | الرد

    الآن السؤال الذي يطرح نفسه. هل من الحكمة استخدام هذه الطريقة وزيادة غموض الكود حتى يضطر كل من يقرأه إلى الذهاب إلى سيرجي وسؤاله عنها؟
    ثم هل هذا من C القياسية؟ مع أني أعتقد ذلك إلا أنني أعتقد أيضاً أنه ستكون هناك اختلافات في التنفيذ بين مختلف المترجمات. اختلافات في ترتيب الأرقام مثلاً والتحويل إلى signed types ("الأنواع المؤشورة" إذا كانت هذه ترجمتها). لست متأكداً لكن هذا يزيد من غموض الكود

  • wbahnassi

    14/03/2010 07:36:29 ص | الرد

    هل من الحكمة استخدام هذه الطريقة وزيادة غموض الكود حتى يضطر كل من يقرأه إلى الذهاب إلى سيرجي وسؤاله عنها؟
    الأمر نسبي. فالخبراء لن يجدوا أي حرج من استخدامها، أما العامة المساكين من المبرمجين أمثالي فسيسعدون بتعلم شيء جديد Laughing


    ثم هل هذا من C القياسية؟
    لم أقرأ مواصفات لغة سي، لكن كون هذا الكود يترجم بنجاح على 5 منصات لكل منها مترجم خاص فإن هذا دليل قوي على كونها ضمن المواصفات.


    مع أني أعتقد ذلك إلا أنني أعتقد أيضاً أنه ستكون هناك اختلافات في التنفيذ بين مختلف المترجمات
    أو على وجه التحديد بين المنصات. الموضوع موضوع ترتيب البايتات العليا في المتغيرات العريضة. إنتل بترتيب الانتهاء بصغير العدد (little endian) وباور بي سي بترتيب الانتهاء بكبير العدد (big endian).

    هي في النهاية حيلة صغيرة قد تفيدك وقد لا تفيدك في حياتك البرمجية. لكن لاحظ أننا استخدمنا هذا المبدأ كثيراً فيما سبق مع ما يدعى بالشفرات الرباعية FourCC Codes والتي تستخدم في تحديد ترويسات الأقسام في الملفات الصوتية بصيغة WAV مثلاً. فقط الفرق أننا نستخدم ماكرو لبناء الرمز Smile

  • بلال

    29/05/2010 03:44:06 ص | الرد

    السلام عليكم ورحمة الله وبركاته،
    لما قرأت هذه التدوينة تذكرت ايام السي والسي ++ من حوالي 15 سنة او اكثر. سبحان الله كيف مرت الأيام بهذه السرعة. يومها كان مترجم بورلاند (نسيت اسمها الحالي) يرفض مثل هذا التعريف ولكن مترجم مايكروسوفت يقبله.

    كما كانت هذه الأسئلة من الأسئلة الدائمة الحضور في كراسة الإمتحان لمعرفة مدى اهتمام التلامذة بدقائق المترجم والتكيف معه.

  • حسن أيوب

    18/08/2010 11:25:38 ص | الرد

    بصراحة صدمتني!!!
    كنت مفكرا نفسي ضليعا بالc++, ولكن مقالتك أرجعتني الى الصفر.
    ماشاء الله عليك وشكرا لك.

  • SIFE

    21/01/2011 08:01:44 م | الرد

    u_int  th_x2:4
    ما رأيك بهذا، إنه متغير th_x2 من نوع unsigned int بحجم 4 بايت، أول مرة رأيته تعجبت مثل ما تعجبت في تدوينتك هذه.
    هذا السطر موجود في ترويسة TCP في نواة FreeBSD 8.1، أنظر إلى السطر 55 هنا: fxr.watson.org/.../tcp.h?v=FREEBSD81

  • wbahnassi

    22/01/2011 11:07:33 ص | الرد

    u_int th_x2:4
    ما رأيك بهذا، إنه متغير th_x2 من نوع unsigned int بحجم 4 بايت،


    نعم، هذا الأسلوب ينفع فقط عند إعلان متغيرات داخل بنية struct أو صنف class. عندما تضع رقماً بعد اسم المتغير فإنك تحدد عدد البتات (وليس البايتات) للمتغير. بهذه الطريقة تستطيع الإعلان عن متغيرات بحجم بت واحد فقط. يعتمد محرك أنريل Unreal كثيراً على هذه الحيلة للإعلان عن متغيرات من النوع البولياني bool دون إضاعة الذاكرة، لأن متغير bool الاعتيادي يتراوح عرضه من بايت إلى 4 بايتات أحياناً.

أضف تعليقاً

Loading