C – ۶

شروع برنامه نویسی واقعی

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

first.c

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

خروجی:

توضیح: چنانچه برنامه رو در IDE نوشتید و پس از اجرا برنامه فورا بسته میشود در یک خط قبل از return عبارت ;()getchar رو قرار بدید. این دستور باعث میشه برنامه پس از چاپ خروجی، منتظر فشردن یک کلید از طرف کاربر شود. در این باره در فصلهای بعدی مفصل توضیح خواهیم داد.

شرح برنامه

کامنت یا توضیحات

قسمتهایی از برنامه که بین دو علامت */ و /* محسور شده و یا پس از // قرار گرفته اند کامنت نامیده میشوند. در کامنتهای تک خطی از //استفاده میشود. برای ایجاد کامنتهای چند خطی میتوان از */ و /*استفاده کرد:

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

فایل سرآیند یا هدر

فایل سرآیند فایلی است که در بالای برنامه قرار میگیرد و کتابخانه ای را در برنامه کپی میکند. این فایل در برنامه ما stdio.h است که در برنامه وارد شده است. کمی عمیق تر به این موضوع نگاه کنیم و ببینیم اصلا چی هست و به چه دردی میخوره.
در یونیکس( و طبعا C و لینوکس) مفهومی بنام کتابخانه های استاندارد اشتراکی وجود دارد. این کتابخونه ها چی هستند و چه کاری انجام میدهند؟ با یک مثال قضیه رو بشکافیم. فرض کنیم شما یه خونه یا ساختمون ساختید و باید گاز طبیعی رو به خونه تون بکشید. چیکار میکنید؟ آیا میرید یه لوله از دمِ درِ خونه تون میکشید تا پارس جنوبی در خلیج فارس؟ آیا این کار منطقیه؟ آیا اگر هر خونه ای اینکار رو بکنه نظم جامعه مختل نمیشه؟ قطعا شما همچین کاری رو نمیکنید. یک شرکت متولی میاد با شاه لوله های بزرگ گاز رو به هر استان و شهری میرسونه. و با تقسیمات و انشعابات، لوله گاز رو تا دمِ درِ خونه تون میاره. این دقیقا مفهوم کتابخونه های اشتراکیه. عده ای برنامه نویس خبره نشستن و کتابخونه هایی رو نوشتن تا هر برنامه نویسی مجبور نباشه برای کارهای روتین، خیل عظیمی از کدها رو به تنهایی بنویسه. این کار چندین مزیت بزرگ داره. اول اینکه در وقت برنامه نویس صرفه جویی میشه. دوم اینکه برنامه کوچک و کم حجم خواهند شد. بیاید محتویات فایل stdio.h رو ببینیم:

اگر هدرهای موجود در این فایل رو هم دستی کپی کنیم هزارن خط میشه. شما میتونستید اون خطی رو که حاوی stdio.h بود رو پاک کنید و این همه کد رو قرار بدید یا خودتون ساز و کاری برای گرفتن ورودی و دادن خروجی مینوشتید. اما آیا منطقیه؟ کدومش راحت تره؟ در واقع شما با نوشتن اون یک خط کل محتوای فایل stdio.h رو در برنامه خودتون کپی کردید. به همین دلیل است که حجم برنامه های یونیکسی از برنامه های ویندوزی کمتر است.(تفاوت کتابخانه‌های حین کامپایل با کتابخانه‌های حین اجرا بسیار واضح است. صرفا جهت تعمیم مثال) مثلا برنامه VirtualBox رو در نظر بگیرید. در هنگام نگارش این مطلب، حجم نسخه لینوکسیش ۶۵ مگابایت و حجم نسخه ویندوزیش ۱۰۹ مگابایته. این به این دلیل که در ویندوز هر برنامه باید کتابخانه های خودشو، رو دوشش اینور اونور بکشه. مزیت سوم امنیتشه. این کتابخانه رو افراد با تجربه و خبره مینویسند و به صورت اوپن سورس در معرض دید همگان قرار دادن. نمیتونن توش کدهای مخرب کار بزارن. چون میلیونها چشم و چشمان خود شما مراقبشه. در حالی که اگر هر برنامه نویس خودش بخواد این توابع رو برای خودش بنویسه میتونه هر کد دلخواه مخربی رو درش قرار بده. مزیت چهارمش پایداری و بهینه بودنشه. زیرا این کدها کار افراد خبره ست و چون بصورت اوپن سورس روی وب قرار گرفته هر بنی بشری میتونه بهتر و پایدارترش کنه.

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

دلیل اینکه به این کتابخونه ها استاندارد میگن اینه که سازمان استانداردسازی زبان C تمام سازندگانی رو که کامپایلر میسازن مجبور کرده این کتابخونه ها رو داشته باشن. در غیر اینصورت قادر به دریافت استاندارد نخواهند بود. بعلاوه کتابخانه های استاندارد، هر کامپایلری میتونه بنابر احساس نیازی که در جامعه کاربرانش میبینه کتابخانه های دیگری رو هم قرار بده. همچنین اگر شما بر روی پروژه ای کار میکنید و به کتابخانه های بخصوصی نیاز دارید که باید در چند برنامه مورد استفاده قرار بگیرند ولی بصورت استاندارد موجود نیستند، میتوانید کتابخانه های شخصی مخصوص خودتون را بسازید. در این باره در آینده بیشتر و مفصل تر بحث خواهیم کرد.
این کتابخانه بخصوص ما به اسم stdio.h مخصوص برنامه هایی است که در اون برنامه قراره ورودی از/خروجی به کاربر داشته باشه. اسمش مخفف standard input output است و پسوند h مخفف header یا سرآیند است. در واقع بدون وجود این برنامه مجاز به استفاده از تابع ()printf نمیبودیم.
سوالی که ممکن است برای شما پیش بیاید این است که چرا باید برای هر برنامه ای این کتابخونه رو وارد کدمون بکنیم؟ مگر نه اینکه ورودی/خروجی یکی از پرکاربردترین المانهای مورد استفاده در هر برنامه است؟ سوال شما بجاست. اما اگر مبحث استانداردها رو مطالعه کرده باشید، در اون استانداردها به وضوح قید شده که زبان C باید در کوچکترین حالت ممکن باشه و چیز اضافه ای درش نباشه. بعلاوه، خیلی از برنامه هایی که برای سیستم های توکار نوشته میشوند، نیازی به گرفتن ورودی از کاربر و دادن خروجی به او ندارند. بعنوان مثال برنامه ای که در خودروها مقدار سوخت موجود در باک را نمایش میدهد، نیازی به دخالت کاربر ندارد. ورودی خود را از برنامه ای دیگر تحت عنوان آرگومان میگیرد و خروجیش را نیز به برنامه دیگری برگشت میدهد.
این نوع از دستورات رو در زبان C به اسم دستورات پیش پردازنده میشناسیم و علامت # معرف این نوع از دستورات هستند. اما چرا پیش پردازنده؟ یادتونه در بحث کامپایلر از چیزی به نام لینکر اسم بردم؟ اینجا لینکر یا ارتباط دهنده کارش مشخص میشه. در واقع این دستورات پیش پردازنده هستند، چون قبل از اینکه چیزی در برنامه پردازش بشه، این فایلها هستند که لینک میشن به برنامه. این کار رو لینکر انجام میده که قسمتی از کامپایلرهای امروزیه. لینکر کاربردهای هم دیگری در حین کامپایل داره. از جمله ترکیب رونوشت کتابخانه‌های لازم با فایلهای آبجکت با فرمت o. بمنظور خلق فایل اجرایی.

تابع ()main

تابع ()main چنانکه از اسمش پیداست در واقع مهمترین و اصلی ترین تابع همه(بجز تعدادی معدود که میتوانید اکنون نادیده بگیرید) برنامه هاست. در زبان C تابع به قطعه کدی گفتی میشود که درون } , { محسور شده و مقادیری رو بعنوان ورودی پذیرفته و خروجی را چنانکه مشخص شده پس میدهند. وجود تابع main در برنامه الزامی است. زیرا C از تابع main شروع به اجرای برنامه میکند.
یکبار دیگه با هم این خط رو مرور کنیم:

تابع ()main رو که شرح دادیم. پرانتز جلوی ()mian نمایانگر اینه که ()main یک تابع است. دو مورد باقی میمونه. اول int که قبل از main اومده و دوم void داخل پرانتز.
با یک مثال بامزه شروع کنیم. من دوستم رو به اسم محمد صدا میزنم. محمد اینجا یک تابعه. میگم محمد اون برنجها رو وزن کن ببین چقدره. این دستوری که من به محمد دادم آرگومانه. محمد بعد از وزن کردن برنجها بهم میگه که ۵ کیلو بود. این ۵ کیلو مقدار بازگشتیِ تابعیه به نام محمد. پس داده ای رو به محمد دادم و محمد بعد از پردازش به من خروجی داد. حالا بریم سراغ بحث فنی.
ببنید در واقع هر تابعی دو مشخصه داره. مشخصه اول آرگومانها یا پارامترهاییه که میگیره. و مشخصا دوم مقداری که پس از پردازش برمیگردونه. به چه صورت؟ فرض کنید یک تابع دارید که کارش جمع زدن دو عدد و برگردوندن حاصل جمعه. این تابع دوتا آرگومان داخل پرانتز میگیره و یک نتیجه ای رو بعد از پردازش پس میده. اینکه اینجا ما داخل پرانتز کلمه void به معنی خالی یا empty رو نوشتیم اینو میرسونه که ما نمیخوایم به این تابع چیزی بدیم. استاندارهای C90 و ماقبلش کلمه void رو داخل پرانتز قبول نمیکنه و این قانون در استاندارد C99 به بعد وارد C شد. ممکنه کامپایلر شما پرانتز خالی رو بپذیره. اما اگر میخواید که برنامه تون، روی هر سیستمی قابل اجرا باشه و بعدها به مشکل نخورید، شما هم از آخرین استانداردها پیروی کنید و از آرگومان void استفاده کنید.
حالا میرسیم به int که قبل از تابع main اومده. گفتیم که هر تابع مقادیری رو تحت عنوان آرگومان یا پارامتر میپذیره و مقادیری رو بعد از پردازش به برنامه ای که اون تابع رو صدا زده پس میده. همینجا عرض کنم که صدا زدن یا فراخواندن تابع دقیقا مثل مفهومیه که ما آدمها بکار ببریم. وقتی ما بخوایم کسی رو صدا کنیم اسمشو میبریم. مثل مثال محمد. ما اینجا تابع محمد رو صدا زدیم. اما کی صداش کرده؟ هیچ تابعی قبل از محمد وجود نداره. پس کار کی میتونه باشه؟ اگه درست حدث زدید برای خودتون اسپند دود کنید. سیستم عامل تابع ()main رو صدا زده. در واقع برای همینه که مهمترین تابعه. چون ازون خفنهای روزگاریه که سیستم عامل خودش مستقیم صداش میزنه. پس int چی بود؟ کلا int و void انواعی از داده ها هستن که در باره شون مفصل حرف خواهیم زد. int مخفف integer به معنی عدد صحیح یا عدد غیر اعشاری ست. خب اینجا از تابع ()main خواسته شده که یک عدد صحیح رو به سیستم عامل برگردونه. اما کو عدد؟ کو صحیح؟ یه بار دیگه سورس برنامه رو نگاه کنید؟ این int جلوی تابع ()main با اون return 0 موذی کاملا در ارتباطه. صفر یک عدد صحیحه و دستور return به معنی برگشت دادنه. برگشت دادن به کسی که صداش زده. که در اینجا سیستم عامله.
اما چرا صفر؟ قحطیه عدده؟ این یک بیشتر جنبه تاریخی داره تا قانون. در یونیکس، که C هم برای توسعه یونیکس خلق شد چنین در نظر گرفته شد که اگر برنامه ای پس از بسته شدن کد صفر رو به سیستم عامل پس داد، به این معنیه که برنامه به درستی اجرا شده. باور نمیکنید؟ بزارید تست کنیم. در ترمینال سیستم عاملتون دستورهای زیر رو بترتیب وارد کنید:

دستور اول لیست تمام فایلهای موجود در دایرکتوری فعلی رو بهتون نشون میده. وقتی نشون میده یعنی موفق عمل کرده. دستور دوم مقدار بازگشتی دستور قبلشو چاپ میکنه. اینجا ۰ نشون میده. یعنی دستور قبلی یا همان ls کارشو بدرستی انجام داده. این کد رو بهش میگن exit status code. یعنی چی؟ هر برنامه‌ای که در سیستم عاملهای یونیکسی وقتی بسته میشه یک کد حالت خروج رو میفرسته به سیستم عامل تا سیستم عامل بفهمه چجوری بسته شده. صفر یعنی عالی و بی مشکل بسته شده. اگر برنامه ناموفق اجرا بشه کدی غیر از صفر رو برمیگردونه و هر کد مشخص کننده یک نوع خطاست. بیاید امتحانش کنیم و یه دستور من دراوردی بنویسیم:

عددی غیر صفر(۱۲۷) به شما نشون داده میشه. اینجاست که میتونید بفهمید اون return چرا جلوش صفره. سیستم عامل از برنامه خواسته که یک مقدار صحیح رو بهش برگردونه. اگر برنامه تا آخرین خط به درستی اجرا بشه خب این خط ;return 0 هم اجرا خواهد شد. پس خیلی منطقیه. میگیم اگر تا اخرین دستور (return 0) درست پیش رفتی صفر رو بعنوان exit status code به سیستم عامل برگردون. در حقیقت بازگردوندن صفر باربر است با اعلام اجرای موفقیت آمیز برنامه. پس اگر برنامه به هر شکل نتونه به این خط برسه یعنی نتونسته با موفقیت اجرا بشه و صفر هم دیگه برنمیگرده. چون اصلا به این خط نرسیده متوقف شده. اگر هم این خط رو ننویسید باز هم ممکنه برنامه به درستی اجرا بشه. اما خوبه که طبق قوانین مندرج در آخرین استانداردها پیش بریم و اون خط رو همیشه بنویسیم. بعضی کامپایلرها این شکل از تابع ()main رو هم میپذیرن:

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

آکولاد {}

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

اعلان متغیر

متغیر اصلا چی هست؟ اعلان متغیر چیه؟
در سیستم های کامپیوتری دو نوع کلی حافظه وجود داره. حافظه فرار یا volatile و حافظه غیرفرار یا non-volatile. همونطور که از اسمشون پیداست در حافظه های فرار اطلاعات موندگار نیستند. در این نوع از حافظه ها، اطلاعات پس از قطع برق، از حافظه پاک میشوند. انواع مختلفی از حافظه های فرار وجود دارند که به ترتیب زیر هستند. در نظر داشته باشید قیمت و سرعت رابطه بسیار مستقیمی با هم دارند و هرچقدر سریعتر باشند گرانتر هستند و گنجایش کمتری دارند:
۱- ثبات یا رجیستر
۲- کش
۳- رم یا حافظه اصلی
حافظه های غیر فرار مانند ssd ها و هاردیسک و دیسک نوری برای ذخیره دائمی اطلاعات مورد استفاده قرار میگیرند.
متغیرها همونطور که از اسمشون مشخصه متغیرن و مقدارشون در طی اجرای برنامه میتونه تغییر کنه. ما برای متغیرها اسم تعیین میکنیم. مانند num در برنامه ای که نوشتیم. در واقع متغیر اسمیه که برای قسمتی از حافظه تعیین میکنیم که مقدار مورد نظر ما در اون مستقر میشه. متغیرها انواع گوناگونی دارند. مثل int که گفتیم نشان دهنده عدد صحیحه. اما چرا نوع؟ یه توضیحی بدم بعد بریم سراغ دلیل تعیین نوع.
گفتیم پردازنده سرعت بسیار زیادی داره. مثلا یک پردازنده با سرعت ۴ گیگاهرتز میتونه ۴ میلیارد عملیات را در یک ثانیه انجام دهد. این واقعا عدد بزرگیه. اگر پردازنده بخواد با چنین سرعتی اطلاعات و دستور العملها رو از هارد دیسک و امثال اون بخونه کارها بسیار کند میشه چون سرعتی بسیار بسیار کمتر دارند و وقت عظیمی از پردازنده هدر میره. برای رفع این مشکل اومدن حافظه های سریعتر با حجم کمتری ساختن که در اون فقط اطلاعاتی قرار بگیره که نیاز به پردازش دارند. انواع گوناگونی از حافظه معرفی شد. یکیشون همین رم بود. قطعا سرعت رم نیز در برابر سرعت پردازنده کند است اما نسبت به هر نوع حافظه غیر فراری بسیار بیستر است. برای رسیدن به بهینه ترین حالت پردازنده، حافظه های دیگری مثل کش و ثبات معرفی شدند. در واقع داده هایی که در رم منتظر پردازش هستند بصورت نوبتی در آرایه هایی به کش و ازونجا به ثباتها برای پردازش فرستاده میشن و بعد از پردازش به رم بر میگردن. در حینی که پردازنده مشغول محاسبات است این نقل و انتقالات پیوسته انجام میشود و پردازنده بسیار کمتر بیکار و منتظر میماند. در یک سیستم کامپیوتری منابع سخت افزاری از اهمیت زیادی برخوردار هستند و چون محدود نیز هست هر پروسه و برنامه ای نباید به میل خود به هر مقدار و خودسرانه به منابع دست پیدا کنه. اینجاست که سیستم عامل وارد میدان میشه. در واقع سیستم عامل برنامه‌ایه که در لایه پایین سیستم(روی سخت افزار لخت) میشینه و میشه مسئول تخصیص منابع سخت افزاری. هر برنامه ای که احتیاج به رم یا پردازنده یا ورودی/خروجی داشته باشه باید با سیستم عامل حرف بزنه. و این سیستم عامله که مسئول تخصیص منابع به برنامه ها میشه. وقتی برنامه ما هم اجرا میشه به کرنل یا هسته سیستم عامل میگه که من احتیاج به منابع سخت افزاری دارم. حالا این برنامه ما باید به کرنل بگه که چه مقدار رم و به چه شکلی میخواد.کرنل هم پس از بررسی های دقیق و کامل بهش اوکی میده و برنامه رو برای اجرا وارد رم میکنه. این مقدار و شکل رو نوع متغیر تعیین میکنه. چرا اصلا باید اینجوری باشه؟ گفتیم که زبان C زبان بسیار بهینه شده ایه. تصور کنید C به هر نوع داده ای ۶۴ بیت جا میداد. اونوقت یک عدد ساده مثل یک ۶۴ بیت از حافظه رو میگرفت، در صورتی که با یک بیت نیز میتوان عدد ۱ رو در رم نگه داشت. اینجاست که انواع مختلف داده ها پا به عرصه وجود میزارن. درباره انواع مختلف متغیرها و ویژگی ها و مکانیسم های هر متغیر در ادامه تاپیک به طور کامل صحبت خواهیم کرد. در اینجا فقط در همین حد باید بدونید که با نوشتن کلمه کلیدی int، به برنامه میگیم که متغیر ما از نوع عدد صحیح است(چون مقدار رمی که برای عدد صحیح در نظر گفته میشود با انواع داده های دیگر مثل عدد اعشاری فرق دارد) پس به اندازه یک عدد صحیح(فارغ از مقداری که ما میدهیم) برای آن در رم جا در نظر بگیر.

حالا =
مساوی رو در ریاضی خوندیم و همه مون آشنایی داریم باهاش. اما در برنامه نویسی تفاوت داره معناش با ریاضی. در ریاضی عبارت ۵ + ۱ = ۶ رو اینگونه مخونیم که ۵ بعلاوه ۱ مساویست با ۶. اما در برنامه نویسی اینگونه نیست. در برنامه نویسی یک مقدار عددی هیچگاه نمیتواند در سمت چپ مساوی قرار بگیرد. دلیلشو الان خدمتتون عرض میکنم. دلیلش اینه که این علامت = در برنامه نویسی اصلا مساوی خونده نمیشه بلکه انتساب خونده میشه. به چه صورت؟
برگردیم به برنامه خودمون num = 1. در ریاضی بدین گونه خونده میشه num مساویست با ۱. اما در برنامه نویسی به این صورت خوانده میشه: عدد ۱ رو در متغیر num قرار بده. عدد ۱ رو به num نسبت بده. در C برنامه از بالا به پایین و از چپ به راست خونده میشه توسط کامپایلر. یکی از معدود مواردی که کامپایلر از راست به چپ میخونه همین علامت انتصابه، پس حالا میفهمیم چرا عدد نمیتونه در سمت چپ انتساب قرار بگیره. چون نمیتونیم بگیم متغیر num رو در ۱ قرار بده. چون ۱ یه عدده و ثابته و هیچ عددی جایی برای ذخیره چیزی نداره بلکه این متغیرها هستند که جا برای ذخیره سازی دارند.

پس دوباره و بطور خلاصه مرور میکنیم:

کلمه کلیدی int نوع داده رو مشخص میکنه برای اینکه کرنل بفهمه چه مقدار فضا از رم بهش بده.
متغیر ما num است که گفتیم نامی است که برای اون قسمت از حافظه که مد نظر ماست و براش تعیین کردیم برای راحتی خودمان.
اگر دقت کنید در طرفین مساوی فاصله قرار دادیم. این قانون نیست و لازم هم نیست. اما به شکیل و خوانا شدن کدهامون بسیار کمک میکنه. در حقیقت C تمام فاصله های خارج از رشته های کاراکتری(در آینده خواهیم خواهند) رو نادیده میگیره. پس چه بهتر که کدهامونو قشنگتر بنویسیم.

نامگذاری متغیر

در نامگذاری متغیرها باید به چند نکته دقت کرد.
اول اینکه اینکه نام متغیر گویای چیزی باشه که هست. مثلا فرض کنید دوتا متغیر به اسم قد و وزن داریم. سعی کنیم از خودِ کلمات قد و وزن استفاده کنیم.

همین رو میتونستیم با x و y هم تعریف کنیم. اما برای خواننده برنامه بسیار بی معنیه. حتی بعد از گذشتن مدتی، شاید خودمونم نفهمیم این x و y چکارن. پس توصیه میشه از نامهای با معنی استفاده کنید.

در صورتی که به هر دلیل نتونستید از نام بامعنی برای متغیرهاتون استفاده کنید، حتما کامنت یا توضیحات براشون بگذارید:

اینجا ما از نام با معنی برای متغیرمون استفاده نکردیم. اما در توضیحات نوشته ایم که این متغیر شعاع دایره میباشد.

مورد دوم شیوه نامگذاری متغیرهاست. C قواعدی رو برای نامگذاری متغیرها گذاشته که برای نوشتن برنامه های درست و پرتابل باید ازین قوانین و استانداردها پیروی کنیم.

نام متغیر میتواند شامل حروف کوچک، حروف بزرگ، اعداد و علامت زیرخط(_) باشد.اما نام متغیر نمیتواند با عدد شروع شود.

از آنجا که اسم بعضی از کتابخانه های C با زیرخط شروع میشوند، مانند G_config.h_ توصیه میشود که از شروع نام متغیر با _ حذر کنید.

تابع ()printf

از کلمه print متوجه میشویم که وظیفه این تابع چاپ اطلاعاتی در خروجی است. این اطلاعات، همان مقادیری است که در داخل پرانتز آمده است. حرف f مخفف formatted یا قالب بندی شده است. زیرا تابع ()printf خروجی را پیراسته و قالب دهی میکند. عملکرد و نحوه کار این تابع در فایل stdio.h مشخص شده است. علامتهای دابل کوتیشن ” در ابتدای و انتهای رشته های کاراکتری الزامی است. در مورد رشته های کاراکتری در آینده بحث خواهیم کرد. هرآنچه که بین پرانتزها قرار میگیرد، آرگومان خوانده میشود. این آرگومانها را تابع ()main به تابع ()printf ارسال کرده است(پاس داده است). بیاید یه لحظه عمیقتر به سیستم برنامه نگاه کنیم. برنامه ما با تابع ()main شروع شده. تمام دستورات از بالا به پایین اجرا میشوند و هنوز کنترل در اختیار ()main است. در لحظه ای که به تابع ()printf میرسیم کنترل برنامه از دست ()main خارج شده و کنترل به ()printf میرسد. این تابع بعد از اتمام کار، کنترل برنامه رو به ()main میده مجدد. یه لحظه به بحث قبلمون برگردیم. یادتون هست گفتیم تابع ()main صحت اجرای هر تابع رو چک میکنه و خودِ سیستم عامل هم مستقیم صحت اجرای تابع ()main رو کنترل میکنه؟ خب خب. الان تابع ()main باید چجوری بفهمه که تابع ()printf درست کار کرده؟ اینو موکول میکنیم به بحث ساختارهای تصمیم. فقط خواستم در نظر داشته باشید که تابع ()printf یک مقدار عددی رو به تابعی که صداش زده برمیگردونه.

مورد بعدی در این برنامه ما که در تابع ()printf دیده میشه بک اسلش یا backslash است. همینجا یه توضیحی بدم:

بک اسلش در C و بسیاری از زبانها معرف و نشانگرِ توالیهای فراره. توالی چیه؟ فرار از دست کی؟
توالی یعنی پشت سرم هم و ممتد. ازین جهت بهش میگن توالی چون مستقیما پشت سر این آقای بک اسلش باید کاراکتر معنادار بیاد. کاراکتر معنا دار کاراکتریه که بطور استاندارد براش یه رفتار و وظیفه توصیف شده باشه.
فرار از دست کی؟ دشمن. فرار از دست f در ()printf. گفتیم که این تابع خروجیش رو قالب دهی میکنه. فکر کنید شما در حال نوشتن اطلاعاتی در تابع ()printf باشید که این اطلاعات برای تابعمون معنای مشخصی دارند. چجوری باید به تابع ()printf بگیم که ما منظورمون چیز دیگه ایه؟ یا مثلا میخواهید که تابع بعد از چاپ اطلاعات موجود در خودش، یک خط فاصله بده و بره خط بعدی؟ چجوری بهش میگید که یه خط رد کن؟ اینتر میزنید؟ اولا Enter کلیدیه که برای شما معنا داره نه برای اون. ثانیا بعد از اجرای برنامه، شما فقط در قسمتهایی که ورودی میخواد، قدرت زدن اینتر رو دارید. اگر برنامه ورودی نپذیره، اینتری در کار نخواهد بود. در حقیقت وقتی ما کلید اینتر رو میزنیم دستوری رو از صفحه کلید به سمت سیستم عامل میفرستیم. که محتوای اون دستور به زبان ساده اینه که یک خط رو رد کن. پس سیستم عامل چنین دستوری رو میفهمه. حالا ما بجای زدن اینتر بطور سخت افزاری و دستی، ازش میخواهیم خودش همچین دستوری رو بدون دخالت کاربر به سیستم عامل بده.
در مثال ما در تابع ()printf توالی فرار n\ وجود داره.
در مثال ما n\ بیانگر ایجاد یک خط جدید است. n را با newline یا خط جدید به خاطر بسپارید. توالی های فرار دیگری نیز وجود دارند که در جای خود شرع خواهیم داد.
نکته ای که حائز اهمیت است بکار بردن این توالی هاست. یکبار دیگر کد منبع برنامه این پست و نتیجه آن را نگاه کنیم:

خروجی:

همانطور که مشاهده کردید، صرفِ نوشتن جمله در دو خط، به معنای جدا کردن خطوط در خروجی نیست. اما خط بعد از دومین تابع ()printf بدلیل داشتن n\ در انتهای آن، خالی رد میشود.
در مورد سِمیکالن یا نقطه ویرگول هم یک توضیح بدم. در C، حضور ; به معنای اتمام دستور میباشد. بگونه ای که حتی اگر اینتر هم نزنیم و تمام دستورات را در یک خط بنویسیم قابل پذیرش است. این دو برنامه هر دو یک کار انجام میدهند:

کد نویسی به شیوه دوم باعث ناخوانا شدن برنامه میشود. خوانایی برنامه را قربانی هیچ چیز نکنید.

مشخص‌کننده

گفتیم تابع ()printf تابعیه که خروجیشو قالب دهی میکنه. در باره شیوه و مکانیزم قالب دهی به مرور بیشتر آشنا میشم.
در C ما تعدادی identifier یا مشخص کننده داریم. هر نوع داده ای، مشخص کننده خاص خودش رو داره. برای نوع داده عدد صحیح یا int دو نوع مشخص کننده وجود داره: d% و i%. در استفاده از هرکدوم ازینها مختارید. ولی چیزی که مهمه اینه که بدونید برای مشخص کردن عدد صحیح در C ازین دو نوع مشخص کننده استفاده میشه. خب فرض کنیم ما در تابع ()printf از این مشخص کننده استفاده کردیم. بعد چه خواهد شد.
مثال قبلمون رو دوباره بررسی کنیم:

گفتیم که C یک زبان کامپایلیه. یعنی کامپایلر یک بار اون رو ترجمه و به زبان ماشین برمیگردونه و یک فایل اجرایی بهمون میده. پس همه چیز در حین کامپایل باید درست باشه.
یکبار دیگه به اون خط کد نگاه کنید. در میانه متن d% قید شده است. این به کامپایلر میگه که آقا/خانم کامپایلر اینجا قراره یه عدد صحیح چاپ بشه. کامپایلر هم میگه مشکلی نیست ولی کدوم عدد صحیح؟ تابع ()printf به کامپایلر میگه: همون عدد صحیحی که در یک متغیر قرار گرفته و نام متغیر مستقیما بعد از این رشته اومده. کامپایلر میره نگاه میکنه ببینه که چه متغیریه. در توابع علامت ویرگول یا کاما مشخص کننده جدایی آرگومانهاست. کامپایلر میره نگاه میکنه یه ویرگول هست. میفهمه که اینجا آرگومان اول که یک رشته بوده تموم شده و به دنبال آرگومان یا پارامتر دومه. آرگومان دوم یه متغیره به نام num. نگاه میکنه ببینه آیا همچین متغیری در برنامه تعریف شده یا نه. و میبینه که بله چند خط بالاتر این متغیر از نوع عدد صحیح یا int تعریف شده و به خوشی و سلامتی این خط رو قبول میکنه. حالا هر وقت این برنامه اجرا بشه، مقدار موجود در متغیر num در محل d% قرار میگیره و چاپ میشه. هر یک از خطوط برنامه رو اینگونه برای خودتون بشکافید میبینید که واقعا چیز سختی نیست.


درباره نویسنده: Hoshyar Karimi

مطالب زیر را حتما بخوانید

1 دیدگاه

  1. سلام
    خیلی عالی بود نشستم به دقت خوندم و خیلی مسائل برام روشن شد اگر چند تا منبع معرفی میکردی بد نبود غنی تر میشد ممنون

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

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