زبان برنامه نویسی سی/حلقه do while و while

حلقه‌های do ... while و while ویرایش

حلقه‌های do...while و while همانند حلقه for می مانند و تا زمانی که شرطشان برقرار باشد بدنه خود را اجرا و تکرار می کنند . حلقه‌ها بخش مهمی از هر زبان برنامه‌نویسی هستند . حلقه‌های do while و while همانند یکدیگر هستند و فقط تفاوت‌ جزئی با یکدیگر دارند . شکل کلی حلقه do while به شکل زیر است :

do {
statement 1;
statement 2;
.
.
.
//updating for conditional-expression
} while (conditional-expression);

و شکل کلی حلقه while به شکل زیر است :

while(conditional-expression)
{
statement 1;
statement 2;
.
.
.
//updating for conditional-expression
}

نکته : در حلقه while تنها جفت آکولاد باز و بسته نوشته می‌شوند و در انتهای آن نیازی به سمی کالن ( ; ) نیست اما در حلقه do while بعد از آکولادهای do که داخل آنها دستورات و حکم‌ها را نوشتید و درست پس از شرط آن که داخل while نوشته می‌شود باید یک سمی کالن یا همان نقطه ویرگول ( ; ) بگذارید

حلقه do while یک بار اجرا می‌شود و سپس شرط داخل while مقابل آن بررسی می‌شود تا اگر برقرار بود بدنه do تکرار شود ؛ بنابراین یک بار یا بیشتر به اجرا در می‌آید اما حلقه while بررسی می‌کند که آیا شرط آن برقرار است یا خیر ، اگر نبود حلقه به کلی نادیده گرفته می‌شود و اجرا نمی‌شود . این تفاوت do while و while است . دقت کنید : شما می‌توانید از آکولادهای باز و بسته برای ایجاد بدنه برای do while و while استفاده نکنید اما در این صورت تنها یک دستور statement می‌توانید برای آنها بنویسید تا اجرا و تکرار شوند و خطوط بعدی ، از نظر کامپایلر خارج از حلقه خواهند بود و در صورتی که برای آنها آکولاد بنویسید ، داده‌هایی که در آن اعلان یا تعریف می‌کنید محلی خواهند بود ( که می‌توانید داخل آنها دستورات متعدد و یا حلقه‌های دیگر را در کنار عملگرها بنویسید )

دقت کنید : در شکل کلی هر حلقه نوشته‌ایم : updating for conditional-expression به معنای اینکه شما باید تغییری را در عبارت شرطی حلقه در حلقه ایجاد کنید تا در هر بار اجرای حلقه به اجرا گذاشته شود و مرحله به مرحله به سمتی برود که دیگر شرط برقرار نباشد و حلقه بشکند . در غیر این صورت حلقه شما دائمی و بی‌نهایت خواهد شد و به سیستمی که در آن برنامه شما اجرا می‌شود فشار می‌آورد ( CPU شدیداً مشغول خواهد شد ) . بنابراین مهم است که به لحاظ منطقی حلقه شما به شکلی طراحی شود که شرط آن در صورت برقرار بودن به سمت برقرار نبودن حرکت کند و در یک جا غلط flase شود و حلقه بشکند

نکات :

با دستور break شما می‌توانید حلقه را در هر جای آن بشکنید و اجرای آن را متوقف و روند اجرای برنامه را به بعد از حلقه بسپارید ؛ که البته واضح است شما می‌توانید با کمک دستورات شرطی ( if ها ) برای شکستن حلقه خود اقدام کنید . دستور goto روند اجرای برنامه را به برچسب نام برده شده در آن انتقال می‌دهد . شما حلقه‌ها را تنها در تابع‌ها می‌توانید به کار ببرید و اگر در داخل حلقه خود از دستور return استفاده کنید مقدار نسبت داده شده به return برای تابع به عنوان خروجی در نظر گرفته می‌شود و حلقه شکسته می‌شود ( اجرای آن به پایان می‌رسد ) در هر کدام از حلقه‌های do while و while نوشتن دستور continue باعث می‌شود تا روند اجرای برنامه به انتهای حلقه برود و یک بار دیگر از اول بررسی کند که شرط برقرار است یا خیر و دستوراتی که مابین آن تا انتهای حلقه قرار دارند نادیده گرفته می‌شوند که بدیهی است می‌توانید آن را با کمک دستورهای شرطی ( if ها ) به اجرا بگذارید . ضمن اینکه شما می‌توانید دستورات حلقه do while و while را هر دو را یا هر کدام از آنها را به صورت تو در تو نیز بنویسید و محدودیتی برای تعداد تو در تو نوشتن حلقه‌ها وجود ندارد . ضمناً شما می‌توانید داخل یک حلقه do while یا while از حلقه یا حلقه‌های for نیز استفاده کنید ، عکس آن نیز امکان پذیر است ؛ یعنی می‌توانید داخل حلقه‌های for خود از حلقه‌های do while و while استفاده کنید و محدودیتی ندارید

