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

ویکی‎کتاب، کتابخانهٔ آزاد

دستور حلقه for[ویرایش]

دستور for یک حلقه است و یک حلقه ، دستورات ، حکم‌ها و عملگرهای خود را اجرا کرده و تکرار می‌کند . دقت کنید که حلقه‌ها را تنها داخل بدنه تابع‌ها می‌توان تعریف نمود . for در لفظ به معنی « برای » می‌باشد اما در زبان برنامه‌نویسی C و خانواده آن بیشتر به معنای « در ازای ... » می‌باشد . حلقه for بیشتر در قسمت‌هایی از برنامه نوشته می‌شود که ما نیاز داریم تا به تعداد معینی حکم یا حکم‌هایی ( معمولاً همراه با دستورهای دیگر و عملگرها ) اجرا شوند و به تکرار در بیایند

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

for(initialization-expressions ; conditional-expressions ; modifier-expressions)
{
statement 1;
statement 2;
.
.
.
}

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

در حلقه for ابتدا با عبارت initialization یک یا چند متغیر را تعریف می‌کنیم که ممکن است پیش از دستور for نیز تعریف شده باشند که در این صورت تنها شناسه آنها را می‌نویسیم یا اینکه پیش از for اعلان شده باشند که آنها را مقدار دهی می‌کنیم و حلقه for در ازای عبارت تغییر دهنده یا مرحله تا زمانی که عبارت شرطی ( conditional-expression ) برقرار باشد ، حکم‌های داخل بدنه خود را به اجرا در می‌آورد که به initialization شمارنده یا counter نیز گفته می‌شود . دقت کنید که شما می‌توانید جفت آکولاد باز و بسته را برای دستور for ننویسید ، اما با این کار تنها مجاز هستید تا فقط یک حکم برای دستور حلقه for خود بنویسید و فقط همان یک حکم تکرار می‌شود . ضمن اینکه اگر برای حلقه for خود آکولادهای باز و بسته را بنویسید ، تمام داده‌هایی که داخل آکولادهای for اعلان یا تعریف شده‌اند محلی خواهند بود

اگر قصد دارید حلقه تکرار خود را به چند متغیر وابسته کنید در قسمت initialization-expressions متغیرهای خود را با کاما « , » از یکدیگر جدا کنید . اما در قسمت شرط یعنی conditional-expressions نمی‌توانید با کاما شرط‌ها را از یکدیگر جدا کنید بلکه باید با کمک عملگرها ؛ مثلاً عملگر « و منطقی » یعنی && آنها را از هم جدا کنید که البته به این معنا خواهد بود که تا زمانی شرط اول و دوم برقرار هستند ، حلقه تکرار شود ( محدودیتی برای تعداد شرط‌ها وجود ندارد ) نوشتن شرط اختیاری است و تا زمانی که شرط برقرار باشد حلقه تکرار می‌شود یعنی تا زمانی که شرط غلط ( false ) نباشد . بنابراین هر حلقه for هیچ بار یعنی ۰ بار یا بیشتر به اجرا در می‌آید که وابسته به برقرار بودن شرط حلقه است . دقت کنید که اگر شرطی را برای حلقه for ننویسیم شرط همیشه برقرار است و حلقه دائمی خواهد بود و تا بی‌نهایت بار اجرا می‌شود مگر آنکه با دستور break آن را بشکنیم یا داخل حلقه از دستور return استفاده کنیم که این دستور return باید همراه با یک مقدار باشد و چون حلقه داخل تابع تعریف می‌شود به عنوان خروجی تابع نیز خواهد بود ( یا اینکه با دستور goto روند اجرای برنامه را به بعد از حلقه for ارجاع دهیم ) همچنین دقت کنید که هر مقدار غیر 0 که به عنوان شرط حلقه نوشته شود به معنای برقرار بودن همیشگی شرط است و در صورت نوشتن 0 یعنی شرط برقرار نیست و غلط یا false است . البته می‌توانید از مقادیر منطقی ( Boolean ) flase و true نیز استفاده کنید ( این مطلب در فصل فایل‌های سرآیند بررسی و تشریح خواهد شد )

