دوشنبه , ۳۰ اردیبهشت ۱۳۹۸
آخرین مطالب
LINK TABLES
خانه | برنامه‌نویسی | زبان C – دستورات پیش‌پردازش(جلسه ۱۲)

زبان C – دستورات پیش‌پردازش(جلسه ۱۲)

امروز نوبت به یکی از بخش های کاربردی زبان C رسیده. اگه سعی کنید به این بخش تسلط کافی پیدا کنید می‌تونید باهاش ویژگی ها جالبی به برنامتون اضافه کنید و سطح برنامه نویسی خودتون را نسبت به دوستاتون چند level بالاتر بیارید. خب وقتو تلف نکنیم و سریع تر بریم سراغ جلسه امروز که درباره ی دستورات پیش پردازش هست.

سایر جلسات زبان C

معرفی چند مفهوم مهم در برنامه نویسی

جلسه اول = مقدمه                                      جلسه دوم = انواع داده

جلسه سوم = اشاره گر                                 جلسه چهارم = آرایه

جلسه پنجم = رشته                                     جلسه ششم = آرایه و اشاره گر

جلسه هفتم = ساختار (structure)                   جلسه هشتم = عملگرها

جلسه نهم = ساختارهای شرطی                    جلسه دهم = حلقه های تکرار

جلسه یازدهم = تابع

دستورات پیش‌ پردازش

یکی از امکانات زبان C فرمان‌های پیش ترجمه یا پیش پردازش است. استفاده از این فرمان‌ها از یک‌ طرف باعث سهولت برنامه‌نویسی شده و از طرف دیگر قابلیت اصلاح و جابه‌جایی برنامه را بالا می برد.

همان طور که از عنوان آن مشخص است این فرمان‌ها قبل از شروع ترجمه‌ی برنامه و در یک مرحله‌ی مقدماتی (قبل از اینکه برنامه کامپایل شود) بررسی‌شده و دستوراتی که با نماد # شروع‌شده‌اند ترجمه می‌شوند. این دستورات به منظور قابل فهم شدن برای کامپایلر نیاز پیش به پردازنده دارند. پیش پردازنده نوعی مترجم است که دستورات توسعه‌یافته‌ای از یک‌زبان را به دستورات قابل‌فهم برای کامپایلر همان زبان تبدیل می‌کند.  

دستورات پیش پردازنده کاربردهای زیادی دارند. در جدول زیر می توانید این دستورات را مشاهده کنید. در ادامه این دستورات بر اساس کاربرد توضیح داده می شوند.

ضمیمه کردن فایل‌ها

ضمیمه کردن فایل‌ها سرآیند یا header توسط دستور پیش پردازنده include# انجام می‌گیرد. برای نمونه جهت قرار دادن فایل‌های هدر C نظیر stdio.h و string.h از دستور پیش پردازشگر include # استفاده می‌شود. فایل‌های سرآیند یا header که شامل مجموعه ای از توابع یا تعاریف از پیش نوشته شده هستند به دودسته تقسیم می‌شوند:

  1. فایل‌هایی کتابخانه استاندارد که همراه کامپایلر C وجود دارند.
  2. فایل‌هایی که توسط برنامه‌نویس نوشته می‌شوند.

برای هر کدام از این فایل ها یک روش مخصوص ضمیمه کردن (include) وجود دارد که در کدهای زیر آمده است:

  • خط یک برای include کردن کتابخانه های استاندارد است. این فایل ها در مسیر نصب کامپایلر موجود می باشند.
  • خط دو برای include کردن کتابخانه های نوشته شده توسط برنامه نویس است. این فایل ها در پوشه مربوط به هر پروژه باید قرار بگیرند.

به مثال زیر دقت کنید:

  • در خط یک کتابخانه استاندار ورودی خروجی زبان  C ضمیمه شده است. در این حالت جستجو برای یافتن این فایل در مسیر نصب کامپایلر انجام می شود.
  • در خط دو کتابخانه ای که برنامه نویس آن را تهیه کرده به پروژه ضمیمه شده است. این فایل در مسیر پروژه وجود دارد.

