تماس درباره   صفحه اصلی
  زبان ++C > پيش پردازنده ها و ماکروها  
 
 

پيش پردازنده ها و ماکروها


دستورات پيش پردازنده که اغلب برای تعريف ثابت و ماکرو يا ضميمه کردن فايل بکار می روند قبل از ترجمه برنامه تفسير و تغييراتی را در متن برنامه ايجاد می کنند.

پيش پردازنده
راهنماها
ماکروها


پيش پردازنده

پيش پردازنده (preprocessor) بخشی از کامپايلر است که قسمت هائی از برنامه را، قبل از اينکه کل آن توسط کامپايلر ترجمه شود، مورد ارزيابی قرار می دهد. برنامه نويس می تواند دستوراتی را در برنامه خود درج کند که مستقيما پيش پردازنده را فراخوانی کند به اين دستورات پيش پردازنده می گويند. پيش پردازنده ها ممکن است باعث تغيير متن برنامه قبل از تحويل آن به کامپايلر شوند.

قبلا از پيش پردازنده ها در ضميمه کردن فايل يا تعريف ثابت استفاده کرده ايد.

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

• راهنماها
• ثابت ها
• ماکروها


راهنماها

راهنماها (directives) دستوراتی هستند که توسط برنامه نويس به پيش پردازنده داده می شوند تا عمل خاصی را انجام دهد. برای مثال ثابتی را در متن جايگزين کند، محتوای فايل ديگری را در فايل مبدا درج کند يا بخشی از کد را ترجمه نکند.

راهنماها باعث می شوند متن برنامه به سادگی تغيير کند و درمحيط های مختلف قابل کامپايل باشد.

خطوط پيش پردازنده قبل از بسط ماکروها تشخيص و اجرا می شود. بنابراين اگر ماکرو به چيزی مشابه يک فرمان پيش پردازنده بسط داده شود دستور توسط پيش پردازنده تشخيص داده نمی شود.

پيش پردازنده راهنماهای زير را تشخيص می دهد:

#define #error#include#if
#else#elif #endif #ifdef
#ifndef#import#line#pragma
#undef#using   

