خوش آموز درخت تو گر بار دانش بگیرد، به زیر آوری چرخ نیلوفری را


آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
نویسنده : امیر انصاری
خطاهای مربوط به نگارش زبان و خطاهای معنایی

سیستم یکپارچۀ سازمانی راهکار



برنامه نویسی می تواند دشوار باشد، و راه های زیادی برای ایجاد اشتباهات وجود دارد. خطاها معمولاً در یکی از دو دسته بندی زیر هستند : خطاهای دستور زبان (syntax errors) و خطاهای معنایی (semantic errors) که به آنها خطاهای منطقی (logic errors) نیز گفته می شود.

یک خطای دستور زبان (syntax error) هنگامی اتفاق می افتد که یک بیانیه (statement) از نظر قوانین نگارشی زبان ++C صحیح نباشد. خطاهای دستور زبان (syntax errors) می تواند شامل مواردی همچون فراموش کردن سمی کالن ها، متغیرهای اعلام نشده، پرانتزها یا براکت هایی که بسته نشده اند، رشته هایی که به درستی پایان نیافته اند و ... باشد. برای مثال برنامه زیر دارای چندین خطای دستور زبان (syntax error) می باشد :

#include "iostream"; // preprocessor statements can't have a semicolon on the end

int main()
{
std:cout < "Hi there; << x; // invalid operator (:), unterminated string (missing "), and undeclared variable
return 0 // missing semicolon at end of statement
}

خوشبختانه، کامپایلر معمولاً خطاهای دستور زبان (syntax errors) را کشف می کند و هشدار و خطایی تولید می کند، برای همین شما به سادگی می توانید این خطاها را پیدا کرده و برطرف سازید. برای همین کافیست تا مجدداً برنامه را بعد از رفع خطاهای دستور زبان (syntax errors) کامپایل کنید تا از شر این خطاها خلاص شوید.

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

بعضی وقتها خطاهای معنایی (semantic errors) منجر می شوند تا در هنگام اجرا، برنامه کرش کند، مانند خطای تقسیم بر صفر (divide by zero).

#include "iostream"

int main()
{
int a = 10;
int b = 0;
std::cout << a << " / " << b << " = " << a / b; // division by 0 is undefined
return 0;
}

بعضی وقتها هم خطاهای معنایی (semantic errors) فقط منجر به تولید نتایج اشتباه می شوند :

#include "iostream"

int main()
{
std::cout << "Hello, word!"; // spelling error
return 0;
}

یا

#include "iostream"

int add(int x, int y)
{
return x - y; // function is supposed to add, but it doesn't
}

int main()
{
std::cout << add(5, 3); // should produce 8, but produces 2
return 0;
}

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

در مثال های بالا، خطاها نسبتاً آسان به نظر می رسند. اما در برنامه های واقعی و غیر آزمایشی، معمولاً پیدا کردن خطاهای معنایی (semantic errors) به این آسانی نیست که صرفاً با یک نگاه در کد بتوانیم به دلیل خطا پی ببریم.

خوشبختانه، برای رفع این گونه خطاها ابزار اشکال زدا (debugger) در محیطهای IDE معمولاً وجود دارد.

دیباگر یا اشکال زدا (debugger)


یک دیباگر (debugger) ، یک برنامه کامپیوتری است که به برنامه نویس ها اجازه می دهد تا چگونگی اجرای یک برنامه را کنترل کنند و آن چیزی را که در طول اجرای برنامه اتفاق می افتد مشاهده کنند. به عنوان مثال، برنامه نویس می تواند با استفاده از ابزار دیباگر (debugger) یک برنامه را خط به خط اجرا کند و مقادیر متغیرها را در هر خط مشاهده و بررسی کند. آن گاه برنامه نویس می تواند مقادیر ایجاد شده در طول برنامه را با مقادیر واقعی که مد نظرش دارد مقایسه کند تا ببیند که برنامه انتظارش را برآورده کرده است یا خیر، و یا مسیر اجرای ترتیبی کدها را مرور کند و با انتظاراتش مقایسه کند. ابزار دیباگر (debugger) یک ابزار فوق العاده برای ردیابی خطاهای معنایی (semantic errors) می باشد.