modifier-expressions عبارت یا عبارت‌هایی هستند که عبارت‌های تعریف شونده موسوم به کنترلگر initialization-expressions را تغییر می‌دهند تا به سمت برقرار نبودن شرط حرکت کنند تا حلقه از یک جا شروع شود و در ازای اینکه کنترلگر به سمت غلط بودن حرکت می‌کند بررسی شود که شرط برقرار است یا نه تا حلقه for اجرا شود و بدنه خود را تکرار کند . بنابراین modifier-expressions که به عنوان مرحله یا step نیز شناخته می‌شود مقدار اولیه متغیرهای کنترلگر را تغییر می‌دهد تا تعداد دفعات معینی که برنامه‌نویس با حلقه for نوشته حلقه for به اجرا در بیاید و در یک جا حلقه شکسته شود . متداول‌ترین مرحله‌ها یا تغییر دهنده‌ها ، عملگرهای ++ یا -- هستند که به صورت پسوند یا پیشوند نوشته می‌شوند . دقت کنید که طبق استاندارد C شما نباید برای عبارت تغییر دهنده ( modifier ) یا همان مرحله ، سمی‌کالن بگذارید . مطمئناً طبق روال شما می‌خواهید به عنوان مثال بنویسید ;++i اما این کار طبق استاندارد C مجاز نیست و کامپایلر از شما خطا خواهد گرفت ؛ بنابراین در داخل دستور for تنها دو سمی‌کالن « ; » می‌گذارید یکی برای کنترلگر و مقدار دهی و دیگری برای شرط

نجوه نوشتن دستور for بی‌نهایت بدین شکل می‌باشد :


for( ; ; )

این حلقه بی‌نهایت بار تکرار می‌شود و حکم‌های خود را که می‌توانند دارای دستورها و عملگرها نیز باشند به اجرا می‌گذارد مگر اینکه طبق دستور صریح شما ( مثلاً با break ) حلقه شکسته شود

اخطار : حلقه‌ها در هر زبان برنامه‌نویسی از روی حلقه‌هایی که جزء instruction set ها یا دستورات مستقیم CPU هستند تعریف می‌شوند و داده‌های حلقه‌ها نیز داخل register های کش CPU و یا پشته سسستم که سریعتر است ذخیره می‌شوند . بنابراین حلقه‌های بی‌نهایت به شدت به سیستم فشار می‌آورند و ممکن است باعث قفل شدن سیستم شوند . معمولاً تنها برنامه‌نویس‌های مجرم برای نوشتن بدافزارها ( malwares ) از حلقه‌های بی‌نهایت استفاده می‌کنند

یک مثال برای درک چگونگی عملکرد حلقه for :


#include<stdio.h>

int main()
{
	for(int i = 0; i<5; i++)
	{	
	printf("Hello World!\n");
	}
	return 0;
}

در مثال بالا بعد از ضمیمه نمودن فایل سرآیند stdio که قصد استفاده از تابع کتابخانه‌ای printf را از آن داریم ، تابع اصلی اجرا کننده برنامه یعنی main را نوشته‌ایم که با موفقیت به پایان می رسد ( در انتهای تابع اصلی ;return 0 ذکر شده است ) اما یک دستور حلقه for را نوشته‌ایم که در قسمت مقداردهی که کنترلگر یا شمارنده حلقه می‌باشد یک متغیر از نوع صحیح با نام i با مقدار 0 تعریف نموده‌ایم . چون شرط این است که کوچک‌تر از 5 باشد که شرط برقرار است ، پس بدنه حلقه به اجرا در می‌آید و تابع printf در خروجی خط دستوری یک بار چاپ می‌کند : !Hello World که البته با کاراکتر خروج n\ خط را نیز شکسته‌ایم . سپس ++i به اجرا در می‌آید و حالا مقدار i می‌شود 1 که باز هم کوچک‌تر از 5 است و شرط همچنان برقرار است ؛ پس یک بار دیگر بدنه حلقه for به اجرا گذاشته می‌شود و یک بار دیگر printf چاپ می‌کند !Hello World و خط را می‌شکند . این مراحل تا جایی ادامه پیدا می‌کند که i می‌رسد به 5 و دیگر i کوچک‌تر از 5 نیست و شرط برقرار نمی‌باشد ؛ بنابراین اجرای حلقه for پایان می‌یابد . اگر قطعه کد بالا را کامپایل کنید در خروجی خط‌دستوری در ۵ خط جدا ، جمله !Hello World را خواهید دید

مثال :

#include<stdio.h>

int main()
{
	int i;
	for(i = 0; i < 10; i = i + 2)
	{	
	printf("%d\n", i);
	}
	return 0;
}