دقت کنید : شما برای عبارت شرطی ( conditional-expression ) می‌توانید با کمک عملگرها ، از چند زیر عبارت استفاده کنید مثلاً :

while(i > 5 && i != 16)

که بدین معنی است که تا زمانی که متغیر i بزرگ‌تر از ۵ است و مساوی با ۱۶ نیست ؛ حلقه اجرا و تکرار می‌شود ( که بدیهی است باید در داخل حلقه مقدار i مرتب افزایش یابد که با یک ++i نیز امکان پذیر است که در هر بار اجرا ، i که مثلاً مقدار ۶ دارد ، یک واحد یک واحد مقدارش افزایش خواهد یافت و زمانی که به ۱۵ رسید ، حلقه برای آخرین بار اجرا خواهد شد و سپس حلقه می شکند چون نباید مساوی با ۱۶ باشد )

مثال :

#include<stdio.h>

int checkPrime(int isItPrime);

int main()
{

	printf("Counting Prime Numbers between twos\nEnter two numbers , the First number must be smaller\n");
	int num1, num2;
	scanf("%d%d", &num1, &num2);
	int count = 0;
	do{
	num1++;
	if(checkPrime(num1)==1)
	 count++;
	}while(num1<num2);
	printf("%d\n", count);
	
	return 0;
}

int checkPrime(int isItPrime)
{

	int result = 0, j = 2;
	while(j < isItPrime)
	{
	result = (isItPrime%j);
	j++;
	if (result==0)
	return 0;
	}
		
	return 1;
}

