C preprocessor الجزء 2

تحدثنا في الجزء الأول عن المفهوم النظري وأساسيات الاستخدام لـ C preprocessor وسنركز في هذا الجزء على أمور أكثر تقدماً.

function-like macros

تذكير: تسمى مجموعة الأسطر البرمجية المعرفة عبر define بـmacros .

يمكننا من خلال الC preprocessor إنجاز ما يشبه التوابع ولذلك تسمى function-like مع اختلاف جوهري بين التوابع و function-like macros، شكل الشبَه الوحيد هو إمكانية إنجاز macros يمكن أن تأخذ وسطاء، أما من الناحية التنفيذية فليس هناك أي وجه شبه:

  • function : كتلة من الكود البرمجي يمكن أن نستدعيها بأي مكان من البرنامج الأساسي لتقوم بالتنفيذ واستخدام الوسطاء في حال وجودها مع إمكانية إرجاع قيمة بعد التنفيذ.

  • Function-like macros : مجموعة من الأسطر البرمجية المختصرة برمز، و عند إيراد هذا الرمز في الكود فهو بمثابة إيراد هذه الأسطر البرمجية كما هي، بخلاف مبدأ التوابع المعتمد على الاستدعاء مع وجود الكود دون تكرار .

لابد التنويه إلى الجانب السلبي لاستخدام الC preprocessor وهو حجم البرنامج النهائي، إذا أن استخدام الC preprocessor لإنجاز ما يشبه التوابع function-like macros يؤدي إلى تضاعف حجم البرنامج، فكما قلنا في الجزء الأول استخدام الC preprocessor لا يتعدى في النهاية عن اختصار أسطر برمجية بكلمات مفتاحية ولكنها من ناحية الأداء لا تختلف لو كتبنا الأسطر البرمجية بدل الكلمات المفتاحية، بعكس التوابع التي مهما كررنا استدعاءها فإن الكود واحد لا يتكرر.

النحوية function-like macros syntax

لإنجاز macros يأخد وسيط يجب أن نتبع اسم المايكرو (الرمزبأقواس متوسطة تحوي أسماء الوسطاء مباشرة ، دون تحديد نوعها كما في التوابع، إذ لا يهم الfunction-like macros نوع الوسطاء إذ لا تتعامل معها كوسطاء حقيقيين وإنما كقيم مجردة سيتم تمريرها إلى تعريف الmacros.

عودة إلى الكود في نهاية الجزء الأول

سنأخذ أحد الfunction-like macros كمثال:

اسم macro هو BIT_SET والوسطاء هم ADDRESS,BIT وعند الاستخدام نمرر الوسطاء التي نريد، مثال:

وهذا مكافئ تماماً للتالي:

ملاحظات في function-like macros :

1- function-like macros لا تقوم بعمليات حسابية للوسطاء.

لنفترض الfunction-like macro التالي:

ولنفرض استخدامها كالتالي:

كما قلنا C preprocessor لا يقوم بتنفيذ الكود وبالتالي لن يقوم بالعمليات الحسابية r+1 أو r+2 لأن الـpreprocessor لا يستطيع التنفيذ مهما كانت العملية بسيطة ، وهذا مكافئ للتالي :

وبالتالي لتجنب هذا الخطأ يجب أن نستخدم الأقواس ()، كالتالي:

وعند الاستخدام السابق، ستكون النتيجة بعد مرحلة preprocessor:

2-الأقواس تتبع اسم macro مباشرة.

لنفرض الحالتين التاليتين

إن الأول هو function-like أما الثاني فهو macro عادي كما لو أننا عرفنا الرمز sum فقط، لذلك يجب الانتباه أن نضع الأقواس مباشرة بعد اسم الmacro في حال وجود الوسطاء.

3-C preprocessor scoop

عند ذكر عبارات تعريف متحولات داخل الmacro يجب الانتباه أنها لا تشابه التوابع من ناحية إنشاء local variables، ففي التوابع عند تعريف متحول يبقى مجال رؤية واستخدام المتحول محصور داخل التابع نفسه، أما الC preprocessor فهي كما قلنا لا تتعدى اختصار مجموعة أكواد برمز وعند ذكر الرمز نكون ضمنا هذه الأسطر ضمناً وبالتالي وجب الانتباه ومن أحد الحلول هي إنشاء كيان وهمي بضمن تعريف المتحولات بشكل محلي وذلك باستخدام do while(0)  كالتالي.

فالأسلم أن نقوم بتضمين التعليمات بتعليمة do while(0) ، حيث من المعلوم أن هذه التعليمة ستنفذ مرة واحدة فقط وهذا يفي بالغرض من حيث ضمان أن تكون المتحولات المعرفة داخله محلية وايضاً نفس الأداء لو ذكرنا التعليمات لوحدها. فكما نعلم أن هذه التركيبة يتم تنفيذ جسمها do قبل فحص الشرط في while وبالتالي وباعتبار الشرط خاطئ فإنه سيتم التنفيذ مرة واحدة.

من أخطاء الscoop أيضاً عدم الانتباه لحالة كالتالي:

إن هذا مكافئ لتنفيذ أول تعليمة فقط من تعريف swap، أي تكافئ تعريف المتحول temp وتنفيذ البقية خارج الشرط :

لحل هذه المشكلة يمكن أن نقوم بأحد الإجرائين:

الحل الأول: تغيير طريقة ذكر اسم الـmacro

الحل الثاني: تغيير طريقة تعريف الـmacro

 

استخدام الـ (##)concatenation

يستخدم الرمز ## في أخذ الوسطاء وإضافتها للأوامر التوجيهية directives

مثال :

ويمكننا استخدامها كالتالي:

 

في الجزء الثالث والأخير سنتحدث عن استخدام الـC preprocessor في الترجمة الشرطية conditional compilation بالإضافة إلى إيراد بعض الأمثلة العملية.