در قسمت قبل آموزش برنامه نویسی C به بررسی پروگرام و دیباگ برد NUCLEO-F030R8 و بررسی فایل های پروژه پرداختیم. در این قسمت به بررسی اعداد و متغیرها در زبان C می پردازیم.
حالا که چند برنامه ساده نوشتهایم، وقت آن است که به کامپیوتر کارهای جدیتری بدهیم. در این فصل یاد میگیریم چطور با اعداد کار کنیم.
به عنوان برنامهنویسان امبدد، ما باید به دقت بدانیم که هر عدد در سیستم ما چه معنایی دارد. برای مثال، عدد 32 میتواند تعداد گوسفندان باشد یا اینکه یک پین خاص در میکروکنترلر را فعال کند و باعث روشن شدن یک چراغ هشدار شود. میکروکنترلرهایی مثل STM32 گروهی از پینها را با یک عدد نمایش میدهند. بنابراین، تغییر یک بیت در این عدد میتواند عملکرد دستگاه را به طور کامل تغییر دهد. به همین دلیل، درک دقیق نحوه نمایش و تفسیر اعداد در سیستمهای کامپیوتری برای ما بسیار مهم است.
وقتی درک کردید که اعداد در برنامهنویسی چه نقشی دارند، نوبت به این میرسد که یاد بگیرید چگونه با استفاده از متغیرها، این اعداد را در برنامه خود ذخیره کنید. سپس، خواهید آموخت که چگونه با دستکاری بیتهای موجود در رجیسترهای سختافزاری، عملکردهای مختلف دستگاه را کنترل کنید. این کار به شما کمک میکند تا درک عمیقتری از نحوه کار برنامهای که در فصل ۳ نوشتید، پیدا کنید.
کار با اعداد صحیح (integers)
با اعداد صحیح یا اعداد کامل شروع میکنیم. اینها اعدادی هستند که نقطه اعشار ندارند، مانند 37، 45، -8 و 256.
فهرست عملیاتهای قابلانجام در زبان C در جدول 4-1 نمایشدادهشده است.
جدول 4-1: عملگرهای عدد در C
عملگر | شرح |
+ | جمع |
– | تفریق |
* | ضرب |
/ | تقسیم (به یک عدد صحیح کوتاه میشود) |
% | باقیمانده (باقیمانده پس از تقسیم را برمیگرداند) |
برنامه زیر نحوه کار این عملگرها را نشان میدهد:
1 2 3 4 5 6 7 8 9 | #include <stdio.h> int main() { printf("3 + 2 is %d\n",3 + 2); printf("3 - 2 is %d\n",3 - 2); printf("3 * 2 is %d\n",3 * 2); printf("3 / 2 is %d\n",3 / 2); printf("10 / 9 is %d\n",10 / 9); printf("3 %% 2 is %d\n",3 % 2); return (0); } |
برای نمایش نتیجه هر عملیاتی، از دستور printf استفاده میکنیم. این دستور به ما اجازه میدهد تا نتیجه محاسبات خود نمایش دهیم. برای مثال، اگر بخواهیم نتیجه یک تقسیم را نمایش دهیم، کافیست در داخل دستور printf، در جایی که میخواهیم عدد ظاهر شود، %d قرار دهیم و سپس عبارت محاسباتی را بهعنوان آرگومان بعدی به آن بدهیم. نکته مهم این است که برای نمایش خود علامت درصد (%)، باید آن را دو بار بنویسیم.
برای مشاهده خروجی این کد، بیایید آن را وارد IDE کنیم. System Workbench for STM32 را راهاندازی کنید و سپس مراحل فصل 2 را برای ایجاد یک برنامه طی کنید. البته، این بار به جای ایجاد یک برنامه “Hello World”، یک پروژه خالی C/C++ Native ایجاد خواهیم کرد، بنابراین C Managed Build را به عنوان قالب انتخاب کنید.
در زیر Project Type
Executable ▶ Empty Project
را انتخاب کنید. سپس با انتخاب File ▶ New ▶ Source File فایل برنامه را ایجاد کنید.
متن برنامه را در پنجره ویرایش وارد کرده و سپس فایل را ذخیره کنید. مشابه قسمتهای قبل، کد خروجی باینری پروژه خود را بسازید و اجرا کنید. برنامه باید خروجی خود را در انتهای پنجره کنسول نمایش دهد (شکل 4-1 را ببینید).
همانطور که میبینید، برنامه باید نتیجه هر محاسبهای را که انجام میدهد چاپ کند.
تعریف متغیر int برای نگهداری اعداد صحیح (integers)
تا اینجا، ما با اعداد ثابت کار کردیم. اما برای ذخیره اطلاعاتی که ممکن است تغییر کنند، نیاز به متغیر داریم. متغیرها مکانهایی در حافظه هستند که برای ذخیره اطلاعات استفاده میشوند. قبل از اینکه بتوانیم از یک متغیر استفاده کنیم، باید آن را تعریف کنیم:
1 | type variable_name; // comment (توضیحی که کار این متغیر را مشخص میکند) |
برای نمونه، استفاده از int بهعنوان type، نشان میدهد که متغیر یک عدد صحیح است. دقیقاً، این نوعی از عدد صحیح است که کامپیوتر میتواند راحتتر با آن کار کند. در ادامه این فصل، انواع دیگر اعداد صحیح را بررسی خواهیم کرد.
نام متغیرها (variable_name) با یک حرف شروع میشوند و فقط باید شامل حروف، ارقام و زیرخط (underscore) باشند. کتابخانه نرمافزار STM32 از سبک نامگذاری متغیر بهصورت کوهان شتری (camel case) استفاده میکند، در این روش حرف اول متغیر با حرف کوچک نشان داده میشود؛ اما حرف اول سایر کلماتی که در ادامه میآیند با حروف بزرگ نمایش داده میشود؛ بنابراین برای سازگاری، ما در سراسر این دوره از این سبک استفاده خواهیم کرد.
1 | startTime currentStation area |
نامها میتوانند با یک زیرخط (underscore) شروع شوند؛ اما چنین نامهایی برای توابع سیستم رزرو شدهاند و نباید در برنامهنویسی معمولی استفاده شوند. همچنین، هرگز از l (L کوچک) یا O (O بزرگ) بهعنوان نام متغیر استفاده نکنید؛ زیرا به دلیل شباهت زیاد آنها به اعداد 1 و 0 میتواند منجر به سردرگمی و خطا شود. کد زیر را در نظر بگیرید:
1 | O=l+1+O*0; // |
این نوع برنامهنویسی میتواند شما را به دردسر بیندازد.
از لحاظ فنی، میتوانید کامنت را از تعریف متغیر خود حذف کنید. بااینحال، درج یک نظر به افرادی که بعد از شما با کد کار میکنند، نشان میدهد که چرا متغیر را تعریف کردهاید و چه کاری انجام میدهد. بهعبارتدیگر، به شما کمک میکند تا یک فرهنگ لغت کوچک یا واژهنامه ایجاد کنید.
انتصاب مقادیر به متغیرها
پس از تعریف یک متغیر، میتوانیم با استفاده از دستور انتصاب، مقداری را به آن نسبت دهیم. فرم کلی یک دستور انتساب بهصورت زیر است:
1 | variable = expression; |
این دستور به کامپیوتر میگوید که مقدار عبارت را محاسبه کرده و در متغیر ذخیره کند. سپس متغیرها را میتوان در هر جایی که یک عدد صحیح قرار میگیرد، مانند دستور printf استفاده کرد. برنامه زیر نمونهای از تعریف متغیر، انتساب و استفاده را نشان میدهد:
1 2 3 4 5 6 7 8 9 10 11 | /* * A program to sum two variables */ #include <stdio.h> int main() { int aNumber; // متغیر عدد صحیح int otherNumber; // یک متغیر عدد صحیح دیگر aNumber = 5; otherNumber = 7; printf("Sum is %d\n", aNumber + otherNumber); return (0); } |
این برنامه دو متغیر aNumber و otherNumber ایجاد میکند، سپس به هر کدام مقداری نسبت داده و در نهایت مجموع آنها را چاپ میکند. شما میتوانید این کد را در
System Workbench for STM32 وارد کنید.
مقداردهی اولیه به متغیرها
زمانی که یک متغیر در برنامه خود تعریف میکنید، به کامپایلر C دستور میدهید تا فضای حافظهای را برای یک عدد صحیح (int) اختصاص دهد. اما قبل از اینکه شما مقداری برای آن مشخص کنید، این متغیر مقداردهی نشده تلقی میشود و ممکن است حاوی هرگونه داده بیارزش (garbage value) باقیمانده از آخرین باری که حافظه استفاده شده است، باشد.
برای اینکه این موضوع را در برنامه خود برسی کنیم، دیباگر را بازکرده و پنل متغیرها (Variables panel) را درحالیکه خطبهخط (step-by-step) در برنامه پیشروی میکنید، مدام چک کنید.
قبل از اینکه شما مقادیری را به آنها اختصاص دهید، دو متغیر ما، aNumber و otherNumber دارای مقدار صفر هستند. اما متغیرهای مقداردهی نشده میتوانند هر مقداری داشته باشند؛ این که آنها در اینجا صفر هستند صرفاً تصادفی است.
ما میتوانیم یک متغیر را در زمان تعریف با اضافهکردن یک دستور انتساب، مقداردهی اولیه کنیم:
1 | int aNumber = 5; // Some number |
این کار در اکثر موارد ایده خوبی است، زیرا اطمینان حاصل میکنیم که برنامه با مقادیر مشخص متغیرها کار میکند. بیایید برنامه خود را برای اضافهکردن این مقداردهی اولیه بازنویسی کنیم:
1 2 3 4 5 6 7 8 9 | /* * A program to sum two variables */ #include <stdio.h> int main() { int aNumber = 5; // متغیر عدد صحیح int otherNumber = 7; // یک متغیر عدد صحیح دیگر printf("Sum is %d\n", aNumber + otherNumber); return (0); } |
پس از انجام این تغییر، میتوانیم خطوطی که متغیرها را مقداردهی اولیه میکنند حذف کنیم.
انواع متغیر int
C انواع اعداد صحیح دیگری بهغیراز int دارد که برای نمایش اعداد با اندازههای مختلف استفاده میکند.
در کامپیوترها، حافظه بهصورت گروههای 8 بیتی که بایت نامیده میشوند، سازماندهی میشود. این روش به عنوان کارآمدترین شیوه برای مدیریت دادهها شناخته شده است. برای نمایش اعداد بزرگتر، کامپیوتر میتواند چندین بایت را با هم ترکیب کند تا اعداد 16، 32 یا 64 بیتی ایجاد کند. نوع داده int در زبان برنامهنویسی C، به کامپایلر دستور میدهد تا برای ذخیره اعداد صحیح، از کارآمدترین اندازه ممکن استفاده کند. این اندازه بسته به معماری سیستم میتواند 16 یا 32 بیت باشد. برای مثال، در پردازنده ARM Cortex-M0، که ما از آن استفاده میکنیم، اعداد صحیح به صورت 32 بیتی ذخیره میشوند.
زبان C به برنامهنویس اجازه میدهد تا با انتخاب نوع داده مناسب برای اعداد صحیح، بهینهسازی بیشتری در برنامه انجام دهد. برای مثال، اگر میخواهیم اعدادی بین 0 تا 100 را ذخیره کنیم، میتوانیم از نوع داده short int استفاده کنیم که حافظه کمتری نسبت به نوع داده int اشغال میکند. برای نمایش اعداد کوچکتر، نیازی به تمام فضای یک عددصحیح معمولی نیست. البته، استاندارد زبان C تنها تضمین میکند که short int از int بزرگتر نباشد، اما در اکثر پیادهسازیها، short int کوچکتر است.
عبارت زیر یک short int را تعریف میکند:
1 | short int shortNumber; // یک عدد صحیح کوچکتر از حد معمول |
یک عدد صحیح بزرگتر از حد معمول را میتوان با long تعریف کرد:
1 | long int longNumber; // یک عدد صحیح بزرگتر از حد معمول |
زمانی که کامپیوترها توانایی پردازش کارآمد اعداد طولانیتر از long int را به دست آوردند، مردم به یک نوع عدد صحیح نیاز داشتند که بتواند بیتهای بیشتری را در خود جای دهد.
در نتیجه long long integer ایجاد شد:
1 | long long int veryLongNumber; // یک عدد صحیح حتی بزرگتر |
استاندارد C اندازه هر نوع عدد صحیح را تعریف نمیکند. همه آنها میتوانند اندازه یکسانی داشته باشند و شما همچنان یک کامپایلر استاندارد خواهید داشت. بااینحال، این موارد را تضمین میکند:
1 | sizeof(short int) <= sizeof(int) <= sizeof(long int) <= sizeof(long long int) |
عملگر sizeof به شما میگوید که یک متغیر یا نوع دادهای خاص، چه مقدار فضا در حافظه کامپیوتر اشغال میکند.
بیایید ببینیم که انواع متغیر int برای کامپایلر روی سیستم ما چقدر فضا اشغال میکند.
یک برنامه کوتاه برای چاپ اندازه انواع متغیر int:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /* * / * نمایش انواع مختلف عددی. */ #include <stdio.h> int main () { short int aShortInt; // عدد صحیح کوتاه int aInteger; // عدد صحیح پیشفرض long int aLongInt; // عدد صحیح بلند long long int aLongLongInt; // عدد صحیح خیلی بلند printf("Size of (short int) = %ld (bytes) %ld bits\n", sizeof(aShortInt), sizeof(aShortInt) * 8); printf("Size of (int) = %ld (bytes) %ld bits\n", sizeof(aInteger), sizeof(aInteger) * 8); printf("Size of (long int) = %ld (bytes) %ld bits\n", sizeof(aLongInt), sizeof(aLongInt) * 8); printf("Size of (long long int) = %ld (bytes) %ld bits\n", sizeof(aLongLongInt), sizeof(aLongLongInt) * 8); return (0); } |
در قسمت قبل، از %d برای چاپ یک عدد استفاده کردیم. در این برنامه، از %ld استفاده میکنیم، زیرا sizeof یک long int برمیگرداند و %ld برای چاپ اعداد long int استفاده میشود.
این برنامه خروجی زیر را روی سیستم من تولید میکند:
1 2 3 4 | Size of (short int) = 2 (bytes) 16 bits Size of (int) = 4 (bytes) 32 bits Size of (long int) = 8 (bytes) 64 bits Size of (long long int) = 8 (bytes) 64 bits |
در مثال ارائه شده، اندازه هر دو نوع داده long int و long long int برابر با هم (برابر با 64 بیت یا 8 بایت) میباشد. این نتیجه در کامپایلر GNU GCC روی پردازنده x86_64 حاصل شده است.
توجه داشته باشید که اندازه دقیق انواع داده به معماری سیستم، تنظیمات کامپایلر و استاندارد زبان برنامهنویسی مورداستفاده بستگی دارد. در برخی سیستمها و کامپایلرها، اندازه long int ممکن است 32 بیت و اندازه long long int 64 بیت باشد، که مطابق با استاندارد زبان C است.
بنابراین، برای اطمینان از اندازه دقیق انواع داده در برنامه خود، همیشه از عملگر sizeof استفاده کنید. این عملگر اندازه یک نوع داده یا یک متغیر را بر حسب بایت برمیگرداند.