زبان برنامه نویسی سی/دستور goto

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

دستور برو به goto[ویرایش]

دستور « برو به ... » که به انگلیسی می‌شود « ... go to » و با کلیدواژه goto نوشته می‌شود روند اجرای برنامه را از جایی که کلیدواژه نوشته شود به بخش دیگری از برنامه که با برچسب « label » مشخص شده است انتقال می‌دهد . هر برچسب دارای یک نام یا شناسه است که همانند نام یا همان شناسه متغیرها می‌باشد و باید اختصاصی و انحصاری باشد . یعنی شما نمی‌توانید دو برچسب را با یک نام بنویسید یا نام برچسب را نام ( یا همان شناسه ) یک متغیر انتخاب کنید . نحوه نوشتن یک برچسب ( label ) بدین شکل است :


label-identifier: statement-1;

در مقابل نام برچسب باید یک کالن یا همان دو نقطه بگذارید و طبق استاندارد ISO سپس حداقل یک حکم در مقابل آن بنویسید . مثلاً :


result: printf("%d\n", j);

در اینجا result یک برچسب است با شناسه و نام result که اگر بنویسید :

goto result;

روند اجرای برنامه به برچسب result می‌رود و خط‌به‌خط از آنجا به بعد ادامه یافته و کدها اجرا می‌شوند . برچسب می‌تواند پس از goto یا قبل از goto نوشته شده باشد

در صورتی که بخواهید از چند حکم استفاده کنید ، باید در مقابل برچسب خود یک بلوک ایجاد کنید ( با یک جفت آکولاد باز و بسته ) مثل :

label-identifier:
{
statement-1;
statement-2;
statement-3;
}

یکی از کاربردهای goto برای خروج از حلقه‌های تو در تو می‌باشد ؛ مثلاً فرض کنید شما یک برنامه‌ای می‌نویسید که برنامه هوش مصنوعی است و در هنگام نوشتن هوش مصنوعی ( مثل پروژه CGI ) مجبورید از حلقه‌های خیلی زیادی که تو در تو نیز هستند استفاده کنید ؛ مثلاً فرض کنید ۸ حلقه تو در تو در دارید که اگر شرط خاصی برقرار بود باید تمام حلقه‌ها بشکنند و از تکرار آنها جلوگیری شود ؛ اگر بخواهید از روش متداول شکستن حلقه‌ها استفاده کنید باید یک به یک در حلقه‌ها شرط خود را بنویسید که اگر برقرار بود با دستور break حلقه ، شکسته شود ؛ پس شما ۸ بار باید کد تکراری را بزنید در حالی که با نوشتن یک برچسب بیرون از هر ۸ حلقه ( و پس از آنها ) و نوشتن یک goto داخل حلقه مورد نظر و داخلی اجرای تمام حلقه‌ها را متوقف می‌کنید و روند اجرای برنامه را به بیرون از حلقه‌ها و ادامه برنامه می‌فرستید . از goto برای ایجاد یک حلقه نیز استفاده می‌شود . مثلاً یک برچسب را می‌نویسید و سپس شرطی را می‌نویسید ؛ که متغیر شرط در خط بعدی یا خطوط بعدی تغییر می‌کند و در انتهای آن از دستور goto برای ارجاع به برچسب استفاده می‌کنید که کامپایلر باز می‌گردد به خطوط قبلی و آنها را اجرا می‌کند و سپس دوباره به goto می‌رسد پس دوباره باز می‌گردد به خطوط و کدهای قبلی و آنها را اجرا می‌کند و این در حالیست که در هر بار اجرا متغیر شرط به برقرار نبودن نزدیک می‌شود تا جایی که در نهایت برقرار نیست و بنابراین goto نیز اجرا نمی‌شود و ادامه برنامه دنبال می‌شود . این روش مانند ساخت یک حلقه است اما به هیچ وجه توصیه نمی‌شود . شما می‌توانید از goto برای ایجاد اتصال میان بخش‌های مختلف کدها نیز استفاده کنید ؛ اما این نیز توصیه نمی‌شود چرا که برنامه شما را ناخوانا و پیچ در پیچ می‌کند ( حتی ممکن است خود شما نیز به اشتباه بیافتید که در نهایت برنامه خروجی ، نتیجه مطلوب را نخواهد داشت ) مثلاً فرض کنید شما چند حلقه و دستورها و عملگرهای مختلفی داخل تابع خود دارید و با کمک دستورهای شرطی و دستور goto ، مرتب برنامه را از این قسمت به آن قسمت و از آن قسمت به این قسمت ، پاس کاری می‌کنید ولی این کار درک برنامه را مشکل می‌کند و هیچ یک از برنامه‌نویسان حرفه‌ای و اساتید برنامه‌نویسی ، چنین کد زدنی را به برنامه‌نویسان توصیه نمی‌کنند