نکته. راهنماها هميشه با علامت # (sharp sign) شروع می شوند.
نکته. بين علامت # و اولين کاراکتر راهنما می تواند فاصله باشد.
نکته. بعضی راهنماها شامل آرگومان ها و مقادير هستند. هر متن ديگری بجز آرگومان و مقدار که به دنبال راهنما می آيد ابتدايش بايد علامت توضيح باشد (//) يا در بين علائم (/* */) قرار بگيرد.
نکته. خط شامل راهنما می تواند با علامت انتهای خط (\) ختم شود.
نکته. راهنماها می توانند در هرجائی از فايل منبع قرار بگيرند اما تنها روی بقيه کد تاثير می گذارند.

ثابت ها

همانطور که در بخش ثابت ها توضيح داده شده است از راهنمای #define می توان برای تعريف يک ثابت سمبليک استفاده کرد. شکل کلی تعريف ثابت به صورت زير است:

#define [identifier name] [value]

پيش پردازنده قبل از کامپايل در متن برنامه شناسه Identifier name را با مقدار value جايگزين خواهد کرد.

اگر ثابت را به صورت يک عبارت محاسباتی تعريف کنيد بهتر است مقدار را درون پرانتز قرار دهيد. به اين ترتيب الويت عمليات حفظ خواهد شد.


مثال. تعريف ثابت زير در نظر بگيريد:

#define PI_PLUS_ONE (3.14 + 1)

که به صورت زير استفاده می شود:

x = PI_PLUS_ONE * 5;

اگر پرانتزها را قرار ندهيم عبارت به صورت زير محاسبه خواهد شد، يعنی ابتدا عمل 1*5 انجام می شود سپس عمل جمع.

x = 3.14 + 1 * 5;


ضميمه کردن فايل

راهنمای include# به پيش پردازنده می گويد که متن يک فايل را بگيرد و در برنامه جاری درج کند. معمولا راهنمای include# در ابتدای برنامه قرار می گيرد. به همين دليل نام header file به فايل هائی که ضميمه می شوند گفته می شود.

نام فايل هدر در مقابل راهنمای include# قرار می گيرد. درج نام فايل بين علائم < > يا " " نحوه جستجوی فايل را مشخص می کند. اگر نام فايل بين علائم < > محصور باشد کامپايلر فايل را در مسيرهای تعيين شده در بخش تنظيمات کامپايلر جستجو می کند ولی اگر نام فايل مابين علائم " " قرار گيرد کامپايلر آنرا ابتدا در مسير جاری برنامه جستجو می کند.

فايل های هدر استاندارد نظير iostream.h بين علائم < > محصور می شوند.

کامپايل شرطی

مجموعه ای از راهنماها وجود دارند که تعيين می کنند آيا خطوط برنامه قبل از تحويل به کاميايلر حذف شوند يا خير. اين راهنماها شامل #if، #elif، #else، #ifdef و ifndef# هستند.

يک بلوک شرطی که با يکی از راهنماهای شرطی شروع می شود حتما بايد به endif# ختم شود. بهتر است مقابل عبارت endif# توضيحی باشد که مشخص شود کدام بلوک شرطی بسته شده است.

يکی از کاربردهای راهنماهای شرطی وقتی است که فايل هدری در چند فايل هدر ديگر که بايد در برنامه اصلی ضميمه شوند مورد نياز است. مشکلی که پيش می آيد اين است که متغيرها، ثابت ها، کلاس ها و توابع فايل هدر چندبار در برنامه ظاهر خواهند شد که سرباری برای کامپايلر می شود. با پيش پردازنده ها به راحتی می توان تضمين کرد که هر فايل هدر تنها يکبار در برنامه اصلی اضافه می شود. راهنمای (if not defined) #ifndef بلوکی از متن را تنها اگر عبارت خاصی قبلا تعريف نشده باشد اجرا می کند.

کدی که در idndef# ضميمه می شود تنها يکبار زمانی که فايل لود می شود بار می شود.

#ifndef _FILE_NAME_H_
#define _FILE_NAME_H_
   /* code */
#endif // #ifndef _FILE_NAME_H_

شناسه ای که مقابل ifndef# ذکر می شود لازم نيست حتما مقداری داشته باشد زيرا با اضافه شدن خط define# تعريف می شود.


مثال. تعريف ثابت خاص NULL توسط راهنماهای شرطی.

#ifndef NULL
#define NULL (void *)0
#endif // #ifndef NULL

مثال. راهنماهای شرطی برای بلوکی که شامل توضيحات چندخطی است و می خواهيد همگی توضيحی شوند روش خوبی است.

#if 0
/* comment ...
*/
// code
/* comment */
#endif


ماکروها

همانطور که گفته شد با راهنمای define# يک شناسه را می توان به يک ثابت نسبت داد تا يک ثابت سمبليک شکل بگيرد. از راهنمای define# برای مربوط کردن شناسه های معنی دار به عباراتی که زياد استفاده می شوند هم بکار می رود. وقتی يک شناسه به يک عبارت يا جمله ربط داده می شود ماکرو (macro) ناميده می شود.

شکل کلی تعريف ماکرو به صورت زير است:

#define MACRO_NAME(arg1, arg2, ...) [code to expand to]

arg1، arg2 و ... آرگومان های ماکرو هستند که تعداد آنها اختياری است.


مثال. ماکرو افزايش عدد.

#define INCREMENT(x) ((x)++)

مثال. ماکرو مربع عدد.

#define SquareOf(x) ((x)*(x))
double yout,xin=3;
yout = SquareOf(xin);


ماکرو می تواند چندخطی باشد برای اتصال خطوط در انتهای هر خط بايد علامت \ قرار بگيرد. خط آخر نيازی به علامت \ ندارد.


مثال. ماکرو جابه جا کردن دو مقدار.

#define SWAP(a, b) { \
    a ^= b; \
    b ^= a; \
    a ^= b; \
}


وقتی پيش پردازنده با نام ماکرو در برنامه مواجه می شود آن را يک فراخوانی به ماکرو تلقی می کند و يک کپی از بدنه ماکرو را جايگزين نام ماکرو می کند. اگر ماکرو دارای پارامترهائی باشد، آرگومان هائی که بدنبال نام ماکرو قرار دارند جايگزين آن ها در بدنه ماکرو می شوند.