تشریح : در ابتدای این قطعه کد فایل سرآیند stdio را توسط دستور مستقیم include ضمیمه برنامه خود نموده‌ایم تا از دو تابع کتابخانه‌ای printf و scanf به ترتیب جهت چاپ در خروجی خط‌دستوری و گرفتن ورودی از صفحه کلید ( کیبورد ) کاربر استفاده کنیم . سپس تابعی با نوع داده صحیح با نام checkPrime اعلان نموده‌ایم که آن را در انتهای فایل تعریف نموده‌ایم . بدین شکل که یک پارامتر از نوع صحیح دارد ، پس آرگومانی از نوع صحیح می‌پذیرد و می‌توان به آن پاس داد ( فرستاد ) ؛ اما تابع checkPrime در داخل خود یک متغیر صحیح دارد با نام result به معنی نتیجه که با مقدار دهی اولیه 0 تعریف شده است و البته متغیر صحیح j که یکمین عدد اوّل را در خود ذخیره کرده است ( اگر از ریاضی اعداد به یاد داشته باشید « اعداد اوّل » اعدادی هستند که جز به خودشان و عدد ۱ به عدد دیگری بخش‌پذیر نیستند ) سپس در یک حلقه while تا زمانی که j کوچک‌تر از عدد پاس داده شده باشد بدنه خود را اجرا و تکرار می‌کند . نتیجه result باقی‌مانده تقسیم پارامتر تابع را ( که می‌شود عددی که به تابع پاس داده می‌شود ) به عدد j در خود ذخیره می‌کند . سپس j یک واحد افزایش می‌یابد . اگر نتیجه تقسیم برابر با 0 باشد تابع مقدار 0 را باز می‌گرداند . بنابراین حلقه j را از 2 شروع می‌کند و یک واحد یک واحد افزایش می‌دهد و عدد پاس داده شده به تابع که همان isItPrime است را بر j مرتب تقسیم می‌کند و اگر باقی‌مانده تقسیم 0 بود که یعنی عدد پاس داده شده دست به یک عدد بخش‌پیر است تابع مقدار 0 را باز می‌گرداند . در صورتی که حلقه هیچ مقدار 0 ـی باز نگرداند ، تابع مقدار 1 را باز خواهد گرداند
اما کاربرد آن چیست ؟ به وضوح مشخص است که تابع checkPrime به معنی چک کردن اول بودن یا نبودن ، اگر عدد اوّل نباشد مقدار 0 را باز می‌گرداند و اگر اوّل باشد مقدار 1 را . حال از این تابع در تابع اصلی برنامه یعنی main استفاده نموده‌ایم در تابع اصلی زمانی که برنامه به اجرا گذاشته می‌شود ، در خروجی خط‌دستوری چاپ می‌کند ( با کمک تابع کتابخانه‌ای printf ) : شمردن اعداد اوّل بین دو عدد ؛ خط شکسته می‌شود ؛ سپس دوباره چاپ می‌کند دو عدد را وارد کنید ، عدد اولی باید کوچک‌تر از دومی باشد . سپس دو متغیر صحیح با نام‌های num1 و num2 اعلان نموده‌ایم که مقدار آنها را با کمک تابع کتابخانه‌ای scanf از کاربر دریافت می‌کنیم . متغیر صحیح count با مقدار 0 تعریف شده است که از نام آن نیز پیداست که شمارنده است و برای شمردن تعداد اعداد اوّل بین دو عدد داده شده توسط کاربر که اولی کوچک‌تر از دومی است به کار می‌رود . با حلقه تکرار do while تعداد اعداد اوّل را شمرده‌ایم . بدین شکل که تا زمانی که num1 کوچک‌تر از num2 است num1 را یک واحد یک واحد افزایش داده‌ایم و در هر بار اگر عدد اول بود count یک واحد افزایش می‌یابد که از 0 شروع می‌شود و شمرده می‌شود که چند عدد اوّل بین دو عدد داده شده وجود داشته است که این کار را نیز با کمک دستور شرطی if انجام داده‌ایم بدین ترتیب که اگر num1 که عدد کوچک‌تر است ( و یک واحد یک واحد تا عدد دوم که بزرگ‌تر است افزایش می‌یابد ) پاس داده شود به تابع checkPrime که پیشتر توضیح دادیم که در صورت اوّل نبودن عدد مقدار 0 را باز می‌گرداند و در صورت اوّل بود مقدار 1 را و در صورتی که 1 بود یعنی اوّل بود count یک واحد افزایش می‌یابد . بعد از آن نیز در خروجی خط‌دستوری مقدار count که شمارنده تعداد اعداد اوّل بوده است ، چاپ می‌شود و تابع main مقداری باز نمی‌گرداند و برنامه پایان می‌یابد

مثال :

#include<stdio.h>

int main()
{

	int a = 32;
	while((a>31)&&(a<127))
	{
	printf("%c\n", a);
	a++;
	}
	
	return 0;
}

مثال بالا کاراکترهای قابل مشاهده ازکی ( ASCII ) را در خروجی خط‌دستوری چاپ می‌کند . در قطعه کد بالا ابتدا فایل سرآیند stdio را جهت استفاده از تابع کتابخانه‌ای printf ضمیمه برنامه خود نمودیم و در تابع اصلی برنامه ( یعنی تابع mian ) یک متغیر صحیح با نام a ایجاد نمودیم و به آن مقدار 32 دادیم که اولین مقدار برای کاراکترهای قابل مشاهده کاراکترست ازکی می‌باشد . سپس با کمک دستور حلقه while از زمانی که a بزرگ‌تر از ۳۱ و کوچک‌تر از ۱۲۷ است که می‌شود ۳۲ تا ۱۲۶ تابع کتابخانه‌ای printf را به اجرا می‌گذارد و سپس یک واحد به a اضافه می‌کند و سپس تا زمانی که شرط برقرار باشد تکرار می‌شود . تابع کتابخانه‌ای printf مقدار متغیر صحیح a را با مقدار معادل آن در ازکی ( با کمک کاراکتر کنترلی c% ) در خروجی خط‌دستوری به عنوان کاراکتر چاپ می‌کند و خط را می‌شکند . در پایان برنامه تابع main برنامه با موفقیت به پایان می‌رسد

مثال :

#include <stdio.h>

int main()
{
	int i, j, rows;

	printf("Enter number of rows : ");
	scanf("%d", &rows);

	i = 1;
	while(i<=rows)
	{ 

		j=i;
		while(j<rows)
		{
			printf(" ");
			j++;
		}
		j=1;
		while(j<=(2*i-1))
		{
			printf("*");
			j++;
		}

		printf("\n");
		i++;
	}

	return 0;
}

