خطاهای کامپایل و معنی پیامهای کامپایلر
خطاهای رایج کامپایل آردوینو را با متن دقیق پیام، علت و راهحل میشناسید؛ از جاافتادن سمیکالن تا حروف فارسی داخل کد و ناهمخوانی نوع داده.
آنچه در این درس میآموزید
- بفهمید کامپایلر چه میکند و پیام خطا را با قالب file:line:col بخوانید
- شش خطای پرتکرار کامپایل را با متن دقیقشان تشخیص دهید و رفع کنید
- تفاوت هشدار (Warning) و خطا (Error) و خطر «کامپایل با هشدار» را بدانید
فهرست مطالب
دکمهٔ Verify را زدهاید و کنسول پر از متن قرمز شده است. خبر خوب: خطای کامپایل بیخطرترین خطاست؛ برد شما هیچآسیبی نمیبیند و کامپایلر تقریباً همیشه آدرس دقیق مشکل را به شما میدهد. در این درس یاد میگیرید زبان کامپایلر را بخوانید و شش خطای پرتکرار را با یک نگاه تشخیص دهید.
کامپایل یعنی چه؟
میکروکنترلر ATmega328P روی برد Uno فقط زبان صفر و یک میفهمد. کامپایلر (Compiler) — در آردوینو از زنجیرهٔ ابزار avr-gcc استفاده میشود — کد C++ شما را به کد ماشین ترجمه میکند. کامپایلر بسیار سختگیر است: قبل از ترجمه، قواعد دستوری را سطربهسطر چک میکند و با نخستین خطا متوقف میشود. به همین دلیل خطای کامپایل تقریباً همیشه یعنی «یک غلط نوشتاری در کد»، نه مشکلی در برد.
محل خطاها در IDE 2 کنسول پایین پنجره است. قالب هر پیام چنین است:
blink.ino:7:3: error: expected ';' before '}' tokenیعنی فایل blink.ino، خط ۷، ستون ۳. همیشه از همین قالب استفاده کنید تا شمارهٔ خط را پیدا کنید؛ شمارهٔ خط روی نوار کنار ویرایشگر مشخص میشود. طبق درس عیبیابی، کنسول را از پایین به بالا بخوانید و نخستین خطا را بگیرید.
expected ';' و expected '}'
رایجترین خطای دنیای C++. کامپایلر انتظار سمیکالن دارد اما به } میرسد:
// Wrong
int ledPin = 13// Right
int ledPin = 13;اگر خودِ سمیکالن نبود و یک { بسته نشده مانده باشد، در پایان فایل این را میبینید:
expected '}' at end of inputاین یعنی یک { جایی باز شده و تا آخر فایل بسته نشده است؛ از آخرین { شمردهنشده شروع کنید و جفتها را بشمارید.
'xyz' was not declared in this scope
یعنی «این نام را اصلاً نمیشناسم». سه علت دارد:
// Wrong
void loop() {
digitalWrite(ledpin, HIGH); // declared as ledPin
}// Right
void loop() {
digitalWrite(ledPin, HIGH);
}- غلط تایپی —
ledpinبهجایledPin. - حساسیت به بزرگی و کوچکی حروف — C++ بین
ledPinوledpinفرق میگذارد؛pinModeرا همpinmodeننویسید. - تعریف نشدن متغیر — نامی را استفاده کردهاید که هیچجا تعریف یا اعلام نشده است.
حروف فارسی داخل کد: stray و expected unqualified-id
کد فقط انگلیسی میشود؛ یک حرف فارسی داخل کد (مثلاً هنگام تایپ کامنت فارسی در جای اشتباه، یا گیومهٔ «فارسی» بهجای گیومهٔ کد) بایتهایی تولید میکند که کامپایلر نمیشناسد:
stray '\331' in programعدد داخل پیام همان بایت ناشناخته است و بسته به حرف تایپشده فرق میکند؛ گیومهٔ خمیدهٔ انگلیسی هم معمولاً stray '\342' in program میدهد. اگر خط ادامه پیدا کند، کامپایلر ممکن است بلافاصله این را هم اضافه کند:
expected unqualified-id before 'int'این یعنی «توانستم تا اینجا بخوانم، اما بعدش چیزی نیست که انتظار تعریفی داشت». هر دو خطا معمولاً در یک خط هستند؛ خطِ اعلامشده در پیام را باز کنید و کاراکتر غیرانگلیسی را پاک کنید.
invalid conversion و cannot convert
کامپایلر چک میکند نوع دادهٔ هر ورودی تابع با آنچه تعریف شده یکی باشد:
// Wrong
digitalWrite(13, "HIGH"); // "HIGH" is text, not HIGH// Right
digitalWrite(13, HIGH);پیام دقیقش چنین است:
invalid conversion from 'const char*' to 'int'یعنی رشتهٔ متنی (const char*) را جای عدد (int) گذاشتهاید. در مقداردهی اولیه همیشه این شکل را میبینید:
cannot convert 'const char*' to 'int' in initializationمثلاً int x = "5"; — عدد ۵ را بدون گیومه بنویسید.
too few و too many arguments
تعداد ورودیهای تابع باید دقیقاً درست باشد:
too few arguments to function 'void digitalWrite(int, int)'یعنی ورودی کم دادهاید، مثل digitalWrite(13); که حالت HIGH یا LOW ندارد. حالت برعکس:
too many arguments to function 'void delay(long unsigned int)'مثل delay(1000, 500); که یک ورودی اضافه دارد. شمارهٔ خط را ببینید و پرانتز را با امضای تابع در مرجع رسمی مقایسه کنید.
کد بیرون از تابع: 'Serial' does not name a type
هر دستور اجرایی باید داخل setup() یا loop() باشد. اگر خطی را بالای توابع بنویسید، کامپایلر آن را بهعنوان «تعریف» میخواند و نمیتواند؛ بسته به خط، پیامهایی مثل این میبینید:
'Serial' does not name a typeیا
expected constructor, destructor, or type conversion before '.' tokenراهحل: خط را ببرید داخل setup() (اگر باید یک بار اجرا شود، مثل Serial.begin(9600)) یا داخل loop().
هشدار، نه خطا
کنسول دو نوع پیام میدهد: خطا (Error) کامپایل را متوقف میکند و پیام exit status 1 میآید؛ هشدار (Warning) یعنی «کد ترجمه شد، اما چیزی مشکوک است» — مثل متغیری که تعریف کردهاید اما استفاده نکردهاید. کد با هشدار کامپایل میشود و آپلود هم میشود، اما رفتار غلط احتمالی در کمین است: مثلاً مقایسهٔ عدد علامتدار و بیعلامت، یا قطع کردن قسمتی از مقدار در تبدیل نوع، نتیجهٔ نادرست اما بیصدا میدهد. قانون: هشدارها را هم صفر کنید، نه فقط خطاها را.
جدول مرجع سریع
| پیام خطا (دقیق) | رایجترین علت | راهحل |
|---|---|---|
expected ';' before ... |
نبود سمیکالن در خط قبل | سمیکالن انتهای خط قبل را اضافه کنید |
expected '}' at end of input |
آکولاد بازِ بستهنشده | جفتهای { و } را بشمارید |
'xyz' was not declared in this scope |
غلط تایپی، بزرگی/کوچکی، یا تعریفنشدن | نام را با محل تعریف مقایسه کنید |
stray '\331' in program |
حرف فارسی یا گیومهٔ خمیده داخل کد | کاراکتر غیرانگلیسی را پاک کنید |
expected unqualified-id before ... |
ادامهٔ خطای کاراکتر ناشناخته | خط اعلامشده را اصلاح کنید |
invalid conversion from 'const char*' to 'int' |
رشته بهجای ثابت عددی | "HIGH" را به HIGH تغییر دهید |
cannot convert 'const char*' to 'int' in initialization |
مقدار رشتهای برای متغیر عددی | عدد را بدون گیومه بنویسید |
too few arguments to function ... |
ورودی کمتر از تعریف تابع | امضای تابع را در مرجع ببینید |
too many arguments to function ... |
ورودی اضافه | ورودی اضافه را حذف کنید |
'Serial' does not name a type |
دستور اجرایی بیرون از تابع | خط را داخل setup یا loop ببرید |
تمرینها
تمرین
تمرین ۱ — سه خطا در یک اسکچ
در کد زیر دقیقاً سه خطای کامپایل عمدی گذاشتهایم. هر سه را پیدا کنید، پیام احتمالی هر کدام را حدس بزنید و سپس نسخهٔ اصلاحشده را بنویسید:
int ledPin = 13
void setup() {
pinMode(ledpin, OUTPUT);
}
void loop() {
digitalWrite(ledPin, HIGH)
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
}نمایش راهحل
خطاها: (۱) سمیکالن جاافتاده در انتهای int ledPin = 13 که پیام expected ';' before 'void' میدهد؛ (۲) ناهمخوانی حروف در ledpin بهجای ledPin که پیام 'ledpin' was not declared in this scope میدهد؛ (۳) سمیکالن جاافتاده در انتهای digitalWrite(ledPin, HIGH) که پیام expected ';' before 'delay' میدهد. نسخهٔ اصلاحشده:
int ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
}این اسکچ بدون هیچ خطا و هشداری کامپایل میشود.
:::
خلاصه
- کامپایل با avr-gcc کد شما را به کد ماشین ترجمه میکند و با نخستین خطا میایستد.
- قالب پیام خطا file:line است؛ شمارهٔ خط را از همانجا بگیرید.
- رایجترین خطاها سمیکالن و آکولاد جاافتاده و غلط تایپی نامها هستند.
- حروف فارسی داخل کد خطای
stray '\...'میدهند؛ کد همیشه انگلیسی است. - هشدار یعنی کد کامپایل شده اما مشکوک است؛ هشدارها را هم صفر کنید.
کدتان حالا سبز کامپایل میشود؛ در درس بعدی میرویم سراغ مرحلهٔ بعدی زنجیره، یعنی جایی که Verify سبز است اما آپلود روی برد شکست میخورد.