فایل‌های سرآیند از اهمیت ویژه‌ای برخوردارند، زیرا:

  1. بسیاری از توابع مثل ()getchar و ()putchar در فایل‌های سرآمد مربوط به سیستم تعریف‌شده‌اند.
  2. با فایل‌های سرآیندی که برنامه‌نویس می‌نویسد، می‌توان از بسیاری از تعاریف تکراری جلوگیری کرد.
  3. با این فایل ها می توان به صورت تیمی برنامه نویسی کرد. مثلا نوشتن کتابخانه های هر قسمت از پروژه می تواند به عهده ی یک نفر باشد. و یک نفر هم با استفاده از کتابخانه های نوشته شده برنامه اصلی را بنویسد.

تعریف کردن ماکرو

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

  1. ماکروهای بدون پارامتر
  2. ماکروهای شبه تابع (همراه با پارامتر)

در هر دو نوع ماکرو از دستور پیش پردازنده‌ی define# برای تعریف استفاده می‌شود. ابتدا ماکروهای بدون پارامتر را بررسی می کنیم. این دستور به‌صورت زیر به کار می‌رود:

  • دستور define# به‌ پیش پردازشگر می‌گوید نام ماکرو را با بدنه ماکرو جایگزین کند.
  • نام ماکرو همانند نام یک متغیر در C است. البته بهتر است جهت مشخص بودن در برنامه با حروف بزرگ نمایش داده شود.
  • هر دستور ماکرو باید در یک خط نوشته شود و در انتهای آن نیازی به سمیکالن وجود ندارد. (تنها، دستورات C هستند که با سمیکالن به پایان می‌رسند)
  • در هرجایی از برنامه می‌توان تعریف ماکرو را قرارداد. به‌هرحال پیش از اینکه ماکرو استفاده شود نام ماکرو باید تعریف شود.

به مثال زیر دقت کنید:

  • یک ماکرو با نام age تعریف شده است.
  • محتوای این ماکرو عدد ۴۵ است.
  • تصور کنید age در چندین فرمول در طول برنامه به کار می رود. با این تعریف دقیقا جای به کار رفتن age را می دانیم و اگر زمانی نیاز به تغییر آن بود به سادگی بدنه ماکرو را تغییر می دهیم و نیازی نیست در طول برنامه به دنبال فرمول بگردیم و اعداد را تغییر دهیم.

ماکرو های شبه تابع به صورت زیر تعریف می شوند:

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

به مثال زیر دقت کنید:

  • ماکرویی با نام square یا توان دو تعریف شده است.
  • پارامتر این ماکرو x است.
  • بدنه ماکرو حاصل ضرب x*x است.
  • در هر جای برنامه اگر نام ماکرو نوشته شود و به جای پارامتر آن هم عددی قرار گیرد، در زمان پیش ترجمه توان دوم آن عدد حساب شده و جای ماکرو قرار می گیرد.

از دستور undef# جهت حذف ماکرویی که پیش‌تر تعریف‌شده است استفاده می‌کنیم. روش استفاده از این دستور به‌صورت زیر است:

در اینجا نام ماکرو شناسه‌ای است که پیش‌تر توسط دستور define# تعریف‌شده است.

برای مشخص شدن کاربرد ماکرو به مثال زیر دقت فرمایید:

  • ماکروی تعریف شده دو پارامتر x و y را دریافت کرده و ماکزیمم آن ها را بر می گرداند.
  • در بدنه ماکرو از عملگر ? یا همان if فشرده استفاده شده است.
  • در نهایت در تابع printf به عنوان پارامتر قابل چاپ از ماکرو استفاده شده است.
  • حال با توجه به توضیحات داده شده سعی کنید این تکه کد را تحلیل کرده و صحت آن را در کامپایلر بررسی کنید.

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

دستورات پیش‌پردازش شرطی

گاهی می خواهیم طبق شرایط بعضی قسمت های کد توسط کامپایلر، کامپایل شود و گاهی هم برعکس این حالت مد نظر است. در چنین مواقعی سراغ دستورات پیش پردازش شرطی می رویم. دستورات پیش پردازنده شرطی عبارت‌اند از:

  • #if
  • #else
  • #endif
  • #ifdef
  • #ifndef

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

