خوش آموز درخت تو گر بار دانش بگیرد، به زیر آوری چرخ نیلوفری را


آموزش زبان ++C : مروری بر پیش پردازنده ها (preprocessor)

آموزش زبان ++C : مروری بر پیش پردازنده ها (preprocessor)
نویسنده : امیر انصاری
بهترین راه درک پیش پردازنده (preprocessor) اینست که به آن به منزله یک برنامه جداگانه که قبل از برنامه اصلی توسط کامپایلر اجرا می شود، نگاه کنید. وقتی که پیش پردازنده (preprocessor) اجرا می شود، به سادگی تمامی فایل ها را بررسی می کند تا دنبال دستورات پیش پردازنده (directives) بگردد. دستورات پیش پردازنده (directives) دستورالعمل های خاصی هستند که با نماد # آغاز می شوند و انتهای آنها با یک خط جدید (newline) و نه با سمی کالن، خاتمه می یابد. چندین نوع دستورات پیش پردازنده (directives) مختلف وجود دارد که در ادامه به آنها می پردازیم.



دستورات پیش پردازنده (directives) هوشمند نمی باشند و گرامر زبان ++C را درک نمی کنند، بلکه، کاری که می کنند اینست که قبل از اجرای کامپایلر، تغییراتی را در متن ایجاد می کنند. سپس متن دستکاری شده توسط دستورات پیش پردازنده (directives) به کامپایلر ارسال می گردد. توجه داشته باشید که دستورات پیش پردازنده (directives) کد اصلی برنامه را ویرایش نمی کنند، بلکه تمامی تغییرات در یک فضای موقتی در حافظه صورت می پذیرد و کد اصلی برنامه به همان شکلی که شما نوشته اید، دست نخورده باقی خواهد ماند.

Includes


شما تا اینجای آموزش دستور پیش پردازنده include را در عمل دیده اید. وقتی که از دستور پیش پردازنده include استفاده می کنید، پیش پردازنده، محتویات فایل include شده را در محل درج دستور include کپی می کند. این دستور برای جاهایی که می خواهید اطلاعاتی را در چندین محل کپی کنید، مناسب می باشد (مانند اعلامیه های پیشاپیش (forward declarations)).

Macro defines


دستور پیش پردازنده define می تواند برای ایجاد ماکرو (macro) استفاده شود. یک ماکرو (macro) قانونی است که تعریف می کند، چگونه یک توالی از ورودی ها (input sequence) - برای مثال یک شناسه (identifier) - به یک توالی جایگزین از خروجی ها - مانند یک متن - تبدیل می شود.

دو نوع اصلی از ماکروها وجود دارند :

  • object-like macros (ماکروهای شبیه اشیاء) : این ماکروها می توانند به یکی از دو روش زیر معرفی شوند :

    #define identifier
    #define identifier substitution_text

    در تعریف اولیه متن جایگزینی (substitution text) وجود ندارد، در حالی که در تعریف بعدی، متن جایگزین وجود دارد. توجه داشته باشید، از آنجا که اینها دستورات پیش پردازنده (directives) هستند و نه بیانیه (statements)، هیچ کدام از آنها با سمی کالن خاتمه نیافته اند.

  • function-like macros (ماکروهای شبیه توابع) : این ماکروها، مانند توابع عمل می کنند، و هدف مشابهی را نیز دنبال می کنند. در مورد این نوع ماکروها زیاد بحث نخواهیم کرد، زیرا استفاده از آنها به طور کلی خطرناک است، و تقریباً هر کاری که این نوع ماکروها می توانند انجام بدهند، توسط یک تابع درون خطی (inline function) می تواند صورت بپذیرد.

ماکروهای شبیه اشیاء (Object-like macros) همراه با متن جایگزین


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

قطعه کد زیر را در نظر بگیرید :

#define MY_FAVORITE_NUMBER 9

std::cout << "My favorite number is: " << MY_FAVORITE_NUMBER << std::endl;

پیش پردازنده کد بالا را به شکل زیر تغییر می دهد :

std::cout << "My favorite number is: " << 9 << std::endl;

در نهایت، اگر این برنامه اجرا شود، خروجی زیر را چاپ خواهد کرد :

 My favorite number is: 9

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

ماکروهای شبیه اشیاء (Object-like macros) بدون متن جایگزین


ماکروهای شبیه اشیاء (Object-like macros) می توانند بدون متن جایگزین نیز مورد استفاده قرار بگیرند.

برای مثال :

#define USE_YEN

ماکروهایی شبیه این، همانطور که ممکن است انتظار داشته باشید، کار می کنند : در کدهای بعد از ماکرو، هر شناسه ای که با آن نام تکرار شده باشد با مقدار nothing (هیچ چی) جایگزین می شود!

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

