در تپش این هفته، ماجرای فریب و تعرض در پوشش عرفانهای دروغین و رمالی را بررسی کردیم
پیش از اینکه چیزی را بخواهیم شروع کنیم، لطفا هیچ پیشفرضی در مورد کامپایلر نداشته باشید. کامپایلرها از ویژگیهای مختلفی پشتیبانی میکنند که ممکن است در همه آنها ثابت نباشد. مثلا این تکه کد را نگاه کنید:
Array[i] = i++;
نتیجه عبارت بالا چه خواهد شد؟ آیا اول عملیات جایگزینی انجام میشود و بعد یک واحد به i اضافه میشود یا برعکس این موضوع صادق خواهد بود؟ ایـن مسـالـه تـا حـدودی گنـگ اسـت، امـا یـک برنامهنویس خوب همواره از نوشتن کدهایی که ابهام دارد، خودداری میکند. هیچ وقت فراموش نکنید که نوشتن یک کد تمیز، خوانا و قابل ردیابی، بسیار بهتر از یک کد کوتاه، اما غیرقابل ردیابی است که نیاز به هوش بالایی دارد.
1– عبارت اگر برای مقایسه ثابتها
وقـتی میخواهید مقدار ثابتی را با یک متغیر مـقایسه کنید، به دو روش برمیخورید. نخستین حالت آن در این شرط مقایسهای، چیزی شبیه به کد زیر خواهد بود:
if (input_temp == CONST_VAL ) {
//....some code goes here
}
و حالت دوم بهصورت زیر:
if (CONST_VAL == input_temp ) {
///....some code goes here
}
حالا بهنظرتان کدام یک بهتر است؟ روش دوم کمی عجیب بهنظر میرسد، اما بهتر است از آن اسـتـفاده شود! چرا؟ این حالت را در نظر بگیرید که بهطور اشتباهی یک مساوی فراموش شود:
if (input_temp = CONST_VAL) {
حالا میتوانید اشتباه کد بالا را درک کنید. در ایـن حـالـت،همیشه جواب شرط، مقدار درست خـواهـد بـود. این اشتباه وقتی که ثابت را اول بنویسیم برطرف خواهد شد، چرا که کامپایلر خطا میدهد.
2– تقدم عملگرها
اگر بیشتر از یک عملیات وجود داشته باشد، حتما از پرانتز استفاده کنید. این کد را ببینید:
;2=1var
;10=2var
;4=3var
;3*var2+var1temp = var
اگر منتظرید که عبارت بالا نتیجه 48 را بدهد به حقیقت فکر کنید که نتیجه 42 را خواهید گرفت. اشتباه از کجاست؟
اگر ندانید که عملگر ضرب نسبت به عملگر جمع اولویت بیشتری دارد،در این صورت ایرادی منطقی خواهید داشت که بهسادگی قابل رفع نخواهد بود. برای سادگی کار میتوان کد در دو خط را نوشت. البته پرانتزگذاری بهترین راه حل است.
3– بررسی کد بازگشتی توابع استاندارد
صرف نظر کردن از کدهایی که توابع استاندارد کتابخانهای پس میفرستند، تبدیل به امری عادی شده است.
اما حواستان باشد که اگر بخواهید فرض بگیـریـد کـه ایـن تـوابـع همواره مقدار درست را بـرمـیگردانند، از همینجا ضربه خواهید خورد. بهعنوان مثال، تابع malloc برای تخصیص پویای حافظه استفاده میشود که شکل کلی آن به این صورت است:
void *malloc(size_t size);
malloc اشارهگری را به حافظهتازه تخصیص داده شدهای برمیگرداند که اندازه آن را با مقدار size به آن دادهایم. تکه کد زیر را در نظر بگیرید:
* sizeof (int)); 100int *stk_ptr = malloc(
if (NULL == stk_ptr) {
// Memory could not be allocated
// Take corrective action
}
اگـر malloc بـه هـر دلیلی نتوانست حافظه مـنـاسـب را تخصیص بدهد، در این صورت اگر بررسیای صورت نگرفته باشد، برنامهتان بهسادگی از کار میافتد و شما بههیچعنوان نخواهید توانست آن را رهگیری کنید.
4– غلبه بر محدودیت آرایه
وقتی یک آرایه ایجاد میکنید، شاید بزرگترین مشکلمان با آن، محدودیتاش باشد. کد زیر را در نظر بگیرید:
];10unsinged char input_buffer[
unsigned char i;
; i++) {10; i <=0 for (i =
input_buffer[i] = in_port();
}
خب در کد بالا انتظار میرود که 10بایت داده از ورودی دریـافـت گردد و داخل بافری که تعریف کردهایم، قرار گیرد. اما بهخاطر اینکه داخل حلقه for افتادهایم، و همچنین، خود اندیس 10 را هم بررسی میکنیم، بنابراین 11بایت از ورودی دریافت میشود و داخل آرایه 10 عنصری قرار نمیگیرد. از این دست اشکالات باعث میشود وقت زیادی را صرف رفع آن کنید.
چندین ابزار هست که میتوانید به کمک آنها این ایـرادهـا را کشـف و تصحیح کنید، PC-Lint و Valgrind از این دست نرمافزارها است.
5– داده از نوع منطقی
بهطور معمول، نوع داده منطقی (بولین) شامل دو ارزش میشود: درست یا نادرست، انتخاب یا خـالی، بالا یا پایین، فعال یا غیرفعال، روشن یا خاموش و مانند آن.
بیایید فرض بگیریم که میخواهید ثابتهایی تعریف کنید که نشان دهنده فعال و غیر فعال باشند. دو روش برای این کار وجود دارد:
0 #define DISABLE
#define ENABLE (!DISABLE)
که در این دو دستور، فعال مخالف غیرفعال است و عدد 1 در آن قرار میگیرد. و روش دوم:
0 #define DISABLE
1 #define ENABLE
که بهطور دستی مقدار 0 و 1 را به غیرفعال و فعال بدهیم. در این صورت، اگر بخواهیم حالتها را تغییر بدهیم، در روش اول، کافیست غیرفعال را از 0 به 1 تغییر دهیم، تمام کدمان تغییر میکند و بهشیوه جدید فعال میشود. اما اگر بخواهیم بهروش دوم این کار را انجام بدهیم، باید هر دو دستور را تغییر داده و این یعنی بالارفتن احتمال اشتباه.
6– همیشه از آکولاد استفاده کنید
بـرخـی از بـرنامهنویسان برای دستورات if و حلقههای for و while، در زمانی که تنها یک دستور دارند از آکولاد استفاده نمیکنند، مثلا عبارت زیر را ببینید:
if (SET == timer_is_flag)
timer_is_flag = CLEAR;
یا دستور زیر:
; i < BUFF_SIZE; i++)0 for (i =
buff[i]=i;
خب این دستورات بهخودی خود هیچ اشکالی ندارند، اما اگر به دستورات بالایی، یک دستور اضافه کنید و حواستان نباشد که آکولاد بگذارید، با مشکل بزرگی روبرو میشوید، برنامهتان مطابق انتظار رفتار نمیکند و وقت قابل توجهی را باید صرف کنید تا اینرا پیدا کنید.
از اینرو، پیشنهاد میشود که با دقت پس از هر دستور شرطی و حلقه، آکولاد بگذارید و سپس اقدام بهکدنویسی داخل محتوای آن بکنید. در این صورت، پس از چندین روز تمرین، دست شما بهطور خودکار آکولادگذاری را انجام میدهد و از این خطای نابهنگام رهایی پیدا میکنید.
منابع
در تپش این هفته، ماجرای فریب و تعرض در پوشش عرفانهای دروغین و رمالی را بررسی کردیم
گزارش «جامجم» درباره دستاوردهای زبان فارسی در گفتوگو با برخی از چهرههای ادب معاصر
معاون وزیر بهداشت: