ساختارهای کنترلی
در فصل ششم اشاره کردیم که در برنامه نویسی ساختیافته، هر برنامه از 3 ساختار کنترلی بنام: ساختار ترتیب، ساختار انتخاب و ساختار تکرار تشکیل می گردد. از آنجا كه اين 3 ساختار، نحوه و ترتيب اجراي برنامه را كنترل مي كنند، به آنها ساختارهاي كنترلي گفته مي شود. تا کنون فقط با برنامه هایی سروکار داشته ایم که از ساختار ترتیب استفاده می کرده اند، چرا که دستورهای زبان C در حالت عادی به همان ترتیبی که نوشته شده اند، یکی پس از دیگری اجرا می شوند.
اما زبان C دارای 3 نوع ساختار انتخاب می باشد که عبا رتند از : ساختار if یا ساختار تک انتخابی، ساختار if / else یا ساختار دو انتخابی و ساختار switch یا ساختار چند انتخابی. علاوه براین، این زبان دارای 3 نوع ساختار تکرار بنامهای while، for و do / while نیز می باشد که هریک را بطور کامل شرح خواهیم داد.
قرارداد: توجه کنید که در هنگام تشریح یک دستور، خود دستور با رنگ آبی و عملگرهای آن مانند () با رنگ قرمز نشان داده می شوند. قسمتهایی که در داخل <> قرار می گیرند، عبارت یا دستوری هستند که باید در هنگام استفاده جایگزین گردند.
ساختار انتخاب if
این دستور به شکل زیر استفاده می شود:
if () ;
نحوه کار بدینصورت است که ابتدا عبارت موجود در قسمت ارزیابی می شود. در صورتیکه درست ارزیابی گردد، دستور قسمت اجرا خواهد شد و در صورتیکه نادرست باشد، بدون اینکه دستور قسمت را اجرا کند به دستور بعدی خواهد رفت. این دستور می تواند بصورت زیر نیز استفاده گردد:
if () ;
else ;
در اینصورت ابتدا عبارت موجود در قسمت ارزیابی می شود. در صورتیکه درست ارزیابی گردد، دستور قسمت اجرا خواهد شد، و در صورتیکه نادرست باشد، دستور قسمت اجرا خواهد شد. در هر حال فقط یکی از این دو قسمت اجرا خواهد گردید.
دقت کنید که پرانتز استفاده شده پس از دستور if برخلاف برخی زبانهای دیگر، اجباری است. علاوه براین، در این دستور نیازی به استفاده از then نمی باشد.
بعنوان مثال چنانچه متغیر grade حاوی نمره دانشجو باشد و بخواهیم بر مبنای نمره وی، پیغام مناسبی چاپ کنیم، می توانیم از دستور زیر استفاده کنیم:
if (grade> = 10) printf(“Passed !”);
else printf(“Failed!”);
در حالت عادی دستور if منتظر یک دستور در بدنه خود می باشد، اما چنانچه می خواهید چندین دستور را در بدنه یک دستور if دهید، باید آنها را در داخل آکولاد باز وبسته { } قرار دهید. این مجموع دستورات را یک دستور مرکب می گویند. بطور کلی در زبان C هرجا که می توان یک دستور قرار داد، می توان از یک دستور مرکب نیز استفاده کرد. به یک دستور مرکب، بلوک نیز گفته می شود. بنابراین صورت کلی دستور if به شکل زیر است:
if () {
;
;
….
;
}
else {
;
;
…….
;
}
توجه کنید که وجود قسمت else اختیاری است و در ضمن ممکن است یکی از دو قسمت دارای دستور ساده و دیگری دارای دستور مرکب باشد. بعنوان یک مثال کاملتر به برنامه زیر توجه کنید:
برنامه 1) برنامه ای بنویسید که ضرایب یک معادله درجه 2 را دریافت و ریشه های آن را محاسبه و چاپ نماید.
#include
#include
#include
#include
void main() {
int a, b, c;
float x1, x2, delta;
clrscr();
printf(“Please enter a, b and c : “);
scanf(“%d %d %d”, &a, &b, &c);
if (a==0) {
printf(“wrong equation!”);
exit(1) ;
}
delta = b*b – 4*a*c;
if (delta <0) delta ="="" x1 =" -b" x =" %f”,x1);" delta =" sqrt(delta);" x1 =" (-b+delta)" x2 =" (-b-delta)" x1=" %4.2f" x2=" %4.2f”," x1 =" 2.00" x2 =" 0.33">
چندين نكته درمورد برنامه بالا قابل ذكر است.
1- در اين برنامه از 2 تابع جديد استفاده شده است.اولي تابع sqrt كه يك عدد را به عنوان ورودي دريافت و جذر آن را باز مي گرداند. اين تابع در فايل math.h تعريف شده است. و ديگري تابع exit كه باعث مي شود اجراي برنامه خاتمه يابد. اين تابع نيز در فايل stdlib.h تعريف شده است.
2- به نحوه دندانه گذاري در برنامه دقت كنيد، هرجا كه بلوك جديدي ايجاد شده است، دستورات آن حدود 3 كاراكتر جلوتر نوشته شده اند. اينكار باعث مي شود كه خوانايي برنامه افزايش يابد.
3- توجه كنيد كه همانطور كه قبلا نيز گفته شد، خروجي يك عبارت مقايسه اي، يك عدد است كه 0 نشانه نادرست و هر عدد ديگر نشانه درست است. بنابراين در قسمت شرط يك دستور if مي توان بجاي يك عبارت مقايسه اي، هر عبارت ديگري كه يك مقدار عددي بازگرداند نيز قرار داد! بعنوان مثال مي توان بجاي دستور
if (a==0) …
از دستور زير استفاده كرد:
if (!a) …
دراينصورت چنانچه a برابر صفر باشد، نادرست تلقي خواهد شد و در نتيجه نقيض آن يعني ! ، درست محسوب مي گردد.
يك روش متداول استفاده از دستور if، استفاده از if هاي تودرتو مي باشد كه در مثال بالا نيز ديده مي شود. دراينحالت مجموعه اي از عبارات if-else-if بصورت متداخل قرار داده مي شوند. بعنوان نمونه به مثال زير توجه كنيد:
if (grade> = 18) printf("good!");
else if (grade> = 15) printf("medium!");
else if (grade> = 12) printf("rather weak!");
else if (grade> = 10) printf("weak");
else printf("failed!");
در چنين دستوري، كليه شرطها بترتيب از بالا به پايين بررسي شده و به محض اينكه يكي از آنها درست باشد، دستور مربوط به آن اجرا شده و از بقيه دستورات صرفنظر مي گردد. درصورتيكه هيچ يك از شرطها درست نباشد، دستور مربوط به آخرين else اجرا مي گردد. درچنين حالتي توصيه مي گردد كه شرطهاي نادر را كه امكان وقوع آنها كم است، در انتهاي كار بررسي نماييد، تا تعداد مقايسه كمتري صورت پذيرد.
مشكلي كه در مورد if هاي تودرتو پيش مي آيد، مسئله تعيين if مربوط به هر else است. بعنوان مثال در مورد دستور زير، else به كدام if تعلق دارد؟
if (a ;
else ;
همانطور كه از دندانه گذاري نيز مشخص است، else مربوط به دومين if مي باشد. يعني درصورتي اجرا خواهد شد كه a
if (a < if="" c=""> ; if (c ;
else ; }
else ; else ;
بعنوان يك مثال ديگر، به نمونه زير دقت كنيد:
if () {
if ()
if () ;
else ; // this refer to if ()
}
else ;//this refer to if
برنامه 2) برنامه اي بنويسيد كه 3 عدد را دريافت و حداكثر آنها را چاپ كند.
#include
void main() {
int a, b, c, max;
printf("Please enter 3 numbers :");
scanf("%d %d %d",&a, &b, &c);
if (a> b)
if (a> c) max = a;
else max= c;
else if (b> c) max = b;
else max = c;
printf("Maximum is %d",max);
}
ساختار تكرار while
همانطور كه در بخش الگوريتمها نيز گفته شد، يك ساختار تكرار باعث مي شود تا زمانيكه شرط خاصي برقرار است، عمليات مشخصي تكرار گردد. دستور while نيز باعث ايجاد يك حلقه تكرار به شكل زير مي گردد:
while () ;
اين دستور باعث مي شود تا زمانيكه شرط موجود در قسمت درست است، دستور قسمت تكرار شود، و به محض اينكه شرط نادرست گردد، كنترل اجرا به دستور بعد از حلقه مي رود.
بازهم دستور موجود در قسمت مي تواند يك دستور مركب باشد، دراينصورت دستور بصورت زير درخواهد آمد:
while () {
;
;
…….
;
}
برنامه 3) برنامه اي بنويسيد كه يك عدد را دريافت و فاكتوريال آن را محاسبه و چاپ نمايد.
#include
void main() {
int i,number;
long int factorial;
printf("Please enter number :");
scanf("%d",&number);
factorial = 1;
i = 1;
while (i <= number) { factorial *= i; i ++; } printf("Factorial of %d is %ld"number,factorial); }
برنامه 4) برنامه اي بنويسيد كه يك متن را از كاربر دريافت و آن را با حروف بزرگ چاپ كند.
#include
void main() {
char ch;
ch = getch() ;
while (ch != 13) {
if (ch>= 'a' && ch <= 'z') ch -= 32; putch(ch); ch = getch(); } }
ساختار تكرار for
همانگونه كه در مثال مربوط به حل مسئله فاكتوريال ديده مي شود، گاهي نياز به حلقه تكراري داريم كه به تعداد دفعات مشخصي تكرار گردد. در چنين مواقعي با استفاده از يك متغير شمارنده، تعداد تكرارها را تا رسيدن به مقدار مورد نظر می شماریم و سپس به حلقه پایان می دهیم. به چنین حلقه هایی، تکرار تحت کنترل شمارنده یا تکرار معین می گوییم، چرا که تعداد تکرار ها از قبل مشخص است. چنین حلقه ای دارای 3 جزء اصلی می باشد:
1- مقداردهی اولیه به متغیر شمارنده حلقه
2- شرط پایان حلقه (پایان شمارش) 3- نحوه افزایش متغیر شمارنده
از آنجا که در تمام حلقه هایی که تکرار معین دارند، همین ساختار استفاده می شود؛ در اکثر زبانهای برنامه سازی یک ساختار تکرار ویژه، بنام حلقه for، برای اینکار در نظر گرفته شده است. اما در این بین، حلقه تکرار for در زبان C دارای ویژگیهای خاصی است که آنرا از سایر زبانها متمایز کرده و به آن قدرت بسیار بالایی داده است. شکل کلی این دستور بصورت زیر است:
for ( ; ; ) ;
وظایف عبارات فوق بشرح زیر است :
1- ، مقداردهی اولیه به متغیر حلقه
2- ، شرط اجرای حلقه
3- ،نحوه افزایش متغیر حلقه
البته همانندموارد قبل بازهم قسمت می تواند یک دستور مرکب باشد. نحوه کار حلقه بدینصورت است که در ابتدای شروع حلقه فقط برای یکبار اجرا می شود. سپس عبارت بررسی می گردد و در صورتیکه درست ارزیابی شود (≠ 0)، آنگاه دستور اجرا شده و سپس به ابتدای حلقه باز می گردد. از اجرای دوم به بعد، ابتدا عبارت اجرا می گردد (یعنی متغیر حلقه افزایش می یابد) و سپس عبارت بررسی می گردد و مجددا درصورت درست بودن، حلقه تکرار می شود. اینکار تا زمانیکه مقدار عبارت نادرست ( = 0) ارزیابی شود، تکرار می گردد. به محض اینکه این اتفاق بیفتد، کنترل اجرا به دستور پس از حلقه انتقال می یابد.
درحقیقت هر حلقه for معادل با حلقه while زیر است:
;
while () {
;
;
}
بعنوان یک مثال ساده، تکه برنامه زیر اعداد بین 0 تا 100 را چاپ می نماید:
int count;
for (count = 0; count <= 100; count ++) printf(“%d “,count);
اگر بخواهيم تنها مضارب 5 چاپ شوند، حلقه را به شكل زير تغيير مي دهيم:
for (count = 0; count <= 100; count += 5)
حتي مي توان مضارب 5 را از آخر به اول چاپ كرد:
for (count = 100; count> = 0; count -= 5)
قسمت شرط مي تواند يك شرط مركب نيز باشد، بعنوان مثال:
for (count = 0; count <100 sw="=">
كه در اينصورت در هربار اجراي حلقه، علاوه بر مقدار شمارنده، مقدار متغير sw نيز بررسي مي گردد.
نكته آخر اينكه قسمت مقدار دهي اوليه و افزايش متغير نيز مي توانند شامل چند عبارت باشند كه در اينصورت با كاما از يكديگر جدا مي شوند. بعنوان مثال:
for (a = 0, b = 100; b – a> 50; a++, b--)
در ادامه یک مثال کاملتر برای تشریح این حلقه آمده است:
برنامه 5) برنامه ای بنویسید که تعدادی عدد را از کاربر دریافت و 2 عدد بزرگتر و مجموع آنها را محاسبه و چاپ نماید.
#include
void main() {
int i, n, number;
int sum, max1, max2;
printf(“please enter n : “);
scanf(“%d”,&n);
sum = 0;
max1 = max2 = -1;
for (i=1 ; i max1) {
max2 = max1;
max1 = number;
}
else if (number> max2)
max2 = number;
} //end for
printf(“Sum = %d, Maximum 1=%d, Maximum 2= d”, sum, max1, max2);
}
نكته جالب در مورد حلقه for آنستكه مي توان هريك از 3 عبارت فوق را حذف كرد. به مثالهاي زير توجه كنيد:
for (;i<100;>
مقداردهي اوليه حذف شده است. اين در صورتي است كه بنحوي قبل از شروع حلقه، مقدار متغير i تعيين شده باشد.
for (i= 0;i<100;)>
نحوه افزايش متغير حلقه حذف شده است. اين مورد زماني بكار مي رود كه نحوه افزايش متغير حلقه پيچيده بوده و توسط برنامه نويس در داخل حلقه انجام شود.
for (;i<100;)>
هر دو مورد فوق حذف شده است.
for (i=0; ;i++)
شرط ادامه حلقه حذف شده است. اين مورد زياد متداول نيست و ممكن است باعث شود حلقه براي هميشه اجرا شود. در چنين مواردي حتما بايد در داخل حلقه با استفاده از دستور break (كه در قسمتهاي بعدي توضيح داده خواهد شد)، راهي براي خروج از حلقه قرار داده شود.
حلقه do / while
همانگونه كه در قسمت الگوريتمها توضيح داده شد، در بعضي مواقع لازم است كه شرط ، در انتهاي حلقه بررسي گردد. دستور do / while از نوع حلقه هايي است كه ابتدا دستورات را اجرا كرده و سپس شرط ادامه حلقه را بررسي مي نمايد. شكل كلي اين دستور بصورت زير است:
do
;
while () ;
نحوه كار اين حلقه به اين صورت است كه ابتدا دستور اجرا مي گردد، سپس شرط حلقه بررسي شده و درصورتيكه درست بود، به ابتداي حلقه باز گشته و آن را مجددا اجرا مي كند. البته دستور مي تواند يك دستور مركب باشد.
بعنوان يك مثال كوچك، ممكن است شما از كاربر خواسته ايد كه اعلام كند آيا مايل به ادامه هست يا خير؟ وي بايد پاسخ y يا n بدهد، اما ممكن است يك حرف اشتباه (مانند m) وارد كند. قصد داريم تكه برنامه اي بنويسيم كه عمل دريافت پاسخ را تا زمانيكه يك حرف درست وارد شود، تكرار كند. مسلم است كه بايد ابتدا يك پاسخ وارد شود و سپس درستي آن بررسي گردد.
char answer;
do {
printf("Do you want to continue (y/n) ?");
answer = getch();
} while (answer != 'y' && answer != 'n') ;
بعنوان يك نمونه كاملتر به مثال زير توجه كنيد.
برنامه 6) فرض كنيد نمرات يك گروه از دانشجويان بصورت درجه بندي (A, B, C and D) آماده شده است. برنامه اي بنويسيد كه نمرات دانشجويان را دريافت و در پايان درصد هريك از نمرات را محاسبه و چاپ نمايد. در ضمن از آنجا كه تعداد دانشجويان از قبل مشخص نيست، كاربر در انتهاي نمرات، حرف Q (مخفف Quit) را وارد مي نمايد.
#include
void main() {
int aCount, bCount, cCount, dCount, n;
char grade;
aCount = bCount = cCount = dCount = n = 0;
do {
printf("Enter grade (Q for Quit) : ");
grade = getch() ;
n ++;
if (grade == 'A') aCount ++;
else if (grade == 'B') bCount ++;
else if (grade == 'C') cCount ++;
else if (grade == 'D') dCount ++;
else if (grade == 'Q') n --;
else {
printf("Wrong grade, try again.\n");
n --;
}
} while (grade != 'Q' ) ;
printf("Statistics :\n");
printf("Grade A : %f percent\n", float(aCount)/float(n));
printf("Grade B : %f percent\n", float(bCount)/float(n));
printf("Grade C : %f percent\n", float(cCount)/float(n));
printf("Grade D : %f percent\n", float(dCount)/float(n));
} // end main
ساختار switch / case
اگر مجددا به برنامه 6 دقت كنيد، خواهيد ديد كه در بعضي موارد قصد داريم برحسب مقادير مختلف يك عبارت (در اينجا مقدار متغير grade)، عمليات متفاوتي را انجام دهيم. گرچه اينكار با استفاده از دستورات if / else متداخل قابل انجام است، اما ساختار مناسبتري نيز براي اينكار وجود دارد، كه به آن ساختار چندانتخابي مي گوييم. شكل كلي ان ساختار بصورت زير است:
switch () {
case : ;
;
…
;
case : ;
;
…
;
…
default : ;
;
…
;
}
برنامه 7) برنامه 6 را با استفاده از دستور switch / case بازنويسي نماييد. برنامه را بگونه اي بنويسيد كه حروف بزرگ و كوچك هردو مورد قبول واقع شود.
#include
void main() {
int aCount, bCount, cCount, dCount, n;
char grade;
aCount = bCount = cCount = dCount = n = 0;
do {
printf("Enter grade (Q for Quit) : ");
grade = getch() ;
n ++;
switch (grade) {
case 'A' :
case 'a' : aCount ++; break ;
case 'B' :
case 'b' : bCount ++; break ;
case 'C' :
case 'c' : cCount ++; break ;
case 'D' :
case 'd' : dCount ++; break ;
case 'Q' :
case 'q' : n--; break ;
default : printf("Wrong grade, try again.\n");
n --;
} //end switch
} while (grade != 'Q' ) ;
printf("Statistics :\n");
printf("Grade A : %f percent\n", float(aCount)/float(n));
printf("Grade B : %f percent\n", float(bCount)/float(n));
printf("Grade C : %f percent\n", float(cCount)/float(n));
printf("Grade D : %f percent\n", float(dCount)/float(n));
} // end main
برنامه 8) برنامه اي بنويسيد كه يك عدد، يك عملگر و يك عدد ديگر را از كاربر دريافت و پس از اعمال عملگر برروي دو عدد، حاصل را چاپ نمايد.
#include
void main() {
int number2, number2, result;
char op ;
printf("Please enter number1 operator number2 : ");
scanf("%d %c %d",&number1, &number2, &op, &number3);
result = 0;
switch (op) {
case '+' : result = number1 + number2 ; break;
case '-' : result = number1 - number2 ; break;
case '*' : result = number1 * number2 ; break;
case '/' : if (number2 != 0) result = number1 / number2 ;
else printf("There is no answer!\n");
break;
case '%' : if (number2 != 0) result = number1 % number2 ;
else printf("There is no answer!\n");
break;
default : printf("invalid operator!\n");
}
printf("Result = %d",&result);
}
دستورات break و continue
اين دستورات قادرند مسير اجراي برنامه را در يك حلقه تكرار تغيير دهند. البته اين تغيير مسير بصورت كنترل شده بوده و همانند دستور goto نمي توان به هرجاي دلخواه پرش كرد.
چنانچه دستور break در يك ساختار while، for، do/while ويا switch بكار رود، باعث مي شود كه بلافاصله كنترل اجراي برنامه از ساختار خارج شده و به اولين دستور پس از ساختار برود. قبلا كاربرد اين دستور را در switch ديده ايد. در اينجا به مثال زير دقت كنيد:
برنامه 9) برنامه 5 را بگونه اي تغيير دهيد كه فقط اعداد مثبت را بپذيرد، و درصورتيكه عدد منفي وارد شد، بلافاصله به عمليات خاتمه داده و نتايج تا همين نقطه را چاپ نمايد.
#include
void main() {
int i, n, number;
int sum, max1, max2;
printf(“please enter n : “);
scanf(“%d”,&n);
sum = 0;
max1 = max2 = -1;
for (i=1 ; i<0)> max1) {
max2 = max1;
max1 = number;
}
else if (number> max2)
max2 = number;
} //end for
printf(“Sum = %d, Maximum 1=%d, Maximum 2= d”, sum, max1, max2);
}
اما دستور continue فقط در حلقه هاي while، for و do/while بكار مي رود. نحوه عمل آن بدين صورت است كه به محض آنكه كنترل اجرا به اين دستور برسد، بلافاصله از باقيمانده حلقه صرفنظر كرده و مجددا به ابتداي حلقه باز مي گردد و اجراي آن را از سر مي گيرد. در مورد حلقه for، پس از بازگشت به ابتداي حلقه، عمل افزايش مقدار متغير حلقه نيز صورت مي پذيرد.
بعنوان مثال، چنانچه بخواهيم برنامه 9 را بگونه اي تغيير دهيم كه از اعداد منفي صرفنظر كند و آنها را در محاسبات لحاظ نكند، كافيست دستور
if (number <0)>
را به دستور زير تبديل كنيم:
if (number <0)>
دراينصورت، چنانچه عدد منفي باشد، بدون اينكه محاسبات بعدي انجام شوند، كنترل به ابتداي حلقه بازگشته و عدد بعدي را دريافت مي كند.