زبان C – دستورات پیشپردازش(جلسه ۱۱)
زمان تقریبی مطالعه این مقاله: ۷ دقیقه
سلام به میکرولرنیهای عزیز 🙂 🙂 🙂
این مقاله در ۳۱ اردیبهشت ۱۴۰۰ به روز رسانی شده.
امروز نوبت به یکی از بخشهای کاربردی زبان C رسیده. اگه سعی کنید به این بخش تسلط کافی پیدا کنید میتونید باهاش ویژگیهای جالبی به برنامتون اضافه کنید و سطح برنامه نویسی خودتون را نسبت به قبل چند level بالاتر بیارید. خب وقتو تلف نکنیم و سریعتر بریم سراغ جلسه امروز که دربارهی دستورات پیش پردازنده در زبان C و ماکرو در زبان C هست. با میکرولرن، مرجع برنامه نویسی C همراه باشید.
قبل از شروع این مقاله میتونید در صورت نیاز جلسههای قبلی آموزش زبان C را مطالعه کنید:
- انواع داده و متغیر
- اشاره گر
- آرایه
- رشته
- ساختار (structure)
- عملگرها
- ساختارهای شرطی
- حلقههای تکرار
- تابع در زبان C
فهرست مطالب
دستورات پیش پردازنده در زبان C
یکی از امکانات زبان C فرمانهای پیش ترجمه یا پیش پردازنده است. استفاده از این فرمانها از یک طرف باعث سهولت برنامهنویسی شده و از طرف دیگر قابلیت اصلاح و جابهجایی برنامه را بالا میبرد.
همان طور که از عنوان آن مشخص است، این فرمانها قبل از شروع ترجمهی برنامه و در یک مرحلهی مقدماتی (قبل از اینکه برنامه کامپایل شود) بررسیشده و دستوراتی که با نماد # شروع شدهاند ترجمه میشوند. این دستورات به منظور قابل فهم شدن برای کامپایلر نیاز به پیش پردازنده دارند.
پیش پردازنده نوعی مترجم است که دستورات توسعه یافتهای از یک زبان را به دستورات قابل فهم برای کامپایلر همان زبان تبدیل میکند.
دستورات پیش پردازنده کاربردهای زیادی دارند. در جدول زیر می توانید این دستورات را مشاهده کنید. در ادامه این دستورات بر اساس کاربرد توضیح داده میشوند.
ضمیمه کردن فایلها
ضمیمه کردن فایلهای سرآیند یا header توسط دستور پیش پردازنده include# انجام میگیرد. برای نمونه جهت قرار دادن فایلهای هدر C نظیر stdio.h و string.h از دستور پیش پردازشگر include# استفاده میشود. فایلهای سرآیند یا header که شامل مجموعهای از توابع یا تعاریف از پیش نوشته شده هستند به دودسته تقسیم میشوند:
- فایلهای کتابخانه استاندارد که همراه کامپایلر C وجود دارند.
- فایلهایی که توسط برنامهنویس نوشته میشوند.
برای هر کدام از این فایل ها یک روش مخصوص ضمیمه کردن (include) وجود دارد که در کدهای زیر آمده است:
#include <نام فایل>
#include "نام فایل"
- خط یک برای include کردن کتابخانههای استاندارد است. این فایلها در مسیر نصب کامپایلر موجود میباشند.
- خط دو برای include کردن کتابخانههای نوشته شده توسط برنامه نویس است. این فایلها در پوشه مربوط به هر پروژه باید قرار بگیرند.
به مثال زیر دقت کنید:
#include <stdio.h>
#include "myheader.h"
- در خط یک کتابخانه استاندارد ورودی خروجی زبان C ضمیمه شده است. در این حالت جستجو برای یافتن این فایل در مسیر نصب کامپایلر انجام میشود.
- در خط دو کتابخانهای که برنامه نویس آن را تهیه کرده به پروژه ضمیمه شده است. این فایل در مسیر پروژه وجود دارد.
فایلهای سرآیند از اهمیت ویژهای برخوردارند، زیرا:
- بسیاری از توابع مثل ()getchar و ()putchar در فایلهای سرآمد مربوط به سیستم تعریف شدهاند.
- با فایلهای سرآیندی که برنامهنویس مینویسد، میتوان از بسیاری از تعاریف تکراری جلوگیری کرد.
- با این فایلها میتوان به صورت تیمی برنامه نویسی کرد. مثلا نوشتن کتابخانههای هر قسمت از پروژه میتواند به عهدهی یک نفر باشد. و شخصی دیگر هم با استفاده از کتابخانههای نوشته شده برنامه اصلی را بنویسد.
تعریف کردن ماکرو
ماکرو یک قطعه کد است که نامی به آن اختصاص داده میشود. هر زمانی که این نام در طول برنامه استفاده شود، محتوای ماکرو به جای آن قرار داده خواهد شد. دو نوع ماکرو وجود دارد:
- ماکروهای بدون پارامتر
- ماکروهای شبه تابع (همراه با پارامتر)
در هر دو نوع ماکرو از دستور پیش پردازندهی define# برای تعریف استفاده میشود.
ماکروهای بدون پارامتر
ابتدا ماکروهای بدون پارامتر را بررسی می کنیم. این دستور بهصورت زیر به کار میرود:
#define <رشته ای از کاراکترها> <نام ماکرو>
- نام ماکرو همانند نام یک متغیر در C است. البته بهتر است جهت مشخص بودن در برنامه با حروف بزرگ نمایش داده شود.
- هر دستور ماکرو باید در یک خط نوشته شود و در انتهای آن نیازی به سمیکالن ( ; ) وجود ندارد. (تنها، دستورات C هستند که با سمیکالن به پایان میرسند)
- در هرجایی از برنامه میتوان تعریف ماکرو را قرار داد. به هر حال پیش از اینکه ماکرو استفاده شود نام ماکرو باید تعریف شود.
- دستور define# به پیش پردازشگر میگوید نام ماکرو را با بدنه ماکرو جایگزین کند.
به مثال زیر دقت کنید:
#include <stdio.h>
#define age 45
int main(){
printf("%d", age * 10);
return 0;
}
- یک ماکرو با نام age تعریف شده است.
- محتوای این ماکرو عدد ۴۵ است.
- در مرحله پیش پردازش در دستور printf به جای age عدد ۴۵ قرار داده میشود.
تذکر: تصور کنید age در چندین فرمول در طول برنامه به کار میرود. با این تعریف دقیقا جای به کار رفتن age را میدانیم و اگر زمانی نیاز به تغییر آن بود به سادگی بدنه ماکرو را تغییر میدهیم و نیازی نیست در طول برنامه به دنبال فرمول بگردیم و اعداد را تغییر دهیم.
ماکروهای شبه تابع
ماکروهای شبه تابع به صورت زیر تعریف میشوند:
#define <macro name(parameters)> <macro definition>
- تعریف ماکرو مشخص میکند که چه عملی باید توسط ماکرو انجام گیرد.
- اسامی پارامترها متغیرهایی هستند که در حین اجرای ماکرو به آن منتقل میشوند که اگر تعداد آنها بیشتر از یکی باشد، با کاما از هم جدا میشوند.
به مثال زیر دقت کنید:
#include <stdio.h>
#define square(x) (x * x)
int main(){
printf("%d", square(5));
return 0;
}
- ماکرویی با نام square یا توان دو، تعریف شده است.
- پارامتر این ماکرو x است.
- بدنه ماکرو حاصل ضرب x*x است.
- در دستور printf نام ماکرو نوشته شده است. به جای پارامتر آن هم عدد ۵ قرار دارد.
- در زمان پیش ترجمه توان دوم این عدد حساب شده و جای ماکرو قرار میگیرد.
برای درک بهتر ماکرو به مثال دیگری دقت فرمایید:
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void){
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
- ماکروی تعریف شده دو پارامتر x و y را دریافت کرده و ماکزیمم آنها را بر میگرداند.
- در بدنه ماکرو از عملگر ? یا همان if فشرده استفاده شده است.
- در تابع printf به عنوان پارامتر قابل چاپ از ماکرو استفاده شده است.
- حال با توجه به توضیحات داده شده سعی کنید این تکه کد را تحلیل کرده و صحت آن را در کامپایلر بررسی کنید.
برای فهم این کد نیاز به دانستن عملگرها و توابع دارید. اگر احساس می کنید نیاز به مطالعه دارید، از قسمت یادآوری در ابتدای همین مقاله اقدام کنید.
تذکر: از دستور undef# جهت حذف ماکرویی که پیشتر تعریف شده است استفاده میکنیم. روش استفاده از این دستور بهصورت زیر است:
#undef <نام ماکرو>
در اینجا نام ماکرو شناسهای است که پیشتر توسط دستور define# تعریف شده است.
دستورات پیش پردازنده شرطی در زبان C
در ادامهی بررسی دستورات پیش پردازنده در زبان C، به دستورات شرطی میرسیم. گاهی میخواهیم طبق شرایط بعضی قسمتهای کد توسط کامپایلر، کامپایل شود و گاهی هم عکس این حالت مد نظر است. در چنین مواقعی سراغ دستورات پیش پردازنده شرطی میرویم. دستورات پیش پردازنده شرطی عبارتاند از:
if#
else#
endif#
ifdef#
ifndef#
در حالت معمولی، دستور if برای تصمیمگیری در نقاط مختلف برنامه به کار میرود. شرطهایی که در دستور if ذکر میشوند در حین اجرای برنامه ارزیابی میشوند. یعنی اگر شرط ذکر شده در دستور if درست باشد، کلیه دستوراتی که در بلوک if قرار دارند اجرا میشوند ولی در دستورات پیش پردازنده شرطی، شرطی که در آن ذکر میشود در حین ترجمه برنامه ارزیابی میشود.
دستور if بهصورت زیر به کار میرود :
#if عبارت شرطی
/* مجموعه ی دستورات */
#endif
- عبارت شرطی که در دستور if ذکر میشود، عبارتی است که مقدار آن در زمان ترجمه معلوم است.
- این عبارت در حین ترجمه ارزیابی میشود، چنانچه دارای ارزش درستی باشد، مجموعهی دستورات موجود در بین if# (ابتدای بلوک) و endif# (انتهای بلوک) کامپایل خواهد شد وگرنه این مجموعه دستورات کامپایل نمیشود.
- برخلاف دستور if در C، حکمهای تحت کنترل دستور if# در آکولادها محصور نمیگردند. بهجای آکولادها برای پایان بخشیدن به بلوک if# باید از دستور endif# استفاده شود.
دستور شرطی بعدی ifdef# است. در صورتی مجموعه دستورات این بلوک اجرا میشوند که یک ماکرو از قبل تعریف شده باشد.
قالب کلی استفاده شبیه ifdef# است:
#ifdef <نام ماکرو>
/* مجموعه ی دستورات */
.
.
.
#endif
به مثال زیر دقت کنید:
#ifdef DEBUG
/* مجموعه دستورات */
#endif
- اگر ماکروی DEBUG تعریف شده باشد، آن گاه دستوراتی این بلوک ترجمه خواهند شد.
- دستور شرطی ifdef# برای پایان بلوک خود به دستور endif# نیاز دارد.
دستور بعدی ifndef# است که عکس دستور ifdef# عمل میکند. اگر ماکرویی که نام آن در جلوی ifndef# قرار دارد قبلا تعریف نشده باشد، مجموعه حکمهای ذکرشده کامپایل میگردند، در غیر این صورت کامپایل نخواهند شد.
به مثال زیر دقت کنید:
#ifndef MESSAGE
#define MESSAGE "You wish"
#endif
- اگر ماکروی MESSAGE تعریف نشده باشد، مجموعه دستورات این بلوک بررسی میشوند.
- چون دستورات این بلوک شامل پیش پردازنده است، نیاز به مرحله پیش پردازش وجود دارد.
- دستور شرطی ifndef# هم برای پایان بلوک خود به دستور endif# نیاز دارد.
حال یک مثال کامل را بررسی میکنیم:
#include <stdio.h>
#ifndef MESSAGE
#define MESSAGE "You wish"
#endif
int main(void){
printf("Here is the message: %s\n", MESSAGE);
return 0;
}
زمانی که کد بالا کامپایل و اجرا شود، نتیجه ی زیر را تولید میکند:
Here is the message: You wish
- برنامه نویس میخواسته ماکرویی به نام MESSAGE تعریف کند. اما مطمئن نبوده که در جاهای دیگر برنامه یا فایلهای دیگر چنین تعریفی انجام نشده باشد.
- پس برای تعریف ماکرو شرط گذاشته است.
- در صورتی این ماکرو تعریف میشود که قبلا تعریف نشده باشد.
- با این کار جلوی تعریفهای چند باره و اضافه شدن سربار نرم افزاری گرفته میشود.
سخن آخر
میکرولرنیهای عزیز خدا قوت 🙂
جلسه دستورات پیش پردازنده در زبان C هم به پایان رسید. الان بعد از جلسات مختلف زبان C به اون نقطه رسیدیم که بتونیم برای میکروکنترلرها برنامه بنویسیم و بیشتر نیازهامون تا الان پوشش داده شده. در جلسه بعد درباره ی اصول کتابخانه نویسی در زبان C مطلب مفیدی براتون منتشر می کنیم.
یادتون نره که شرط خوب برنامه نوشتن اول یاد گرفتن اصول و بعد تمرین زیاد هست. نظر دادن و به اشتراک گذاری مطالب ما رو هم اگه فراموش نکنید خیلی خوشحال می شیم. 😀
به عنوان آخرین حرف، یه پیشنهاد ویژه دارم برای اون دسته از دوستانی که علاقه دارند خیلی عمیقتر زبان C را یاد بگیرن و برای میکروکنترلرها یا کارهای نرم افزاری ازش استفاده کنند. دوره جامع زبان C را براتون تهیه کردم که فوق العاده پروژه محور و کاربردیه. خوش حال میشم در اون دوره شما رو ببینم. برای دسترسی به دوره میتونید روی عکس زیر کلیک کنید.
درباره احسان عبداللهی
مدیر و موسس میکرولرن | برنامه نویسی پاسخی ظریف به یک نیاز در دنیای واقعی هست.
نوشته های بیشتر از احسان عبداللهیمطالب زیر را حتما مطالعه کنید
دوره های آموزشی مرتبط
آموزش جنگو
آموزش پایتون
آموزش الگوریتم و فلوچارت – تفکر برنامه نویسی
آموزش برنامه نویسی C برای میکروکنترلر
آموزش الکترونیک دیجیتال
24 دیدگاه
به گفتگوی ما بپیوندید و دیدگاه خود را با ما در میان بگذارید.
سلام من کد زیر رو نوشتم ارور میده چرا؟
#define PORTB.0 buz
#define PORTB.1 stopb
#define PORTB.2 startb
#define PORTB.3 hourb
#define PORTB.4 m10b
#define PORTB.5 m1b
#define PORTC.0 ledg1
#define PORTC.1 ledg
#define PORTC.2 ledc
#define PORTC.3 ledd
#define PORTC.4 lede
#define PORTC.5 ledg4
#define PORTD.0 leda
#define PORTD.1 ledf
#define PORTD.2 ledg3
#define PORTD.3 ledg2
#define PORTD.4 ledb
ledg=0;
Error: F:\AVR\cc\cc.c(59): undefined symbol ‘ledg’
کد را در چه محیطی نوشتید؟
با درود، شما ledg رو مقداردهی کردین در صورتیکه اصلا مشخص نکردین ledg چی هست، یا بعنوان متغیر معرفیش کن یا اگه دیفاین کردی علامت مساوی رو بردار، کدهای شما تو محیط کدویژن نوشته شده و اصلا استاندارد زبان C نیست، از دستورات استاندارد استفاده کن تا برنامه خواناتر و قابل حمل باشه، مدت زیادی از سوال شما گذشته ولی جواب دادم تا اگه کسی دید بی جواب نمونه، موفق باشید
باسلام
بسیار خوب و مفید
لطفا اشتراک گذاری مطالب از طریق واتس اپ هم اضافه بفرمایید
سپاس
سلام
در خصوص define ، در برخی کدها به موارد زیر برخورد میکنم ولی متوجه نشدم برای چی هست:
#ifndef _FILE_NAME_H
#define _FILE_NAME_H
(البته به خاطر چپ چین بودن، یکم به هم ریخته شد)
توی صفحات زیر هم تعاریفی دیدم ولی کامل متوجه نشدم!
https://www.cprogramming.com/tutorial/cpreprocessor.html
اگر میشه یکم توضیح بدید!!!!! ممنون
سلام
لطفا به این پست مراجعه کنید. توضیح کامل داده شده.
http://microlearn.ir/5347/library.html
با درود، این دستور برای اینه که شما اگه شک دارید که این دیفاین قبلا انجام شده یا نشده بتونین دیفاینش کنید، مثلا اگه دیفاین شده بود که هیچی ولی اگه دیفاین نشده بود _FILE_NAME_H دیفاین میشه. علت اصلی اینکار اینه که اگه شما از کتابخانه های زیادی تو برنامه استفاده کردین یا قبلا داخل توابع دیفاین کرده باشین بتونین بدون مشکل دیفاین کنین تا بعد از بیلد کردن برنامه پیام نیاد که قبلا دیفاین شده یا مقدار دیفاین حین برنامه تغییر کنه که باعث ایجاد خطا تو برنامه بشه. با ارزوی موفقیت
سلام و تشکر بابت مطالب خوبی که ارائه دادید , بنده یک برنامه دارم میخواستم ببینم میشه برنامه را برای شما ایمیل کنم و لطف کنید توضیحاتی ضمیمه کنید.تشکر
سلام
بله حتما بفرستید. فقط کمی صبور باشید تا بررسی بشه. چون ترافیک کاریمون زیاده.
سلام , وقت بخیر و خدا قوت به شما دوست عزیز ایمیل کردم info@microlearn.ir
به اگه امکان داره یک زودتر راهنماییم کنید.ممنون
سلام فایل را بررسی کردم. کلیت کار ظاهرا مربوط به PLC هست که تخصص بنده نیست.
ولی اگر PLC بلدید و میخواید بدونید این کدها چرا به این شکل نوشته شده باید زبان سی را یاد بگیرید و بعد سراغ این کد برید. میتونید در دوره های آموزشی زبان سی ما شرکت کنید.
موفق باشید
سلام ممنون از وقتی که گذاشتین
خواهش میکنم
سلام
اقا خیلی عالی خدا خیر بده
سلام.
موفق باشید
سلام با تشکر اززحمات شما مهندس جان
خیلی اطلاعات حیاتی و مفیدی در برنامه نویسی هستن.
با سلام
خوشحالم که مطلب مفیدی بوده براتون.
سلام
هرچیزی ذکاتی داره. و ذکات علم ، نشر و انتقال اونه. من خودم هر چیزی و که بلدم در اختیار بقیه میزارم. حتی سر کلاسم که میرم بیشتر از اون چیزی که باید بگم و میگم. خدا ام کمکم کرده و میکنه. تو یاد دادگیری حریص باشید و تو یاددان دست و دل باز باشید. خدام واستون دست و دلشو باز میکنه.
یا علی
ممنون از شما مهندس بابت مطالب مفیدی که گذاشتید
متشکرم. امیدوارم براتون مفید بوده باشه.
با سلام
تشکر میکنم از زحمت یی که کشیدید بابت آموزش زبان C خیلی عالی هستش
ولی متاسفانه عکس هایی که بین آموزش ها است لود نمیشه و بعضی قسمت ها رو بنده متوجه نشدم مثلا قسمت عملگرها ، خیلی از اون عملگرها داخل اون عکس ها بود و من نتونستم اون اموزش رو یاد بگیرم ،قسمت های دیگه اموزش هم از عکس برای توضیح استفاده شده که دیگه نمیشه اون ها رو دید،من کتاب اموزش زبان C چند تایی دارم ولی اون ها نتونستن به اندازه این آموزش به من کمک کنن مثل کتاب اقای جعفرنژادقمی که خیلی گیج کننده هست
از شما خواهش میکنم این مشکل رو رفع کنید.
پیشنهاد: اگه آموزش به صورت فیلم بود خیلی بهتر می شد،مطمئن هستم خیلی ها اینو میپسندند البته در هر صورت کار شما خیلی ارزشمند هست.
الان اگر چیزی از زبان C بدونم مدیون شما هستم.
با تشکر
سلام مهندس ممنون از مطالبتون
عذر میخوام مهندس من یه کتابخونه ی ساده توی اتمل استدیو برای تمرین نوشتم؛ توی فایل h کتابخونه اومدم و کتابخونه util/delay رو چون نیاز بود از توابع delay داخل فایل c کتابخونه استفاده کنم، اینکلود کردم. همونطور که میدونید، این کتابخونه احتیاج داره که ماکرو F_CPU قبل از اینکلود کردن، براش تعریف بشه وگرنه اخطار میده و خودش تعریفش میکنه که پیش فرضش ۱MHz هستش. من این ماکرو را داخل فایل main تعریف کردم ولی حین کامپایل کردن کد، بازم اخطار تعریف نشدن F_CPU میده و همونطور که گفتم به تعریف پیش فرض برمیگرده! 🙁
حال سوال من اینه که باید چکار کنم که با تعریف این ماکرو (یا هر ماکرو ی دیگه) داخل فایل main، دیگه احتیاجی نباشه که داخل کتابخونه خودم هم تعریفش کنم؟
درود به شما کاربر فعال 🙂
شما بهتره یه فایل با نام global (خودم همین کار رو کردم) بسازید و تمام تعاریف و ماکروهای عمومی مورد نیاز در برنامه هاتون را در این فایل قرار بدین. و در هر پروژهای که می خواید استفاده کنید ابتدا این فایل روا اینکلود کنید. مشکلتون حل میشه. اگه نیاز به راهنمایی بیشتر دارید بگید تا توضیح کاملتری بدم.
خیلی ممنون از راهنماییتون مهندس. مشکلمو حل کردید 🙂
خواهش میکنم دوست عزیز
خوشحال شدم که مشکلتون حل شد. 🙂
توصیه میکنم حتما پست کتابخانه نویسی را که امروز منتشر شده مطالعه کنید.
http://microlearn.ir/5347/library.html