در مجموع سعی کنید تا زمانی که نیازی نیست از goto استفاده نکنید . دقت کنید که از goto تنها داخل بدنه تابع می‌توان استفاده نمود ، ضمن اینکه به هر جای تابع که برچسبی داشته باشد دسترسی دارید و با دستور goto ادامه اجرای برنامه به آنجا می‌پرد ( jump ) یعنی از یک قسمت از برنامه که دارد اجرا می‌شود به قسمت دیگری از برنامه می‌رود ( که مسلماً به واسطه ;goto label-identifier اتفاق میافتد ) اما دقت کنید که goto نمی‌تواند از داخل یک تابع به داخل تابع دیگری برود ؛ یعنی هر برچسب ، محلی می‌باشد و داخل تابع خود معنا و امکان حضور می‌یابد

مثال برای ایجاد یک حلقه :


#include <stdio.h>

int main()
{
	int i = 0;
	label:
	printf("%d\n", i);
	i++;
	if(i<10)
	goto label;

	return 0;
}

در مثال بالا بعد از ضمیمه نمودن فایل سرآیند stdio جهت استفاده از تابع کتابخانه‌ای printf ( که داخل آن تعریف شده است تابع اصلی برنامه را ( یعنی تابع main را ) تعریف نموده‌ایم که متغیری با نام i در آن با مقدار 0 تعریف شده است . سپس یک برچسب با نام label تعریف کرده‌ایم که در خط بعدی مقدار i را به همراه شکستن خط در خروجی خط‌دستوری چاپ می‌کند . سپس i به کمک عملگر افزایش یک واحد افزایش می‌یابد . در خط بعدی اگر i کوچک‌تر از 10 باشد دستور goto را به اجرا می‌گذارد که ادامه اجرای برنامه را به چند خط ، قبل‌تر ، یعنی برچسب label باز می‌گرداند . پس حالا مقدار i که 2 شده است چاپ می‌شود و سپس یک واحد دیگر افزایش می‌یابد و برنامه مرتب تا زمانی که i کوچک‌تر از 10 باشد همین روال را ادامه می‌دهد ، اما زمانی که i به مقدار 10 می‌رسد ؛ دیگر شرط if ما برقرار نخواهد بود و از این روی دیگر برنامه به چند خط قبل‌تر نمی‌رود و به بیرون از دستور if که می‌شود ادامه برنامه خواهد رفت و که حکم و دستور دیگری وجود ندارد مگر دستور return که مقدار 0 را باز می‌گرداند و برنامه ما به پایان می‌رسد

دقت کنید : ما با قطعه کد بالا ، یک حلقه ساختیم ، اما این حلقه توسط کامپایلر به یک حلقه که یک instruction پردازشگر ( CPU ) که سرعت بسیار بالایی در تکرار دارد ، ترجمه نمی‌شود . بنابراین برنامه ، درست مثل این اجرا می‌شود که شما ۱۰ بار حکم چاپ اعداد و شکستن خط را نوشته و در نهایت اجرا کنید . سرعت این شبه‌حلفه بسیار کم‌تر از حلقه‌ای است که توسط دستورهایی مثل for یا while می‌نویسید و توسط کامپایلر به کدهای سیستم‌عامل و یا ماشین ترجمه می‌شوند ( مخصوصاً اگر قصد ایجاد حلقه‌های تو در تویی داشته باشید که محاسبات سنگینی را انجام دهند و داده‌های حلقه‌ها - معمولاً شرط‌ها - را ارزیابی نمایند )

استفاده دیگر عمومی goto

دستور goto در زبان C و خانواده آن و تمام زبان‌هایی که از زبان C الگو گرفته‌اند وجود دارد . اما بسیاری از زبان‌های خیلی سطح بالا امکاناتی دارند که نیاز به استفاده از goto را از میان بر می‌دارند . ولی استفاده goto فقط محدود به خارج شدن از حلقه‌های تو در تو نیست ؛ بلکه جلوی کد نویسی اضافه و اشغال حجم زیاد فایل برنامه را به نحو دیگری همانند خروج از حلقه‌ها می‌گیرد . برای یک مثال ملموس مجبوریم کمی به مباحث نرم‌افزاری بپردازیم . در دنیای نرم‌افزار فرمت‌ها ( Format ) ی مختلفی برای تصاویر رستر ( Raster ) که بر اساس پیکسل تعریف می‌شوند و یا تصاویر وکتور ( Vector ) که با خطوط و اشکال تعریف می‌شوند ، فرمت‌های مالتی مدیا ( چند رسانه‌ای ) مثل فرمت‌های صوتی MP2 یا MP3 یا FLAC یا AIFF یا AC3 یا AAC و یا فرمت‌های ویدئویی MPEG4 و یا ماتروسکا که پسوند mkv ذخیره می‌شود وجود دارند که شما در نوشتن برنامه‌های خود به خواندن و نوشتن آنها احتیاج دارید . اصلاً گاهی ممکن است خودتان بخواهید یک فرمت مخصوص به خود را تعریف کنید ( برای برنامه‌های مربوط به تجارت ، برنامه‌های حسابداری و مالی ، آمار و ... ) این فایل‌ها که هر کدام فرمت خاص خود را دارند یک هدر دارند که به عنوان شناسه آن فرمت مطرح می‌شوند . مثلاً فایل‌های بیت‌مپ ( Bitmap ) که با پسوند bmp در ویندوز وجود دارند توسط مایکروسافت ابداع شده است و هدر ( Header ) آن BM می‌باشد . یعنی بایت اول آن ، کاراکتر B و بایت دوم آن کاراکتر M است . حال فرض کنید شما می‌خواهید یک برنامه Raster Image Editor مثل فتوشاپ ( Adobe Photoshop ) یا پینت‌شاپ پرو ( Corel PaintShop Pro ) یا گیمپ GIMP بنویسید ( البته ادوبی فتوشاپ Adobe Photoshop و کورل پینت‌شاپ‌پرو Corel PaintShop Pro به زبان سی‌پلاس‌پلاس ++C نوشته شده‌اند و گیمپ GIMP به زبان سی C ) . نه وظیفه شما است و نه در حوصله شما که اگر فایلی که کاربر می‌خواهد باز کند ناخوانا باشد را تعمیر و یا احیا کند ( Repair و یا Recovery ) به این گونه فایل‌ها که به هر دلیلی آسیب دیده‌اند و خوانا نیستند فایل‌های خراب ( فاسد یا Corrupted ) گفته می‌شود . بنابراین شما در برنامه خود تعریف می‌کنید تا اگر کاربر فایل مورد حمایت شما را باز کرد اما خوانا نبود پیغام بدهد Your File Is Corrupted یعنی فایل شما خراب ( فاسد ) است . به انواع و اقسام ممکن است فایل ایراد داشته باشد و شما در کدنویسی اگر بخواهید از اتلاف وقت و کد زدن اضافه جلوگیری کنید در تابع خود در پایان آن یک برچسب می‌سازید و می‌نویسید Your File Is Corrupted و پس از شروع به خواندن فایل با شروط مختلف هر جا چیزی جز استاندارد وجود داشت با دستور goto ادامه برنامه را به یک برچسب ارجاع می‌دهید که پیغام خطا می‌دهد که فایل شما فاسد است . و دیگر نیازی ندارید مرتب کد بزنید که پیغام خطا چاپ شود که فایل شما فاسد است . یک بار آن را در یک برچسب می‌نویسید و با شروط ، در کتابخانه و یا مثلاً در Codec خود که فرمت یا فرمت‌های مورد نظر شما را می‌خواند و می‌نویسد کد می‌زنید تا فایل را بخواند و مثلاً نمایش دهد یا در صورت ناخوانا بودن با هر دلیل فقط یک پیغام چاپ کند که فایل شما فاسد است

به صورت سمبلیک می‌توانید این گونه کد نویسی را در خطوط زیر مشاهده کنید :

int reading()
{

if(conditon)//header exists
goto showimage;
else
goto corrupted;
if(condition)//needed data for reading format exist
goto showimage;
else
goto corrupted;
if(condition)//body contents of format are standard
goto showimage;
else
goto corrupted;

showimage:
/*Codes for reading image file to show on monitor
.
.
*/
corrupted:
//error to show file is corrupted
}

در خطوط بالا که شبه‌کد هستند به صورت نمادین و سمبلیک نمایش داده‌ایم که تابعی به نام reading به معنی خواندن تعریف شده‌است که بررسی می‌کند هدر و سرآیند فایل گرافیکی وجود دارد یا خیر . اگر وجود دارد با دستور goto برود به برچسب showimage که کدهایی دارد که فایل گرافیکی را بر روی مانیتور نمایش می‌دهد و در غیر این صورت به برچسب corrupted می‌رود که چاپ می‌کند فایل شما فاسد است . دوباره با دستورهای شرطی بررسی کرده‌ایم که داده‌های مورد نیاز برای تعیین بدنه فایل ( مثل ابعاد عکس ، تعداد تنوع رنگ‌ها برای هر پیکسل Color Depth و ... ) وجود دارد یا نه . اگر وجود داشت می‌رود به showimage و اگر نه می‌رود به corrupted و در نهایت بدنه فایل گرافیکی بررسی می‌شود که طبق استاندارد و داده‌های فایل است یا خیر . اگر بود می‌رود به showimage و اگر نه می‌رود به برچسب corrupted و در نهایت در پایان در برچسب showimage یک بار کدهای نمایش فایل گرافیکی را می‌نویسیم و در برچسب corrupted فقط می‌نویسیم فایل شما فاسد است


یادداشت : از اینکه وارد مطالب حاشیه‌ای شدیم و به مباحث تکنیکی پرداختیم پوزش می‌طلبیم ولی این حداقل مطالبی بود که باید بیان می‌شد تا بتوانیم استفاده متداول از دستور goto در برنامه‌های نوشته شده به C را نمایش دهیم . ضمن اینکه زمانی که شما بخواهید وارد برنامه‌نویسی و مهندسی نرم‌افزار شوید خود به خود ناچارید تمام این مطالب را فرا بگیرید ( نمونه‌های زیادی از این دست وجود دارند مثل پروزه Image Magic و یا mplayer که کاربرد گسترده‌ای نیز دارند ) از سویی دیگر پوزش می‌طلبیم که برنامه‌ای کامل به زبان C را به عنوان نمونه ننوشتیم ؛ چرا که ماکروها و تابع‌های کتابخانه‌ای بسیاری که هنوز به آنها اشاره‌ای هم نشده است ، هستند که باید مورد استفاده قرار بگیرند و باید برنامه مفصلی می‌نوشتیم تا بتواند فایلی مثل بیت‌مپ را که یکی از ساده‌ترین فرمت‌های رستر است را هندل کند که هنوز به آنها نرسیده‌ایم و در آن صورت مجبور بودیم مطالب بسیار بیشتری که ما را بسیار بیشتر به حاشیه می‌راند مطرح کرده و تشریح کنیم . اما بعد از بررسی تمام زبان C ، مثال‌های کاربردی زیادی را خواهیم نوشت تا شما بتوانید به راحتی هر برنامه‌ای که مد نظرتان بود بنویسید