برخلاف ماکروهای شبیه اشیاء (Object-like macros) با متن جایگزین که توصیه نمی کردیم از آنها استفاده کنید، ماکروهای شبیه اشیاء (Object-like macros) بدون متن جایگزینی را معمولاً توصیه می کنیم که مورد استفاده قرار بدهید.

کامپایل شرطی (Conditional compilation)


دستورات پیش پردازنده (directives) کامپایل شرطی (Conditional compilation) به شما امکان می دهند تا تعیین کنید تحت شرایطی کدی کامپایل گردد و یا اینکه کامپایل نگردد. تنها دستورات پیش پردازنده (directives) برای کامپایل شرطی (Conditional compilation) که در این درس مورد بحث قرار خواهند گرفت موارد زیر می باشند :

#ifdef, #ifndef, #endif


دستور پیش پردازنده ifdef بررسی می کند که آیا یک مقداری قبلاً معرفی شده است (defined) یا خیر. اگر اینطور باشد، کدهای ما بین دستور ifdef و endif متناظر آن، کامپایل خواهند شد. اگر هم قبلاً معرفی نشده باشد این کدها کامپایل نمی شوند.

قطعه کد زیر را در نظر بگیرید :

#define PRINT_JOE

#ifdef PRINT_JOE
std::cout << "Joe" << std::endl;
#endif

#ifdef PRINT_BOB
std::cout << "Bob" << std::endl;
#endif

از آنجا که در قطعه کد بالا PRINT_JOE قبلاً معرفی شده است، کد

std::cout << "Joe" << std::endl;

کامپایل می شود. و از آنجا که PRINT_BOB قبلا معرفی نشده است، کد

std::cout << "Bob" << std::endl;

کامپایل نخواهد شد.

دستور پیش پردازنده ifndef متضاد دستور ifdef می باشد. این دستور به شما امکان می دهد تا عدم معرفی شدن یک شناسه را بررسی کنید.

#ifndef PRINT_BOB
std::cout << "Bob" << std::endl;
#endif

در این برنامه متن “Bob” چاپ می شود؛ چرا که شناسه PRINT_BOB هرگز معرفی نشده است و ما هم عدم معرفی شدن آن را با دستور ifndef بررسی کرده ایم.

کامپایل شرطی (Conditional compilation) معمولاً در فایل های هدر و در قسمت گارد هدر (header guards) مورد استفاده قرار می گیرد. در درس بعدی در این مورد دقیقتر خواهیم شد.

محدوده defines


دستورات پیش پردازنده (directives) قبل از کامپایل بررسی می شوند، این بررسی در هر فایل به صورت جداگانه و از بالا تا پایین انجام می پذیرد. هنگامی که کار پیش پردازنده در هر فایل تمام شد، تمامی پیش پردازنده های موجود در آن فایل حذف می شوند.

این مساله به این معنا می باشد که قلمرو پیش پردازنده ها در محدوده همان فایل می باشد. دستورات پیش پردازنده که در یک فایل کد نوشته می شوند در سایر فایل ها هیچ تاثیری ندارند.

مثال زیر را در نظر بگیرید :

function.cpp

#include "iostream"

void doSomething()
{
#ifdef PRINT
std::cout << "Printing!";
#endif
#ifndef PRINT
std::cout << "Not printing!";
#endif
}

main.cpp

void doSomething(); // forward declaration for function doSomething()

int main()
{
#define PRINT

doSomething();

return 0;
}

اگر این برنامه اجرا شود، متن زیر چاپ خواهد شد.

Not printing!

درست است که شناسه PRINT در فایل main.cpp معرفی شده است، اما هیچ تاثیری در فایل function.cpp نخواهد گذاشت.

در نهایت توجه داشته باشید که دستورات پیش پردازنده (directives) ای را که در یک فایل هدر وارد می کنید، می توانند در فایل های کد نیز مورد استفاده قرار بگیرند. پس اگر لازم دارید تا از دستورات پیش پردازنده یکسان در چندین فایل کد استفاده کنید، باید آنها را در فایل های هدر قرار بدهید. در درس بعدی مثالی از این مساله را مشاهده خواهید کرد.


آموزش قبلی : آموزش زبان ++C : فایل های هدر (Header files)

آموزش بعدی : آموزش زبان ++C : گارد هدر (Header guards)



نمایش دیدگاه ها (0 دیدگاه)

دیدگاه خود را ثبت کنید:

انتخاب تصویر ویرایش حذف
توجه! حداکثر حجم مجاز برای تصویر 500 کیلوبایت می باشد.