مثال بالا همانند مثال قبل است جز اینکه متغیر شمارنده یا کنترلگر حلقه for خود را که نام i دارد ، بیرون از حلقه for اعلان نموده‌ایم و داخل حلقه آن را مقدار دهی نموده‌ایم تا تعریف شود . تا زمانی که i کوچک‌تر از 10 باشد حکم i = i + 2 به اجرا گذاشته می‌شود که خروجی ، اعداد 0 2 4 6 و 8 خواهند بود که در خط‌های مجزا از هم چاپ می‌شوند . دقت کنید که تابع printf مقدار خود متغیر شمارنده یعنی i را چاپ یا پرینت می‌کند ؛ یعنی i مقدار 0 دارد که مقدارش چاپ می‌شود ، سپس با عدد ۲ جمع می‌شود و می‌شود ۲ که چاپ می‌شود بعد می‌شود ۴ و چاپ می‌شود و همین طور الی آخر

مثال :

#include<stdio.h>
#include<string.h>

int main()
{
   char* line = "H e  \tl\tlo World\0";
   int space = 0;
   int tab = 0;
   int i;
   int max = strlen(line);
   for (i = 0; i < max; i++ )
   {
      if ( line[i] == ' ' )
      {
          space++;
      }
      if ( line[i] == '\t' )
      {
          tab++;
      }
   }

   printf("Number of spaces: %i\n", space);
   printf("Number of tabs: %i\n", tab);
   return 0;
}

در مثال بالا فایل سرآیند stdio را جهت استفاده از تابع printf برای چاپ خروجی خط‌دستوری و فایل سرآیند string ( به معنی رشته ) را جهت استفاده از تابع کتابخانه‌ای strlen که مخفف string length می‌باشد و طول یک رشته را باز می‌گرداند ( تعداد کاراکتر‌های یک رشته ) ضمیمه برنامه خود نموده‌ایم . سپس یک رشته با نام line ایجاد نموده‌ایم و در آن یک رشته از کاراکترها را ذخیره نموده‌ایم . دو متغیر صحیح را تعریف نموده‌ایم با نام‌های فاصله space و فاصله زیاد tab ( تَب tab به معنی سطر جدول ) که به آنها مقدار پیش‌فرض 0 داده‌ایم و شمارنده حلقه خود را با نام i اعلان نموده و همچنین متغیری با نام max که مخفف maximum می‌باشد و به معنی حد نهایی است که با استفاده از تابع strlen طول رشته line را در خود ذخیره می‌کند تا حلقه for از 0 که مقداردهی اولیه شمارنده i می‌باشد شروع کند و تا زمانی که به max نرسیده دو دستور if داخل خود را اجرا و تکرار کند ؛ که این دو دستور فاصله و فاصله زیاد متن ( رشته ) line را می‌شمارند . بدین شکل که از line به صورت یک آرایه استفاده نموده‌ایم که اندیس آن شماره و مقدار شمارنده یا کنترلگر را دارد و یک به یک کاراکترهای رشته چک می‌شوند و اگر فاصله بود space ( فاصله ) که مقدار پیش فرض 0 دارد با کمک عملگر ++ یک واحد افزایش می‌یابد و اگر فاصله زیاد ( سطر جدول ) tab بود متغیر tab که مقدار پیش فرض 0 دارد یک واحد افزایش خواهد یافت . این کار تا جایی تکرار می‌شود که رشته پایان یابد ؛ بنابراین به تعداد فاصله‌ها و فاصله‌های زیاد ، مقدار متغیرهای space و tab افزایش یافته‌اند و مقداری که دارند نشان دهنده تعداد وجود فاصله‌ها و فاصله‌های زیاد در رشته line خواهد بود

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

مثال :

#include<stdio.h>

int main()
{
	for(int i = 2; i < 10; i++)
	{
		for(int j = 2; j < 10; j++)
		{
		printf("%d*%d=%d \t", i, j, i*j);
		}
	printf("\n");
	}
	return 0;
}

