زبان برنامه نویسی سی/دستور return
دستور return
[ویرایش]دستور return با معنی بازگرداندن به کامپایلر در کامپایل فایل برنامه سی ( c. ) تفهیم میکند که برگردان ؛ یعنی مقداری را که تابع باید بازگرداند ، تعیین میکند ؛ تا به عبارت دیگر ، تابع ، مقداری را به عنوان خروجی ، با دستور return بازگرداند ( و یا به عبارت دیگر ، تحویل دهد ) . فرم کلی استفاده از آن به شکل :
return value;
یا به شکل :
return expression;
میباشد ؛ که در فرمِ اول ، به جای value که معنی مقدار را میدهد ، میتوان یک مقدار عددی یا کاراکتری را نوشت ( که میتواند یک کاراکتر یا یک رشته باشد و یا یک عدد یک رقمی یا چند رفمی و یا حتی عدد اعشاری باشد ) تا به عنوان خروجی تابع ، بازگردانده شود . اما در فرم دومِ استفاده از دستور return به جای مقدار ، یک عبارت را که خود میتواند از چند زیر عبارت تشکیل شده باشد ، در برابر دستور return مینویسیم ، تا در تابع ، ارزیابی شود و سپس مقدارِ به دست آمده به عنوان خروجیِ تابع ، برگردانده شود ( که بهتر است و معمولاً زیر عبارتها داخل جفت پرانتزهای باز و بسته نوشته میشوند )
پیش از این چند بار به مفهوم تابع اشاره کردهایم و به صورت مختصر آن را تعریف کرده و به مباحث آن اشاراتی کردهایم . اما در اینجا نیز یک بار دیگر و به صورت کاملتر اشاره میکنیم ؛ اما فصل بعدی ، فصل تابع است که به صورت کامل و دقیق ، تمام مباحث تابع را که یک عنصر اصلی ، در زبانهای نویسیای همچون C است ، تشریح و تفسیر میکنیم . علت این مسئله نیز کاربرد کلیدواژه دستوری return است که مختص تابع است و تنها داخل بدنه تابع ، قابل استفاده است که در صورت نوشته شدن و اجرا ، بلافاصله مابقی کدهای داخل بلوک تابع نادیده گرفته میشوند و تابع ، خروجی تعیین شده توسط دستور return را بازمیگرداند
تابع ( به انگلیسی Function ) شبیه تابع در ریاضی است ؛ تابع در برنامهنویسی ، داده یا دادههایی را به عنوان ورودی می تواند دریافت کند ( که با عنوان پارامتر Parameter تعریف میشوند و سپس دادههایی به تابع به جای پارامترهای تابع فرستاده میشود که آنها آرگومان Argument میگوئیم ) و سپس معمولاً بر روی آنها پردازش انجام میدهد و داده یا دادههایی را به عنوان خروجی ، تحویل میدهد . تقریباً تمام زبانهای برنامهنویسی قابلیت تعریف کردن تابع و استفاده از تابع را دارند و زبان C و خانواده آن و تقریباً تمام زبانهایی که از سی الگو گرفتهاند تابعگرا هستند تا جایی که حتماً باید تابعی به عنوان تابع اصلی برنامه ؛ ورودیها و خروجیهای برنامه نوشته شده را کنترل کند
در برنامهنویسی معمولی ( یعنی سطح بالا ) تحت سیستمعامل و کرنل Kernel ، به این تابع main یعنی main function ( به معنی تابع اصلی ) گفته میشود که برای خروجی آن باید نوشت
;return 0
تا با تفهیم اینکه برنامه و تابع با موفقیت به پایان رسیده است ، منابع سیستمعامل و کامپیوتر را که اشغال کرده است ، آزاد کند ؛ در غیر این صورت برنامه بعد از اجرا و پایان ، همچنان به عنوان پردازه Process در سیستمعامل ، باقی میماند و منابع کامپیوتر را اشغال میکند ؛ البته برخی سیستمعاملها مثل لینوکس Linux به قدری قدرتمند هستند که برنامههای بیمصرف و اضافه را شناسایی کرده و حذف میکنند . البته در برنامهنویسی سطح پائین مانند نوشتن کامپایلر ( Compiler ) و یا میانافزار ( Firmware ) و کرنل ( Kernel ) شما باید تابعی را به عنوان تابع اصلی برنامه ، تعریف کنید و یا به شکل خاصی از تابع main استفاده کنید تا ورودیها و خروجیهای برنامه را کنترل کند و البته با خواندن راهنمای کامپایلر خود ، با دادن دستورات ویژهای به کامپایلر ، تابع اصلی را تعینن کنید و برنامه خود را برای سیستمعاملی ؛ و یا زبان ماشین ترجمه کنید ( که البته معمولاً و باید برای سیستم عامل هم تحت کرنل به کدهای خوانا برای آن کرنل ، ترجمه کند تا برنامهنویسها بتوانند برای سیستم عامل ، برنامههای مورد نیاز را بنویسند ) . در زبان C تمام کارهای شما توسط تابعها انجام میشوند ، شما نمیتوانید از دستورهای شرطی و یا حلقهها خارج از تابعها استفاده کنید و اگر استفاده کنید برنامه شما دارای خطا یا همان باگ ( Bug ) خواهد شد
به غیر از تابع main هر تابعی باید قبل از استفاده ، تعریف شود ( definition ) تا تعیین کند که تابع چه ورودیهایی میگیرد که برای تابع به عنوان پارامتر ( parameter ) با یک شناسه ، در مقابل شناسه و نام تابع ، داخل یک جفت پرانتز باز و بسته نوشته میشوند ( مثل int func1(int a, int b) که در اینجا func1 نام و شناسه تابع است که از نوع صحیح int میباشد و دو پارامتر صحیح با نامهای a و b دارد ) که پارامترها را نیز با عملگر کاما ( در پارسی به معنی ویرگول ) باید از یکدیگر جدا کنید و سپس باید با یک جفت آکولاد باز و بسته ( یعنی {} ) که بلوک تابع است اما به عنوان بدنه تابع تعریف میشود ( Function Body ) معمولاً پردازشهایی را بر روی پارامترها انجام میدهید و در نهایت با دستور return داخل بدنه تابع مشخص میکنید که تابع چه خروجیای میدهد
تابع ، زمانی که تعریف شد باید همانند متغیرها پس از تعریف ، و هنگام استفاده بدون نوع داده نوشته شود و مورد استفاده قرار گیرد ، که به این کار فراخوانی call تابع و یا احضار invoke آن میگوئیم و اگر پارامتری دارد ، داده یا دادههایی به تعداد پارامترهای تابع که به آن اصطلاحاً پاس داده شوند ( pass به پارسی ، فرستاده شوند ) یا به اصطلاحی دیگر ارجاع داده شوند ( referencing ) تا تابع همان طور که تعریف شده پردازشهایی را بر روی آن دادهها که در مقابل تابع هنگام فراخوانی نوشته شدهاند ( که به آن دادهها به اضطلاح آرگومان Argument میگوییم ) انجام دهد و خروجیای را تحویل دهد و یا آن را در مقابل یک متغیر یا داده دیگر فراخوانی یا احضار کنید تا خروجی تابع در آن ذخیره شود که سپس میتوانید داده دریافت شده و ذخیره شده در متغیر یا داده خود را مورد پردازشهای دیگر قرار دهید و یا اصلاً خود تابع را به عنوان آرگومان به یک تابع دیگر پاس بدهید ( و این از قدرتهای زبان C است ) . در تعریف تابع ، خروجی را با دستور return تعیین میکنیم . اما گاهی تابع را در جایی دیگر تعریف میکنیم و پیش از تعریف تابع از آن استفاده کرده و به اصطلاح آن را فراخوانی و احضار میکنیم که در این صورت ، حتماً باید پیش از استفاده ؛ تابع را اعلان و به اصطلاح prototype کنیم که نوع داده تابع و پارامترهای آن ، یک به یک و به ترتیب باید با تعریف آن ( definition ) یکسان باشند ( در غیر این صورت کامپایلر از شما خطا خواهد گرفت و error میدهد ) . ضمن اینکه تابعها حوزه سراسری دارند و میتوانید یک تابع را داخل تابعی دیگر احضار و فراخوانی کنید ولی نمیتوانید تابعی را داخل تابعی دیگر اعلان یا تعریف کنید ( به غیر از کامپایلر GCC که این امکان را می دهد ولی بخشی از استاندارد نیست و مغایر با آن است )
تابع میتواند پارامتری نداشته باشد ؛ که در این صورت مجاز نیستیم تا دادهای را به عنوان آرگومان به تابع پاس بدهیم یا به پارسی ، بفرستیم ؛ در غیر این صورت ، کامپایلر از ما خطا خواهد گرفت . ضمن اینکه گاهی برای جلوگیری از کد زدن اضافه ، مجموعهای از حکمها را داخل تابع مینویسیم تا با هر بار احضار آن تابع ، آن حکمها را به اجرا بگذارد . دقت کنید تعداد آرگومانهایی که به تابع پاس داده میشوند و نوع داده آنها حتماً باید با نوع داده در تعریف پارامترهای تابع و به ترتیبی که تعریف شدهاند و تعداد آنها یکی باشد ؛ در غیر این صورت ، کامپایلر از شما خطا خواهد گرفت ( که البته شما میتوانید پارامترهای تابع را طوری تعریف کنید که تابع ، بیشمار آرگومان پذیرا باشد که در فصلهای بعدی به آن میپردازیم و نیازمند استفاده از فایل سرآیند مرتبط به خود است که در مباحث مربوطه ، مفصلاً به آنها خواهیم پرداخت )
طبق استاندارد ، نوعی که دستور return باز میگرداند ( مثلاً نوع داده صحیح int ) باید با نوع داده تعریف تابع ( مثل int یا long یا double ) یکی باشد در غیر این صورت ممکن است با خطای کامپایلر مواجه شوید ولی برخی از کامپایلرها نوع دادهها را تبدیل و cast میکنند ( مراجعه کنید به تبدیل و جایگزینی دادهها ) و یا در صورت همخوانی نوع دادهها ولی بزرگتر بودن مقدار خروجی از نوع داده تابع ، بزرگترین مقداری که در آن نوع داده تابع ، قابل ذخیره است به عنوان مقدار خروجی ، تعیین میکنند ( مثل کامپایلر GCC ) ضمن اینکه شما با نوشتن کدهایی میتوانید از دادههای خیلی بزرگ هم استفاده کنید که برنامههای نوشته شده و آماده آن نیز در فضای وب موجود است
همچنین اگر چند بار دستور return را داخل بدنه تابع خود بنویسید ( یعنی بین آکولاد باز و قبل از آکولاد بسته ) ، تنها اولین دستور return مقدار حروجی تابع را تعیین خواهد کرد ؛ مگر آنکه با دستورهای شرطی ، خروجیهای مختلفی را برای تابع خود تعیین کنید و در نهایت در هر شرط از دستور return با یک مقدار یا یک عبارت ، خروجی تابع را تعیین کنید که در صورت صدق هر شرط ، خروجی داخل همان شرط ، خروجی تابع خواهد بود
دقت کنید : نوع داده پوچ void ، همان طور که در مبحث خودِ آن نیز گفته شد میتواند تابع را هم تعریف کند ، اما تابعهای پوچ قادر به بازگرداندن هیچ مقداری نیستند ، بنابراین هر گونه دستور return داخل تابع پوچ که بخواهد مقداری را بازگرداند خطا است و شما مجاز به بازگرداندن مقداری در تابع پوچ نیستید و در صورت نوشتن چنین کدی ، کامپایلر از شما خطا خواهد خواهد گرفت . شما میتوانید در تابع پوچ بنویسید : ;return که نوع نوشتن خوشفرم آن نیز هست و شما نیز سعی کنید همیشه استاندارد و خوشفرم بنویسید
دقت کنید : هر تابعی با نوع دادهای غیر از نوع داده پوچ یعنی void ، مثل نوع int یا long یا char باید طبق استاندارد ، حتماً دارای دستور return باشد و در غیر این صورت کامپایلر ممکن است از شما خطا بگیرد و در صورت خطا نگرفتن نیز ، قطع به یقین برنامه شما دارای خطا و باگ Bug خواهد بود و مشکلدار کار میکند
مثال :
#include <stdio.h>
int mult(int a, int b);
int main()
{
printf("Enter Two Numbers :\n");
int i = 0, j = 0;
scanf("%d%d", &i, &j);
printf("%d\n", mult(i, j));
return 0;
}
int mult(int a, int b)
{
return (a * b);
}
در مثال بالا ، ابتدا فایل سرآیند stdio ( که سرنام standard input/output میباشد - به معنی ورودی/خروجی استاندارد ) با دستور مستقیم include ضمیمه برنامه خود نمودهایم که در آن تابعهای printf و scanf تعریف شدهاند که این دو تابع کتابخانهای استاندارد C ، به ترتیب printf ( سرنام print formatted به معنی چاپ فرمتشده ) کاراکتر یا رشتهای از حروف ، علامات و یا اعداد را در خروجی خطدستوری چاپ میکند و scanf ( سرنام scan formatted به معنی خواندن فرمتشده ) تابعی است که از ورودی تعریف شده برای خود و معمولاً صفحهکلید ( Keyboard کیبورد ) کاراکتر یا رشتهای را که حروف ، علامات و یا اعداد هستند دریافت میکند
سپس تابعی با نام mult مخفف multiplication به معنی ضرب ( در ریاضی ) را اعلان نمودهایم ( اصطلاحاً prototype ) که دو پارامتر با نامهای a و b دارد که هر دو از نوع داده متغیر صحیح هستند int . تابع اصلی برنامه یعنی تابع main چاپ میکند که دو عدد را وارد کنید ( Enter Two numbers ) سپس دو متغیر صحیح با نامهای i و j هر دو را با مقدار 0 تعریف میکند که درست بعد از آن ، تابع scanf ، مقادیر آنها را دریافت میکند ( این مباحث را به صورت مفصل در فصل فایلهای سرآیند تشریح میکنیم ) . سپس تابع printf یک مقدار صحیح را چاپ میکند که مقدار آن از فراخوانی ( و یا همان احضار ) تابع mult بر روی آرگومانهای i و j که اعدادی هستند که توسط کاربر وارد شدهاند ، به دست میآید
اما تابع mult در جایی دیگر تعریف شده است ( یعنی بعد از تابع main ) ؛ درست همانند اعلان آن ، یک داده صحیح است و دو متغیر صحیح با نامهای a و b به عنوان پارامتر دارد که با دستور return داخل بلوک تابع mult که به عنوان بدنه تابع تعریف میشود ( با یک جفت آکولاد باز و بسته ) دو متغیر a و b در هم ضرب میشوند و به عنوان خروجی ، ضرب دو پارامتر در یکدیگر تحویل داده میشود . در ادامه هر جا تابع mult احضار شود و دو مقدار ( عدد صحیح و یا متغیری که مقدار صحیح در خود دارد ) به آن فرستاده شوند ( پاس داده شوند ) حاصلضرب آن دو به عنوان خروجی ، تحویل داده خواهد شد . بدیهی است که با کامپایل برنامه ، شما دو عدد را که قابلیت ذخیره در یک متغیر صحیح را داشته باشند میتوانید وارد کنید تا ضرب آن دو را در هم ، در خروجی خطدستوری ببینید ( به شرط اینکه ضرب آن دو عدد از آخرین مقداری که در نوع داده int قابل ذخیره است بیشتر نباشد ؛ در غیر این صورت آخرین مقدار قابل ذخیره در صحیح ، در خروجی خطدستوری نمایش داده خواهد شد و یا با error در هنگام اجرای برنامه رو به رو خواهید شد )
مثال :
#include <stdio.h>
void message(void)
{
printf("Assume a natural number in Your mind!\n");
printf("Multily Your number by 2 and Add 8 to it!\n");
return;
}
int calculation(int a);
int main()
{
int number = 0;
message();
scanf("%d", &number);
printf("%d\n", calculation(number));
return 0;
}
int calculation(int a)
{
a = ((a/2)-4);
return a;
}
توضیح : این یک بازی ریاضی است که شما از کاربر میخواهید یک عدد طبیعی را در ذهن خود در نظر بگیرد و شما آن را پیدا میکنید
تشریح ریاضی : عدد فرضی در ذهن کاربر x است . از او میخواهیم که آن را در ۲ ضرب کند که میشود 2x و سپس آن را با ۸ جمع کند که میشود y و آن را کاربر به ما میگوید . حالا ما برای به دست آوردن x عبارت جبری را حل میکنیم . 2x + 8 = y اگر بخواهیم x را پیدا کنیم ( y را که کاربر به ما میگوید ) جمله 2x را نصف میکنیم ( تقسیم بر ۲ میکنیم ) که میشود x که ما به دنبال آن هستیم و جمله 8 نیز طبعاً باید نصف شود که میشود ۴ و از دو طرف معادله ۴ واحد کم میکنیم که میشود x = y/2 - 4 پس هر عددی کاربر وارد کرد ؛ آن را تقسیم بر ۲ میکنیم و سپس ۴ واحد از آن کم میکنیم و این همان عددی است که کاربر در ذهن خود در نظر گرفته است
تشریح برنامه :
پس از ضمیمه نمودن stdio ، یک تابع با نوع داده پوچ ( که نمیتواند مقداری بازگرداند ) تعریف کردهایم که پارامتری نیز ندارد ( که به صورت استاندارد به جای خالی گذاشتن آن ، نوشتهایم void که یعنی تابع ، پارامتری ندارد ) که طبعاً مجاز به پاس دادن دادهای به عنوان آرگومان به تابع نخواهیم بود . تابع message در بدنه خود ؛ دو پیغام در خروجی خطدستوری چاپ میکند که کاربر یک عدد طبیعی در ذهن خود در نظر بگیرد ؛ سپس آن را در ۲ ضرب کند و ۸ واحد به آن اضافه کند . در پایان نوشتهایم ;return که طبق استاندارد باید این دستور را بدهیم ( اما به هیچ وجه مجاز نیستیم تا مقدار یا عبارتی را در مقابل آن بنویسیم تا تابع بازگرداند - چون نوع تعریف تابع ، نوع داده پوچ است )
سپس تابعی با نوع داده صحیح و با شناسه calculation ( به معنی محاسبه ) اعلان نمودهایم که یک پارامتر از نوع داده صحیح و با شناسه a دارد که میتوانیم و باید یک داده صحیح را به عنوان آرگومان به آن پاس بدهیم . تعریف تابع در پایان برنامه و پس از تعریف تابع main یعنی تابع اصلی برنامه صورت پذیرفته است . تابع ، پارامتری با شناسه a دارد . پس هر داده صحیحی که به عنوان آرگومان به تابع calculation ، در هنگام احضار ، فرستاده شود در بدنه تابع مورد پردازش قرار میگیرد که بدین شرح است : مقدار عبارت سمت راست یعنی a ( که همان پارامتر تابع است و بعد از تعریف تابع و هنگام فراخوانی ، مقدار آرگومان خواهد بود ) همان طور که در فصل عملگرها و مخصوصاً مبحث اولویتها گفتیم ، مقدار ، تقسیم بر ۲ و سپس منهای ۴ شده که در واقع ارزیابی میگردد و سپس در متغیر a ذخیره میشود که این مقدار ، مقدار نهایی و مورد نیاز ما است ؛ در انتها با دستور return مقدار متغیر a بازگردانده میشود و به عبارت دیگر تحویل داده میشود ( که بدیهی است با احضار یا همان فراخوانی calculation و جایگزین نمودن - فرستادن - مقدار داده شده توسط کاربر ، پاسخ ، که عدد در نظر گرفته شده کاربر است بازگردانده خواهد شد )
در تابع اصلی برنامه یعنی تابع main یک متغیر از نوع صحیح با شناسه number به معنی عدد با مقدار 0 تعریف نمودهایم ( که محلی است ) ، سپس تابع message را احضار یا همان فراخوانی نمودهایم که پیغامهای داخل خود را چاپ خواهد نمود . سپس متغیر محلی number را توسط scanf از کاربر دریافت نموده و سپس توسط تابع کتابخانهای printf تعیین کردهایم که یک مقدار دهدهی چاپ کند که قرار است معین شود با چه دادهای جایگزین شود و خط شکسته شود ، در تابع printf کاراکترهای کنترل ( مثل d% ) دقیقاً به ترتیب و به تعداد ، بعد از عملگر کالن باید مشخص شوند ( در فصل فایلهای سرآیند مفصلاً این مباحث را تشریح خواهیم نمود ) برای چاپ عددی که قرار است جایگزین شود با احضار calculation ( به معنی پردازش ) که متغیر صحیح number را به عنوان آرگومان با آن پاس دادهایم یا به عبارتی فرستادهایم ( که البته number ، عددی است که کاربر وارد نموده است ) در نهایت ، عدد در نظر گرفته شده در ذهن کاربر محاسبه میشود و حالا آن را در خروجی خطدستوری چاپ میکنیم
در پایان با دستور ;0 return تابع mian را با موفقیت پایان میدهیم تا برنامه متوقف شود و منابع اشغال شده را آزاد کند