خوشبختانه امروزه تقریباً تمامی محیطهای توسعه (IDE) دارای ابزارهای دیباگر (debugger) داخلی می باشند و نیازی به نصب برنامه جداگانه ای از این بابت ندارید. محیط توسعه ویژوال استودیو 2012 نیز که در این آموزش ها از آن استفاده می کنیم یک ابزار دیباگر (debugger) قدرتمند در داخلش دارد.

قدم زدن (Stepping)


قدم زدن (Stepping) یکی از ویژگیهای ابزار دیباگر (debugger) می باشد که به شما امکان می دهد برنامه را خط به خط اجرا کنید. با استفاده از امکان قدم زدن (Stepping) می توانید هر خط از کد برنامه را به تنهایی اجرا کرده و نتیجه اجرای آن را با انتظاراتی که از برنامه نوشته شده دارید، مقایسه نمایید.

سه نوع دستور قدم زدن (Stepping) وجود دارد که در ادامه به تشریح آنها خواهیم پرداخت :

  • step into
  • step over
  • step out

Step into


دستور Step into خط بعدی از کد را اجرا می کند. اگر این خط کد مربوط به فراخوانی یک تابع باشد، وارد تابع می شود و کنترل اجرای برنامه را به ابتدای تابع می برد.

بیایید به یک برنامه ساده نگاهی بیندازیم :

#include "iostream"

void printValue(int nValue)
{
std::cout << nValue;
}

int main()
{
printValue(5);
return 0;
}

همانطور که می دانید، وقتی که یک برنامه را اجرا می کنید، اجرای برنامه از تابع main آغاز می شود. از آنجا که ما می خواهیم برنامه را دیباگ کنیم از دستور Step into برای اجرای خط به خط برنامه استفاده می کنیم.

برای انجام این کار وارد منوی Debug شده و دستور Step into را اجرا کنید.

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
وقتی این کار را انجام می دهید دو چیز باید اتفاق بیفتد. اول اینکه از آنجایی که برنامه ما یک برنامه کنسول (console) می باشد، یک پنجره خروجی کنسول باید باز شود. از آنجا که برنامه ما هنوز هیچ خروجی تولید نکرده است، پنجره کنسول (console) باید خالی باشد. دوم اینکه، باید نوعی مارکر (marker) در کنار براکت باز شده در تابع main مشاهده نمایید. در ویژوال استودیو این مارکر (marker) یک فلش زرد رنگ می باشد.

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
این فلش زرد رنگ نشان می دهد که خطی که با این فلش متمایز شده است، خط بعدی است که اجرا خواهد شد. اگر مجدداً دستور Step into را اجرا کنید، آن خط کد اجرا می شود و به خط بعدی می رود.

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
به همین ترتیب می توانید با تکرار دستور Step into برنامه را خط به خط اجرا کنید.

هنگامی که به بیانی زیر می رسید :

std::cout << nValue;

به جای دستور Step into ، دستور Step over را اجرا کنید تا وارد جزئیات آن نشود. اگر می خواهید عملیات دیباگ کردن را متوقف کنید می توانید با استفاده از دستور Stop Debugging این کار را انجام بدهید.

Step over


دستور Step over همانند دستور Step into یک خط از کد را اجرا می کند. تفاوت این دو دستور در این است که در دستور Step over هنگامی که اجرای کد به یک فراخوانی تابع برسد، وارد آن تابع نخواهد شد و آن تابع کلاً اجرا می گردد و کنترل اجرای برنامه به خط بعدی در تابع جاری می رسد.

بیایید یک مثال را با هم مرور کنیم :

#include "iostream"

void printValue(int nValue)
{
std::cout << nValue;
}

int main()
{
printValue(5);
return 0;
}