در مثال بالا به عنوان یک نمونه از دو حلقه for استفاده نموده‌ایم که یکی از آنها داخل دیگری تعریف شده است ( دو حلقه for تو در تو ) و نتیجه آن جدول ضرب است . اما چگونگی آن : بعد از ضمیمه نمودن فایل سرآیند stdio که قصد استفاده از تابع کتابخانه‌ای printf را داریم ، داخل تابع اصلی برنامه یعنی main ( که با موفقیت به پایان رسیده ) یک حلقه for تعریف نموده‌ایم که در آن یک شمارنده یا کنترلگر را با نام i با مقداردهی اولیه 2 تعریف نموده‌ایم که تا زمانی که مقدار i به 10 نرسیده باشد ( که می‌شود 9 ) مرحله به مرحله یک واحد افزایش می‌یابد و حلقه خود را به اجرا می‌گذارد . اما داخل حلقه for اولی و بیرونی ، یک حلقه for دومی و داخلی نیز تعریف نموده‌ایم که با شمارنده و کنترلگری به نام j با مقداردهی اولیه 2 تعریف شده است و تا زمانی که به 10 نرسیده باشد ( که می‌شود 9 ) حلقه خود را به اجرا می‌گدارد . نتیجه این می‌شود که i مقدار 2 دارد پس حلقه خود را اجرا می‌کند که این حلقه یک حلقه دیگر دارد که مقدار j نیز 2 است پس حلقه خود را تا زمانی که j کوچک‌تر از 10 است اجرا و تکرار می‌کند که در خروجی خط دستوری چاپ می‌کند مقدار i ضربدر j مساوی است با محاسبه ضرب i در j و یک فاصله زیاد ( سطر جدولی ) نیز چاپ می‌کند زمانی که آغاز می‌شود 2*2 است و تا 9*2 ادامه می‌یابد ، سپس حلقه به پایان می‌رسد ؛ در خلقه بیرونی یک بار با printf خط را شسکسته‌ایم حالا دوباره حلقه for بیرونی ارزیابی می‌شود ، i یک واحد افزایش می‌یابد و می‌شود 3 و چون کوچک‌تر از 10 است یک بار دیگر حلقه خود را اجرا می‌کند که j نیز مقدار 2 دارد و تا 9 ادامه می‌یابد و تکرار می‌شود . سپس i می‌شود 4 و j از 2 شروع می‌شود و تا 9 ادامه می‌یابد . بنابراین خروجی خط دستوری از ضرب 2 در 2 شروع می‌شود و در خط اول تا 9*2 ادامه می‌یابد ، خط می‌شکند و در خط بعدی از ضرب 3 در 2 شروع می‌شود و تا 9*3 ادامه می‌یابد در خط نهایی ، ضرب ، از 9 در 2 شروع می‌شود و تا 9 در 9 ادامه می‌یابد
نکته : ممکن است رابط خط‌دستوری سیستم‌عامل شما ( Command Prompt در ویندوز که با نام cmd.exe موجود است یا مثلاً Terminal در سیستم‌عامل‌های لینوکس یا مکینتاش و تقریباً همه شبه‌یونیکس‌ها ) اندازه‌ای کوچک‌تر از اندازه این جدول ضرب داشته باشد و مجبور باشد پیش از دستور شکسته شدن خط ، خود خط را بشکند ، برای مشاهده بهتر خروجی قطعه کد بالا که جدول ضرب می‌باشد ، پنجره رابط خط‌دستوری خود را به اندازه بیشینه Maximize در بیاورید
برای درک بهتر و ساده‌تر مثال بالا باید اینگونه بگوئیم که حلقه for اولی و بیرونی ۹ بار به اجرا در می‌آید که در هر بار اجرا حلقه for داخل خود را اجرا می‌کند که می‌شود ۹ بار اجرای آن . اما دقت کنید که همان طور که در مبحث کلاس‌های ذخیره به این مطلب اشاره شد ، داده‌هایی که داخل پرانتزهای دستورها و یا تابع‌هایی که می‌خواهید تعریف کنید وجود دارند حوزه فرمال ( formal scope ) خواهند داشت ؛ بدین معنی که زمانی که اجرا می‌شوند مقدار می‌گیرند و داخل بدنه این مقدار حفظ می‌شود و قابل دسترسی است ولی پس از پایان اجرای دستور یا تابع مذکور ، آن داده‌ها ( که داخل پرانتز تعریف شده‌اند ) از بین می‌روند و تا حدودی شبیه حوزه محلی ( local scope ) می‌باشند اما به آنها حوزه فرمال ( formal scope ) گفته می‌شود که در مثال بالا هم در هر بار اجرای حلقه for بیرونی حلقه for داخلی و دومی که به اجرا در می‌آید ، متغیر j مقدار می‌گیرد و با پایان حلقه for داخلی متغیر j از بین می‌رود و با اجرای بعدی حلقه for بیرونی ، دوباره حلقه for داخلی به اجرا گذاشته می‌شود و متغیر j ایجاد می‌شود و مقدار 2 می‌گیرد که تا 9 ادامه می‌یابد و بدنه خود را اجرا و تکرار می‌کند و سپس با پایان حلفه داخلی ، متغیر j از بین می‌رود