دستور if به‌صورت زیر به کار می‌رود :

  • عبارت شرطی که در دستور if ذکر می‌شود، عبارتی است که مقدار آن در زمان ترجمه معلوم است. این عبارت در حین ترجمه ارزیابی می‌شود، چنانچه دارای ارزش درستی باشد، مجموعه‌ی دستورات موجود در بین if# (ابتدای بلوک) و endif# (انتهای بلوک) کامپایل خواهد شد وگرنه این مجموعه دستورات کامپایل نمی‌شود.
  • برخلاف دستور if در C، حکم‌های تحت کنترل دستور if# در آکولادها محصور نمی‌گردند. به‌جای آکولادها برای پایان بخشیدن به بلوک if# باید از دستور endif# استفاده شود.

دستور شرطی بعدی ifdef# است. در صورتی مجموعه دستورات این بلوک اجرا می شوند که یک ماکرو از قبل تعریف شده باشد.

قالب کلی استفاده شبیه ifdef# است:

به مثال زیر دقت کنید:

  • اگر ماکروی DEBUG تعریف شده باشد، آن گاه دستوراتی این بلوک ترجمه خواهند شد.
  • دستور شرطی  ifdef# برای پایان بلوک خود به دستور endif# نیاز دارد.

دستور بعدی ifndef# است که عکس دستور ifdef# عمل می‌کند. اگر ماکرویی که نام آن در جلوی ifndef# قرار دارد قبلا تعریف‌ نشده باشد، مجموعه حکم‌های ذکرشده کامپایل می‌گردند، در غیر این صورت کامپایل نخواهند شد.

به مثال زیر دقت کنید:

  • اگر ماکروی MESSAGE تعریف نشده باشد، مجموعه دستورات این بلوک بررسی می شوند. اگر دستورات پیش پردازنده داشته باشند، پیش پردازش شده و در غیر این صورت کامپایلر آن ها را بررسی می کند.
  • اما در این حالت فقط نیاز به پیش پردازنده وجود دارد.
  • دستور شرطی ifndef# هم برای پایان بلوک خود به دستور endif# نیاز دارد.

حال یک مثال کامل را بررسی می کنیم:

زمانی که کد بالا کامپایل و اجرا شود، نتیجه ی زیر را تولید می‌کند :

  • بسیاری از قسمت های این کد تکراری بود و در این پست و پست تابع بارها بررسی شده اند پس فقط هدف این کد توضیح داده می شود.
  • برنامه نویس خواسته ماکرویی به نام MESSAGE تعریف کند. اما مطمئن نبوده که در جاهای دیگر برنامه یا فایل ها دیگر چنین تعریفی انجام نشده باشد.
  • پس برای تعریف ماکرو شرط گذاشته. در صورتی این ماکرو تعریف شود که قبلا تعریف نشده باشد.
  • با این کار جلو تعریف های چند باره و اضافه شدن سربار نرم افزاری گرفته می شود.

دستورات پیش پردازنده شرطی در نوشتن فایل های کتابخانه ای بسیار به کار می آیند. در پست آینده حتما درباره آن ها صحبت خواهیم کرد.

خب جلسه دستورات پیش پردازنده هم به پایان رسید. الان بعد از ۱۲ جلسه زبان C به اون نقطه رسیدیم که بتونیم برای میکروکنترلرها برنامه بنویسیم و بیشتر نیازهامون تا الان پوشش داده شده. در جلسه بعد درباره ی اصول کتابخانه نویسی در زبان C مطلب مفیدی براتون منتشر می کنیم. یادتون نره که شرط خوب برنامه نوشتن اول یاد گرفتن اصول و بعد تمرین زیاد هست. نظر دادن و به اشتراک گذاری مطالب ما رو هم اگه فراموش نکنید خیلی خوشحال می شیم. 😀

درباره ی احسان عبداللهی