این برنامه را با دستور Step into دیباگ کنید تا به بیانیه فراخوانی تابع printValue برسید.

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
حالا با دستور Step over کار را ادامه بدهید. دیباگر تابع printValue را به صورت کامل اجرا می کند و کنترل اجرای برنامه را به بیانیه بعدی (return 0) باز می گرداند.

در واقع دستور Step over یک راه ساده تر برای شما فراهم می کند تا در صورتی که از کارکرد توابع اطمینان داشتید و یا قصد دیباگ کردن آنها را نداشتید، زمان دیباگ شما کاهش یابد.

Step out


برخلاف دو دستور قبلی، دستور Step out تنها خط بعدی را اجرا نمی کند. این دستور کلیه کدهای موجود در تابع جاری را اجرا می کند و کنترل اجرای برنامه را به بعد از خاتمه تابع جاری می برد.

بیایید با یک مثال کارکرد دستور Step out را مشاهده کنیم :

#include "iostream"

void printValue(int nValue)
{
std::cout << nValue;
}

int main()
{
printValue(5);
return 0;
}

برنامه را با Step into آغاز کنید تا جایی که به ابتدای تابع printValue برسید.

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
حالا دستور Step out را اجرا کنید. اجرای این دستور باعث می شود تا تابع printValue یکجا اجرا شود و کنترل اجرای برنامه به خط کد بعدی در تابع main باز گردد.

Run to cursor


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

خوشبختانه ابزارهای دیباگر پیشرفته امکانات خوبی را در این زمینه برای ما فراهم آورده اند.

اولین دستور مفید در این ارتباط، دستور Run to cursor می باشد. در این روش شما ابتدا با ماوس یک خط از کد را انتخاب می کنید و سپس دستور Run to cursor را اجرا می کند، برنامه شبیه حالت عادی اجرا می گردد و کلیه کدها تا زمانی که به خط کد جاری برسید اجرا می شوند و سپس در خط مربوطه برنامه متوقف شده و به حالت دیباگ می رود.

تصویر زیر چگونگی این کار را به شما نشان می دهد. با ماوس روی خط کد مورد نظرتان راست کلیک کنید و دستور Run to cursor را اجرا کنید.

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
سپس برنامه اجرا می شود تا به خط کد مربوطه برسد و در آنجا متوقف شده و امکانات دیباگ را در اختیار شما می گذارد.

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها

Breakpoints


یک Breakpoint (نقطه توقف) یک نوع مارکر (marker) خاص می باشد که به دیباگر (debugger) می گوید اجرای برنامه را در خط خاصی که این مارکر را دارد، متوقف کند و وارد حالت دیباگ شود.

برای قرار دادن Breakpoint در یک نقطه خاص، وارد منوی Debug شوید و دستور Toggle Breakpoint را اجرا کنید. همینطو می توانید با راست کلیک ماوس بر روی خط مورد نظر و انتخاب Breakpoints و سپس Insert Breakpoint در آن نقطه یک توقف ایجاد کنید. آیکان Breakpoints را در تصویر زیر مشاهده می کنید :

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
بعد از قرار دادن Breakpoint در یک نقطه، اگر برنامه را اجرا کنید، برنامه به صورت کامل اجرا می شود تا به آن نقطه برسد و سپس در آن نقطه متوقف می شود و به حالت دیباگ می رود.

آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها
اگر قصد آزمایش یک بخش خاص از کد را داشته باشید Breakpoint ها بسیار مفید می باشند.


آموزش قبلی : آموزش زبان ++C : چگونه اولین برنامه های خود را طراحی کنید؟

آموزش بعدی : آموزش زبان ++C : اشکال زدایی (Debugging) برنامه ها قسمت دوم



نمایش دیدگاه ها (1 دیدگاه)

دیدگاه خود را ثبت کنید:

انتخاب تصویر ویرایش حذف
توجه! حداکثر حجم مجاز برای تصویر 500 کیلوبایت می باشد.