مثال بالا یک مثلث متساوی‌الاضلاع را با کاراکتر ستاره ( استریسک ) در خروجی خط‌دستوری چاپ می‌کند . اما چگونه ؟ پس از ضمیمه نمودن فایل سرآیند stdio که سرنام standard input/output می‌باشد ( ورودی/خروجی‌های استاندارد ) در تابع اصلی برنامه که برنامه را فرا می‌خواند و به اجرا می‌گذارد یعنی تابع main که سه متغیر اعلان نموده‌ایم ( با نام‌های i و j و rows ) با کمک تابع کتابخانه‌ای printf که در stdio تعریف شده‌است چاپ می‌کنیم تعداد سطرها ( خط‌ها ) را کاربر وارد کند ، سپس با کمک تابع کتابخانه‌ای scanf که همچون printf در فایل سرآیند stdio تعریف شده است عدد را گرفته و در متغیر rows ذخیره می‌کنیم . سپس دستور می‌دهیم ( حکم می‌کنیم ) تا مقدار متغیر i یک باشد ( 1 ) و تا زمانی که i کوچک‌تر مساوی عدد وارد شده توسط کاربر است ( rows ) حلقه while بیرونی اجرا و تکرار شود . در این حلقه دو حلقه while دیگر نیز تعریف شده‌اند . اولی ابتدا دستور می‌دهد تا مقدار i داخل j گذاشته شود و سپس تا زمانی که j از عدد وارد شده یعنی rows کوچک‌تر است فضای خالی ( space ) چاپ کند ( که با کمک ++j متغیر j مقدارش افزایش می‌یابد و در نهایت حلقه می‌شکند ) یعنی حلقه داخلی اول از i تا rows فضای خالی چاپ می‌کند . سپس دستور می‌دهیم ( حکم می‌کنیم ) تا مقدار 1 در j ذخیره شود تا در حلقه while داخلی و درونی دومی تا زمانی که j کوچک‌تر از یکی کم‌تر از دوبرابر i است استریسک ( ستاره * ) چاپ کند که با کمک ++j مقدار j افزایش می‌یابد و حلقه می‌شکند ( اگر از جبر به یاد داشته باشید اعداد فرد را به صورت 2k-1 نشان می‌دهیم که اینجا به جای k از i استفاده نموده‌ایم ) سپس داخل حلقه while بیرونی خط می‌شکند و i یک واحد افزایش می‌یابد . نتیجه این می‌شود که i مرتب افزایش می‌یابد و j که در حلقه درونی اول مقدار i را می‌گیرد و تا rows ادامه می‌دهد تا فضای سفید چاپ کند در هر دفعه اجرای while بیرونی کمتر تکرار می‌شود ( اول 1 است و تا عدد وارد شده مثلا ۶ اجرا و تکرار می‌شود و در خط بعدی از ۲ شروع می‌کند که کمتر می‌شود ) درست بعد از فضاهای سفید به تعداد اعداد فرد ( و دو برابر i منهای یکی ) ، ستاره چاپ می‌شود و سپس خط می‌شکند و دوباره فضاهای سفید چاپ می‌شوند که مدام کمتر می شوند و در عوض ستاره‌ها بیشتر می‌شوند . نتیجه کامپایل این قطعه کد ، برنامه‌ای است که به تعداد خطوط وارد شده توسط کاربر ، یک مثلث متساوی‌الاضلاع چاپ می کند

توضیح : در مثال بالا اگر عدد ورودی ۲ باشد ، شکل ما چندان شبیه مثلث متساوی‌الاضلاع نخواهد بود ، اما با اعداد ۴ ، ۵ ، ۶ یا ۷ که در هر سطر به صورت افزایشی با اعداد فرد ، استریسک ( ستاره ) چاپ می‌نمائیم ، شکل به دست آمده شبیه مثلث متساوی‌الاضلاع خواهد شد

دقت کنید : همان طور که گفته شد می‌توان داخل while یا do while حلقه for به کار برد و داخل حلقه‌های for می‌توان while یا do while را به کار بست . در پست بالا ما می‌توانیم هر کدام از while ها ( چه بیرونی و چه داخلی‌ها ) را به صورت for بنویسیم