احسان عبداللهی هستم | کارشناسی الکترونیک خوندم و کارشناسی ارشد مخابرات |در سال 94 وب سایت میکرولرن را راه اندازی کردم | سعی کردم هر چیزی را به صورت کاربردی دنبال کنم، برای همین از کارشناسی کار با میکروکنترلرهای AVR و ARM، برنامه نویسی C و طراحی PCB را به صورت تخصصی کار کردم و از کارشناسی ارشد برنامه نویسی پایتون ، Computer vision و deep learning را به صورت تخصصی و کاربردی شروع کردم | الان هم سعی میکنم همیشه خودم را به روز نگه دارم و لذت کار کردن با دنیای برنامه نویسی و امبدد سیستم ها را به دیگران انتقال بدم

۱۱ دیدگاه

  1. سلام
    اقا خیلی عالی خدا خیر بده

  2. سلام با تشکر اززحمات شما مهندس جان
    خیلی اطلاعات حیاتی و مفیدی در برنامه نویسی هستن.

  3. سلام

    هرچیزی ذکاتی داره. و ذکات علم ، نشر و انتقال اونه. من خودم هر چیزی و که بلدم در اختیار بقیه میزارم. حتی سر کلاسم که میرم بیشتر از اون چیزی که باید بگم و میگم. خدا ام کمکم کرده و میکنه. تو یاد دادگیری حریص باشید و تو یاددان دست و دل باز باشید. خدام واستون دست و دلشو باز میکنه.
    یا علی

    ممنون از شما مهندس بابت مطالب مفیدی که گذاشتید

  4. با سلام
    تشکر میکنم از زحمت یی که کشیدید بابت آموزش زبان C خیلی عالی هستش
    ولی متاسفانه عکس هایی که بین آموزش ها است لود نمیشه و بعضی قسمت ها رو بنده متوجه نشدم مثلا قسمت عملگرها ، خیلی از اون عملگرها داخل اون عکس ها بود و من نتونستم اون اموزش رو یاد بگیرم ،قسمت های دیگه اموزش هم از عکس برای توضیح استفاده شده که دیگه نمیشه اون ها رو دید،من کتاب اموزش زبان C چند تایی دارم ولی اون ها نتونستن به اندازه این آموزش به من کمک کنن مثل کتاب اقای جعفرنژادقمی که خیلی گیج کننده هست
    از شما خواهش میکنم این مشکل رو رفع کنید.
    پیشنهاد: اگه آموزش به صورت فیلم بود خیلی بهتر می شد،مطمئن هستم خیلی ها اینو میپسندند البته در هر صورت کار شما خیلی ارزشمند هست.
    الان اگر چیزی از زبان C بدونم مدیون شما هستم.
    با تشکر

  5. سلام مهندس ممنون از مطالبتون
    عذر میخوام مهندس من یه کتابخونه ی ساده توی اتمل استدیو برای تمرین نوشتم؛ توی فایل h کتابخونه اومدم و کتابخونه util/delay رو چون نیاز بود از توابع delay داخل فایل c کتابخونه استفاده کنم، اینکلود کردم. همونطور که میدونید، این کتابخونه احتیاج داره که ماکرو F_CPU قبل از اینکلود کردن، براش تعریف بشه وگرنه اخطار میده و خودش تعریفش میکنه که پیش فرضش ۱MHz هستش. من این ماکرو را داخل فایل main تعریف کردم ولی حین کامپایل کردن کد، بازم اخطار تعریف نشدن F_CPU میده و همونطور که گفتم به تعریف پیش فرض برمیگرده! 🙁
    حال سوال من اینه که باید چکار کنم که با تعریف این ماکرو (یا هر ماکرو ی دیگه) داخل فایل main، دیگه احتیاجی نباشه که داخل کتابخونه خودم هم تعریفش کنم؟

    • درود به شما کاربر فعال 🙂
      شما بهتره یه فایل با نام global (خودم همین کار رو کردم) بسازید و تمام تعاریف و ماکروهای عمومی مورد نیاز در برنامه هاتون را در این فایل قرار بدین. و در هر پروژهای که می خواید استفاده کنید ابتدا این فایل روا اینکلود کنید. مشکلتون حل میشه. اگه نیاز به راهنمایی بیشتر دارید بگید تا توضیح کاملتری بدم.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *