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


پیش از این که چیزی را بخواهیم شروع کنیم، لطفا هیچ پیش فرضی در مورد کامپایلر نداشته باشید. کامپایلرها از ویژگی های مختلفی پشتیبانی می کنند که ممکن است در همه آنها ثابت نباشد. مثلا این تکه کد را نگاه کنید:

array[i] = i++;


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

۱– عبارت اگر برای مقایسه ثابت ها وقتی می خواهید مقدار ثابتی را با یک متغیر مقایسه کنید، به دو روش برمی خورید. نخستین حالت آن در این شرط مقایسه ای، چیزی شبیه به کد زیر خواهد بود:

if (input_temp == const_val ) { //....some code goes here }



و حالت دوم به صورت زیر:

if (const_val == input_temp ) { ///....some code goes here }




حالا به نظرتان کدام یک بهتر است؟ روش دوم کمی عجیب به نظر می رسد، اما بهتر است از آن استفاده شود! چرا؟ این حالت را در نظر بگیرید که به طور اشتباهی یک مساوی فراموش شود:

if (input_temp = const_val)



حالا می توانید اشتباه کد بالا را درک کنید. در این حالت، همیشه جواب شرط، مقدار درست خواهد بود. این اشتباه وقتی که ثابت را اول بنویسیم برطرف خواهد شد، چرا که کامپایلر خطا می دهد.

۲– تقدم عملگرها اگر بیشتر از یک عملیات وجود داشته باشد، حتما از پرانتز استفاده کنید. این کد را ببینید:



;۲=۱var ;۱۰=۲var ;۴=۳var ;۳*var۲+var۱temp = var



اگر منتظرید که عبارت بالا نتیجه ۴۸ را بدهد به حقیقت فکر کنید که نتیجه ۴۲ را خواهید گرفت. اشتباه از کجاست؟ اگر ندانید که عملگر ضرب نسبت به عملگر جمع اولویت بیشتری دارد، در این صورت ایرادی منطقی خواهید داشت که به سادگی قابل رفع نخواهد بود. برای سادگی کار می توان کد در دو خط را نوشت. البته پرانتزگذاری بهترین راه حل است.

۳– بررسی کد بازگشتی توابع استاندارد صرف نظر کردن از کدهایی که توابع استاندارد کتابخانه ای پس می فرستند، تبدیل به امری عادی شده است. اما حواستان باشد که اگر بخواهید فرض بگیرید که این توابع همواره مقدار درست را بر می گردانند، از همین جا ضربه خواهید خورد. به عنوان مثال، تابع malloc برای تخصیص پویای حافظه استفاده می شود که شکل کلی آن به این صورت است:



void *malloc(size_t size); malloc



اشاره گری را به حافظه تازه تخصیص داده شده ای برمی گرداند که اندازه آن را با مقدار size به آن داده ایم. تکه کد زیر را در نظر بگیرید:



* sizeof (int)); ۱۰۰int *stk_ptr = malloc( if (null == stk_ptr) { // memory could not be allocated // take corrective action }


اگر malloc به هر دلیلی نتوانست حافظه مناسب را تخصیص بدهد، در این صورت اگر بررسی ای صورت نگرفته باشد، برنامه تان به سادگی از کار می افتد و شما به هیچ عنوان نخواهید توانست آن را رهگیری کنید.

۴– غلبه بر محدودیت آرایه وقتی یک آرایه ایجاد می کنید، شاید بزرگترین مشکل مان با آن، محدودیت اش باشد. کد زیر را در نظر بگیرید:

];۱۰unsinged char input_buffer[ unsigned char i; ; i++) {۱۰; i <=۰ for (i = input_buffer[i] = in_port(); }



خب در کد بالا انتظار می رود که ۱۰بایت داده از ورودی دریافت گردد و داخل بافری که تعریف کرده ایم، قرار گیرد. اما به خاطر این که داخل حلقه for افتاده ایم، و همچنین، خود اندیس ۱۰ را هم بررسی می کنیم، بنابراین ۱۱بایت از ورودی دریافت می شود و داخل آرایه ۱۰ عنصری قرار نمی گیرد. از این دست اشکالات باعث می شود وقت زیادی را صرف رفع آن کنید. چندین ابزار هست که می توانید به کمک آنها این ایرادها را کشف و تصحیح کنید، pc-lint و valgrind از این دست نرم افزارها است.

۵– داده از نوع منطقی به طور معمول، نوع داده منطقی (بولین) شامل دو ارزش می شود: درست یا نادرست، انتخاب یا خالی، بالا یا پایین، فعال یا غیرفعال، روشن یا خاموش و مانند آن. بیایید فرض بگیریم که می خواهید ثابت هایی تعریف کنید که نشان دهنده فعال و غیر فعال باشند. دو روش برای این کار وجود دارد:


۰ #define disable #define enable (!disable)



که در این دو دستور، فعال مخالف غیرفعال است و عدد ۱ در آن قرار می گیرد. و روش دوم:


۰ #define disable ۱ #define enable



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

۶– همیشه از آکولاد استفاده کنید برخی از برنامه نویسان برای دستورات if و حلقه های for و while ، در زمانی که تنها یک دستور دارند از آکولاد استفاده نمی کنند، مثلا عبارت زیر را ببینید:


if (set == timer_is_flag) timer_is_flag = clear;


یا دستور زیر:



; i < buff_size; i++)۰ for (i = buff[i]=i;



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