در عمل دو نوع ماکرو وجود دارد: شیء گونه که پارامتری ندارد و تابع گونه که می تواند آرگومان هائی را بپذيرد و بسيار شبيه فراخوانی تابع می ماند.

يک ماکرو را می توان دوباره با همان مقدار قبلی تعريف کرد ولی تعريف مجدد ماکرو با مقدار جديد ممکن نيست. مگر اينکه تعريف قبلی را حذف کنيد. راهنمای undef# برای حذف تعريف يک ماکرو است.

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

الويت عبارت

يک راه برای اطمينان از اينکه الويت عبارت در ماکرو حفظ می شود اين است که آرگومان های ماکرو را درون پرانتز قرار دهيد.


مثال. اگر ماکرو به صورت زير تعريف و استفاده شده باشد انتظار داريد مقدار 30 به متغير z اختصاص داده شود درحاليکه مقدار 13 را می گيرد.

#define MULT(x, y) x * y
int z = MULT(3 + 2, 4 + 2);

زيرا وقتی بدنه ماکرو جايگزين می شود به صورت زير در می آيد.

int z = 3 + 2 * 4 + 2;

برای حل اين مشکل بهتر است ماکرو به صورت زير نوشته شود.

#define MULT(x, y) ((x) * (y))

که به صورت زير جايگزين خواهد شد.

int z = (3 + 2) * (4 + 2)


ماکروهای چند دستوری

ماکروهائی که مشابه تابع مقداری را محاسبه و برمی گردانند expression macro ناميده می شوند. و ماکرو می تواند به جای اينکه مقداری را محاسبه کند دارای چند دستور باشد که آنها را action macro می نامند.

بهتر است بدنه ماکروهای چنددستوری را درون علائم {} محصور کنيد تا از بروز مشکل جلوگيری کنيد.


مثال. وقتی ماکرو فراخوانی شود تنها اولين دستور آن a^=b; درون شرط قرار می گيرد و دو دستور ديگر همواره اجرا خواهند شد.

#define SWAP(a, b) a ^= b; b ^= a; a ^= b;
int x = 10;
int y = 5;
SWAP(x, y); // works OK
// What happens now?
if(x < 0)
SWAP(x, y);

بهتر است ماکرو فوق به صورت زير تعريف شود:

#define SWAP(a, b) do { a ^= b; b ^= a; a ^= b; } while ( 0 )


نکته. قرار دادن کل بدنه ماکرو درون پرانتز زمانی که ماکرو مقداری را برنمی گرداند الزامی نيست.
نکته. در ++C از ماکروها تا حد ممکن استفاده نمی شود چون در اکثر موارد نيازی به آنها ديده نمی شود. بيشتر از عبارت const برای تعريف ثابت و توابع درون خطی به ماکروها ترجيح داده می شوند زيرا مشکلات ماکروها را ندارند.


مثال. تابع درون خطی محاسبه مربع عدد.

void SquareOf(int x) {return x*x;}


نکاتی درباره نوشتن ماکرو

1- استفاده از يک قرارداد نامگذاری برای اسامی ماکرو تشخيص آنها را در برنامه ساده تر می کند. برای مثال نام همه ماکروها با حرف m شروع شود.
2- درنظر داشته باشيد چه نوع ماکروئی می نويسيد عبارتی يا چنددستوری.
3- کل بدنه ماکرو را در پرانتز قرار دهيد.
4- در بدنه ماکرو آرگومان ها را درون پرانتز قرار دهيد.
5- در ماکروهایچنددستوری هر دستور را به علامت سميکولن ختم کنيد و کل بدنه ماکرو را درون آکولاد قرار دهيد.
6- کليه ماکروها را در يک فايل هدر قرار دهيد.

ماکروهای تعريف شده

تعدادی ماکرو در ++C تعريف شده است که می توانيد از آنها استفاده کنيد. کامپايلر قادر به شناسائی ماکروهای پيش تعريف شده است. اين ماکروها آرگومانی نمی گيرند و مجدد قابل تعريف نيستند.

بعضی از آنها که توسط ANSI تعريف شده است در جدول زير آمده است:

شرحماکرو

 


 


صفحه اصلی| PDF| درباره| تماس