بسماللهالرحمنالرحیم
اگر تاکنون این سری از آموزشها را دنبال کرده باشید به مباحثی از زبان برنامهنویسی C از جمله متغیرها، کلاسهای حافظه، عملگرها، رشتهها، آرایهها و کاراکترها پرداختیم که توصیه میکنم حتما مقالاتی که نوشته شدهاند حتی در صورتیکه فکر میکنید به این مباحث مسلط هستید مجددا بخوانید (یاداوریش که ضرر نداره). حال در این مقاله میخواهیم به مبحث بسیار کاربردی و همینطور خطرناک حلقهها بپردازیم. حالا چرا خطرناک؟ برای جواب به این سوال توصیه میکنم که این مقاله را تا انتها بخوانید.حلقه در زبان برنامهنویسی C چیست؟
اجازه دهید این قسمت را با ذکر یک مثال توضیح دهیم. فرض کنید به شما گفته شده است برنامه ای بنویسید که یک عبارت دلخواه را 1000 بار در خروجی چاپ کند. یکی از روش ها این است که عبارت موردنظر را هزار بار در برنامه بنویسیم که این عمل، بسیار حجم کد را بالا میبرد و همچنین خوانایی برنامه را به شدت پایین میآورد. روش دیگر استفاده از دستور goto است ( دقت کنید قبل از اینکه از حلقهها در برنامه استفاده کنند از goto استفاده میشده و البته همچنان هم میتوان از این دستور استفاده کرد البته به شرط استفاده رو به جلو یعنی اینکه رو به عقب پرش انجام ندهیم که شرایط خاص خودش رو داره که اینجا بحث ما نیست.) به این ترتیب که در برنامه بگوییم از خط 1 به خط 20 پرش را انجام دهد و مجددا همین روند صورت گیرد و یا به خط دیگری از برنامه رود که این روش هم اصلا استاندارد نیست و ترجیحا در برنامههایتان در صورتیکه پرش به خطوط قبل میخواهید انجام دهید اصلا و ابدا این کار را انجام ندهید و فقط طبق استاندارد MISRA-C پرش رو به جلو توسط این دستور اشکالی ندارد.( میدونم بخشیش جدید بود و براتون خیلی سوال پیش اومده ولی اینجا اگه بخوام توضیح بدم کلا رشته کلام از دستم میره فقط به عنوان راهنمایی اگر خیلی کنجکاوید مبحث MISRA-C رو سرچ کنید.) خب طبق توضیحاتی که دادیم از این دستور هم نمیتوان به صورت کلی استفاده کرد و در اینجا بود که مفهوم حلقه ایجاد شد. و همچنان هم از این مفهوم به روش های متفاوت در برنامهنویسی استفاده میکنند.در واقع اگر بخواهیم حلقه را تعریف کنیم. باید بگوییم یک گوی را که دارای یک نقطه شروع و پایان است، را در نظر بگیرد وسپس گوی را بچرخانید (با فرض چرخش محدود گوی). چیزی که مشاهد میکنید این است که این توپ دائما از نقطه شروع به نقطه پایان میرود و این کار به صورت مداوم در حال انجام است تا زمانیکه چرخش به اتمام رسد. در حلقهها هم دقیقا همین امر صدق میکند دقیقا از یک نقطه شروع به کار میکنند تا زمانیکه به نقطه پایان برسند و مجددا این روند تکرار میشود. حال اگر تعداد تکرار در حلقه مشخص شده باشد که چه مقدار باید از نقطه شروع تا پایان، این تکرار صورت گیرد را، حلقههای محدود یا Finite میگوییم و اگر این تعداد تکرار در حلقه نامشخص باشد به این معنی که تعداد تکرار شروع و پایان محدود نباشد را حلقه Infinite یا بینهایت (نامحدود) میگوییم (امیدوارم که تونسته باشیم این مفهوم رو به درستی انتقال داده باشیم). خب پس از اینکه با مفهوم حلقه و دلیل استفاده از آن در برنامه را متوجه شدیم نوبت به معرفی حلقهها رسیده است.
انواع حلقهها در زبان برنامهنویسی C
- حلقه While
- حلقه For
- حلقه do_while
- حلقههای تودرتو (nested loops)
حلقه While
از این حلقه زمانی استفاده میکنیم که بخواهیم درستی یا نادرستی یک شرط را برسی کنیم که این عمل هم به صورت مقایسهای با استفاده از عملگرها صورت میگیرد. برای درک بهتر موضوع به فلوچارت دقت کنید. فلوچارت حلقه While1 2 3 | while(شرط) { دستورات; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <stdio.h> int main () { /* local variable definition */ int a = 10; /* while loop execution */ while( a < 20 ) { printf("value of a: %d\n", a); a++; } return 0; } |
1 2 3 4 5 6 7 8 9 10 | value of a: 10 value of a: 11 value of a: 12 value of a: 13 value of a: 14 value of a: 15 value of a: 16 value of a: 17 value of a: 18 value of a: 19 |
1 2 3 | while(1){ مجموعه دستوراتی که باید بی نهایت تکرار شوند } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <stdio.h> int main () { /* local variable definition */ int a = 10; /* while loop execution */ while( 1 ) { printf("value of a: %d\n", a); a++; } return 0; } |
حلقه For
از این حلقه زمانی استفاده میکنیم که بخواهیم تعداد گام های شمارش را در حلقه مشخص کنیم و همچنین میتوانیم یک شرط را به همراه مقدار دهی اولیه (مثل حلقه while ) تعیین کنیم. برای درک بهتر به فلوچارت دقت کنید. فلوچارت حلقه For1 2 3 | for ( مقدار دهی اولیه; شرط; گام شمارش میتواند افزایشی یا کاهشی باشد) { دستورات; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <stdio.h> int main () { int a; /* for loop execution */ for( a = 10; a < 20; a = a + 1 ){ printf("value of a: %d\n", a); } return 0; } |
1 2 3 4 5 6 7 8 9 10 | value of a: 10 value of a: 11 value of a: 12 value of a: 13 value of a: 14 value of a: 15 value of a: 16 value of a: 17 value of a: 18 value of a: 19 |
1 2 3 | for(;;){ مجموعه کدی که باید بی نهایت تکرار شوند } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <stdio.h> int main () { int a; /* for loop execution */ for( ; ; ){ printf(" He returns Back to our Life as soon as posssible "); } return 0; } |
نکته طلایی : برای زبان برنامهنویسی C چندین استاندارد وجود دارد که آخرین استاندارد زبان برنامهنویسی C معروف به استاندارد C11 است که در این نوع استاندارد اگر حلقه for در برنامه استفاده میکنید باید به این نکته توجه کنید که نوع متغیر را باید خارج از حلقه for تعیین کنید. در غیر اینصورت، خطایی با مضمون اینکه این ساختار در استاندارد C11 وجود ندارد روبه رو خواهید شد. نکته جالب اینجا است که میکروبلیز هم از این استاندارد پیروی میکند (راهحل های دیگه از جمله تنظیمات خود کامپایلر وجود داره که مسله رو حل کرد که در اینجا اصلا مورد بحث ما نیست به دلیل اینکه هر کدوم از کامپایلرها روند خاص خودشون رو دارند پس بهترین روش اینکه اصولی بریم جلو). برای درک موضوع توصیه میکنیم که مجددا به مثالی که زده شد این بار با دقت بیشتری توجه کنید.
حلقه do_while
این حلقه هم دقیقا عملکردی مشابه حلقه while است با این تفاوت که ابتدا دستورات درون بدنه آن چک میشوند و در نهایت درستی یا نادرسی حلقه را برسی میکند. به عبارت دیگر بزرگترین تفاوت این حلقه با سایر حلقه ها این است که اگر بخواهیم یک دستور در برنامه به هر دلیلی ابتدا یکبار اجرا شود و در نهایت اگر شرط درون حلقه درست بود مجددا این عمل صورت گیرد و در صورتیکه نبود از آن خارج شود یکی از بهترین دستورها که دقیقا این عمل را انجام میدهد دستور do_while میباشد. برای درک بهتر به فلوچارت زیر دقت کنید. فلوچارت حلقه do_while1 2 3 | do { دستورات; } while( شرط ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <stdio.h> int main () { /* local variable definition */ int a = 10; /* do loop execution */ do { printf("value of a: %d\n", a); a = a + 1; }while( a < 20 ); return 0; } |
1 2 3 4 5 6 7 8 9 10 | value of a: 10 value of a: 11 value of a: 12 value of a: 13 value of a: 14 value of a: 15 value of a: 16 value of a: 17 value of a: 18 value of a: 19 |
حلقههای تودرتو (Nested Loops)
این نوع از حلقهها دقیقا از ترکیب چندین حلقه با یک دیگر به وجود میآیند که هر کدام در جای خودشان بسیار میتوانند کاربردی باشند. به طور مثال بدست آوردن سطر و ستون یک ماتریس و یا بدست آوردن پیکسلهای تصویر یا کشیدن پیکسلها روی یک LCD گرافیکی و … که در واقع تمام مثالهایی که زده شد در واقع از ساختار ماتریسی گرفته شدهاند.(لطفا مباحث ماتریس بلاخص بدست آوردن سطر، ستون، مقادیر ویژه و … رو حتما جدی بگیرید). برای درک بهتر اجازه دهید که ابتدا ساختار هر یک از دستورات را بررسی کنیم. حلقه تودرتو while1 2 3 4 5 6 7 | while(condition) { while(condition) { statement(s); } statement(s); } |
1 2 3 4 5 6 7 | for ( init; condition; increment ) { for ( init; condition; increment ) { statement(s); } statement(s); } |
1 2 3 4 5 6 7 8 | do { statement(s); do { statement(s); }while( condition ); }while( condition ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h> int main () { /* local variable definition */ int i, j; for(i = 2; i<100; i++) { for(j = 2; j <= (i/j); j++) if(!(i%j)) break; // if factor found, not prime if(j > (i/j)) printf("%d is prime\n", i); } return 0; } |
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 | 2 is prime 3 is prime 5 is prime 7 is prime 11 is prime 13 is prime 17 is prime 19 is prime 23 is prime 29 is prime 31 is prime 37 is prime 41 is prime 43 is prime 47 is prime 53 is prime 59 is prime 61 is prime 67 is prime 71 is prime 73 is prime 79 is prime 83 is prime 89 is prime 97 is prime |
حالتهای کنترل حلقه
اگر به مفهوم حلقه که در چند بخش قبل به توضیح آن پرداختیم مراجعه کنید خواهید دید که حلقه را به یک گوی که دارای یک نقطه شروع و پایان است تشبیه کردیم و گفتیم که این گوی بسته به اینکه چه مقداری بچرخد میتواند به همان مقدار از نقطه شروع به نقطه پایان برسد و این روند را تکرار کند. حالا همین گوی را مجددا در نظر بگیرید البته این بار، با این تفاوت که میخواهیم مسیری که گوی از نقطه شروع تا نقطه پایان سپری میکند را سریعتر سپری کنیم و یا اینکه جهش هایی را در حین حرکت گوی انجام دهیم. که بعضی از این جهشها سبب شروع مجدد و یا توقف حرکت و شروع مجدد و … میشوند. که میخواهیم به توضیح تک تک این عوامل در زبان برنامهنویسی C بپردازیم.دستور continue
این دستور برخلاف اسمی که دارد در واقع کاری که درحلقه انجام میدهد این است که از روند تکرار خطی و یا عبارتی در حلقه جلوگیری میکند به عبارت دیگر اگر در برنامه خطی یا عبارتی را خواستیم متوقف کنیم کافی است که از دستور countinue استفاده کنیم. برای درک بهتر موضوع به فلوچارتی که در ادامه قرار میدهیم دقت کنید. فلوچارت continue1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <stdio.h> int main () { /* local variable definition */ int a = 10; /* do loop execution */ do { if( a == 15) { /* skip the iteration */ a = a + 1; continue; } printf("value of a: %d\n", a); a++; } while( a < 20 ); return 0; } |
1 2 3 4 5 6 7 8 9 | value of a: 10 value of a: 11 value of a: 12 value of a: 13 value of a: 14 value of a: 16 value of a: 17 value of a: 18 value of a: 19 |
دستور break
این دستور دقیقا مطابق اسمی که دارد عمل میکند یا به عبارتی اگر هر زمانی خواستیم که عملکرد بخشی از برنامه را طبق شرایطی متوقف کنیم از این دستور استفاده میکنیم. برای درک بهتر موضوع به فلوچارت دقت کنید. فلوچارت break1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h> int main () { /* local variable definition */ int a = 10; /* while loop execution */ while( a < 20 ) { printf("value of a: %d\n", a); a++; if( a > 15) { /* terminate the loop using break statement */ break; } } return 0; } |
1 2 3 4 5 6 | value of a: 10 value of a: 11 value of a: 12 value of a: 13 value of a: 14 value of a: 15 |
دستور goto
این دستور در بین برنامهنویسان به دستور پرش معروف است که البته با اسم آن هم بیربط نیست. دلیل اینکه به این دستور، دستور پرش کردن میگویند این است که شما را قادر میسازد از هر خطی از برنامه به خط دیگر بروید (پرش کنید). نکته طلایی: این دستور در صورتیکه ندانید چطور از آن استفاده کنید به حدی خطرناک است که عملا میتواند پروژه شما را با خاک یکسان کند.برای استفاده از این دستور باید یک شرط اساسی را به خاطر داشته باشید ” حتما و حتما و باید از این دستور به صورت پرش رو به جلو استفاده کنید به هیچ وجه از پرش رو به عقب استفاده نکنید.” یعنی اینکه اگر در برنامه خواستید از این دستور استفاده کنید سعی کنید از خط 2 به خط 5 بروید نه اینکه از خط 5 به خط 2 بروید. در صورتیکه بیشتر علاقمند به درک این موضوع هستید توصیه میکنیم حتما استاندارد MISRA_C را مطالعه کنید. (من خودم شخصا از این دستور توی برنامههام استفاده نمیکنم یکی از بزرگترین دلیلهاشم خالی شدن Pipline نه اون چیزی که اکثریت تصور میکنن، ولی گفتم که اگر در صورتی هم که خواستید استفاده کنید با شرطی که گفته شد مشکلی نداره).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <stdio.h> int main () { /* local variable definition */ int a = 10; /* do loop execution */ LOOP:do { if( a == 15) { /* skip the iteration */ a = a + 1; goto LOOP; } printf("value of a: %d\n", a); a++; }while( a < 20 ); return 0; } |
1 2 3 4 5 6 7 8 9 | value of a: 10 value of a: 11 value of a: 12 value of a: 13 value of a: 14 value of a: 16 value of a: 17 value of a: 18 value of a: 19 |
حلقه یا شمشیر دو لبه
حلقه را میتوان به یک شمشیر دو لبه تشبیه کرد. سوالی که هست چرا شمشیر دولبه؟ فرض کنید یک شمشیر دو لبه را در دست خود گرفتهاید در صورتیکه راه و روش استفاده از این شمشیر را ندانید اتفاقی که خواهد افتاد این است که اول از همه به خودتان آسیب میزنید. و در صورتیکه راه و روش استفاده از آن را بلد باشید. بدون اینکه به خودتان ضربه بزنید به راحتی میتوانید از خودتان دفاع کنید(خب چه ربطی به حلقه داشت). حال در زبان برنامه نویسی C اصلی ترین عضو برنامه شما، حلقه شما است، چه میخواهد از سیستمعامل استفاده شود و یا امثال آن و الی … بحثی که بسیار اهمیت دارد این است که چطور از این حلقهها در برنامهها استفاده کنیم. یکی از راهکارهایی که برای مدیرت تمامی حلقهها در برنامه میتوانیم داشته باشیم، بحثی به نام مدیرت زمان در حلقهها است. اینکار به روش های مختلف مانند استفاده از پریفرالهای جانبی( تایمر و سگ نگهبان(watch dog) و … ) و RTOS (اولویت بندی کردن دستورات) و … صورت میگیرد. اجازه دهید برای درک بهتر موضوع یک مثال بزنیم. فرض کنید برنامهای را درون میکروبلیز نوشتهاید که باید زمانیکه حتما دیتا را دریافت کرد آنگاه ادامه دستورات را اجرا کند؟ آیا این طرز برنامهنویسی درست است؟ پاسخ بزرگترین مشکلی که این برنامه دارد این است که هر وقتی که دیتا آمد برنامه ادامه کار خود را انجام میدهد(یعنی فاجعه). خب فرض را بر این بگیریم که دیتایی را اصلا دریافت نکردیم. آیا عاقلانه است که بقیه دستورات را هم به خاطر آن از کار بندازیم. مسلما کاری عاقلانه نیست. راهحل چیست؟ راهحل استفاده از یک زمانبندی است که به طور مثال بگوییم اگر تا یک ثاینه هیچ دیتایی دریافت نشد ادامه دستورات را اجرا کن (البته روشهای دیگری هم وجود دارند که بتوان حلقه ها را کنترل کرد و هدف از توضیح این بخش، این بود که شما مخاطب عزیز بدونید که با چه دستور مهمی در برنامه هاتون مواجه هستید).
سلام این آخرین فصل آموزش زبان c هستش؟
سلام دوست عزیز
ظاهرا دوست عزیزی که مطلب رو مینوشتن – ادامه ندادنش
سلام . بسیااار ممنونم که این بحث عالی رو استارت زدین .
من که تقریبا ۱ سال و نیمه ساله دارم سایت خوب سیسوگ رو دنبال میکنم جزو دسته بندی ها این مقاله رو ندیدم . نمیدونم چرااا. با سرچ در گوگل فهمیدم سایت سیسوگ این مقاله رو هم داره .
ازتون خواهش دارم این مقاله رو پیش ببرین . واقعا عالیه . خیلی ممنون
سلام این مقاله البته خیلی قدیمی هست و متاسفانه ادامه داده نشده مقاله , البته در دسته بندی ها هم موجود هست
ما سعی خودمون را برای ادامه این مقاله خواهیم کرد
خیلی لطف میکنین.
من بیصبرانه منتظر ادامه این آموزش میمونم
تشکر
سلام . ممنون از سایت خوبتون اما چرا دقیقا به جای اصلی که رسد دیگه ادامه شو نمیذارید ؟
سلام دوست عزیز
مطالب این سایت توسط دوستان خودتون نوشته میشند که البته لطف میکنند و البته گاهی وقتها هم ادامه نمیدند
و این انتقاد بر ما روا هست که برخی مطالب بدون سرانجام هستند
سعیمون این هست برخی از این مطالب که درخواست بیشتری داشته باشند را یه جورایی ادامه بدیم و امیدوارم این مطلب را هم بتونیم ادامه بدیم