خوش آموز درخت تو گر بار دانش بگیرد، به زیر آوری چرخ نیلوفری را


آموزش زبان ++C : مروری بر توابع و مقدار بازگشتی توابع (functions and return values)

آموزش زبان ++C : مروری بر توابع و مقدار بازگشتی توابع (functions and return values)
نویسنده : امیر انصاری
یک تابع (function) یک توالی از بیانیه ها (statements) می باشد که به منظور انجام کاری خاص طراحی شده است و قابلیت استفاده مجدد دارد. شما تا الآن دانسته اید که هر برنامه ++C الزاماً باید یک تابع با نام main داشته باشد که اجرای برنامه از اولین بیانیه (statement) در تابع main آغاز خواهد شد. با این حال، اکثر برنامه ها از توابع بسیاری استفاده می کنند.

سیستم یکپارچۀ سازمانی راهکار



اغلب، برنامه های شما نیاز دارند تا در کار جاری شان وقفه ای ایجاد کنند و به صورت موقتی کار دیگری را انجام بدهند. شما در زندگی واقعی نیز همیشه این کار را انجام می دهید. به عنوان مثال، ممکن است مشغول خواندن یک کتاب باشید و یک مرتبه بیاد بیاورید که باید یک تماس تلفنی با شخصی برقرار سازید. در این موقع شما یک نشانه (bookmark) در وسط کتاب قرار می دهید، تماس تلفنی تان را برقرار می سازید و بعد از تماس مجدداً به کتاب بر می گردید و از همانجایی که نشان گذاری کرده بودید مطالعه را ادامه می دهید.

برنامه های ++C نیز به شیوه مشابه ای همین کار را انجام می دهند. یک برنامه بیانیه ها (statements) را به ترتیب انجام می دهد تا اینکه به یک فراخوانی تابع (function call) می رسد. یک فراخوانی تابع عبارتی است که به پردازنده (CPU) می گوید، تابع جاری را متوقف کند و به اجرای تابع بعدی بپردازد. پردازنده (CPU) یک نشانه (bookmark) در نقطه فراخوانی تابع قرار می دهد، و سپس تابعی را که نامش در آن بیانیه قرار گرفته است فراخوانی کرده و اجرا می کند. وقتی که تابع فراخوانی شده به صورت کامل اجرا شد، پردازنده (CPU) به نقطه ای که در آنجا نشانه گذاری کرده بود باز می گردد و ادامه بیانیه ها (statements) را اجرا می کند.

تابعی که تابع دیگری را فراخوانی می کند "صدا زننده" (caller) نامیده می شود و تابعی که فراخوانی می شود "صدا شونده" (callee) نامیده می شود.

در اینجا یک برنامه نمونه قرار دارد که چگونگی معرفی یک تابع و همینطور چگونگی فراخوانی تابع را به شما نشان می دهد :

#include "iostream"

// Definition of function doPrint()
void doPrint()
{
std::cout << "In doPrint()" << std::endl;
}

// Definition of function main()
int main()
{
std::cout << "Starting main()" << std::endl;
doPrint(); // Interrupt main() by making a function call to doPrint(). main() is the caller.
std::cout << "Ending main()" << std::endl;

std::cin.get();
return 0;
}

این برنامه خروجی های زیر را تولید می کند :

Starting main()
In doPrint()
Ending main()

اجرای این برنامه مانند همه برنامه های دیگر ++C از تابع main آغاز می شود. اولین بیانیه از تابع main متن زیر را چاپ میکند.

Starting main()

در دومین بیانیه از تابع main تابع دیگری با نام doPrint فراخوانی می شود. در این مرحله اجرای تابع main موقتاً متوقف می شود و برنامه به تابع doPrint جهش می کند. در بدنه تابع doPrint تنها یک بیانیه وجود دارد و آن بیانیه هم متن زیر را چاپ می کند.

In doPrint()

هنگامی که اجرای تابع doPrint خاتمه می یابد، اجرای برنامه مجددا به تابع main باز می گردد و ادامه دستورات انجام می شوند. بیانیه بعدی در تابع main متن زیر را چاپ می کند.

Ending main()


توجه داشته باشید که فراخوانی یک تابع با قرار دادن نام تابع و یک جفت پرانتز باز و بسته بعد از آن صورت می پذیرد. در مورد این پرانتز ها در درس بعد صحبت خواهیم کرد. در حال حاضر، لازم است بدانید که اگر این یک جفت پرانتز باز و بسته را قرار ندهید، برنامه شما کامپایل نخواهد شد و طبیعتاً تابع مربوطه نیز فراخوانی نخواهد گردید.

قانون مهم : وقتی توابع را فراخوانی می کنید پرانتزهای باز و بسته () را فراموش نکنید.

مقادیر بازگشتی (Return values)


وقتی اجرای تابع main خاتمه می یابد، یک مقدار عددی از نوع integer (که معمولاً هم صفر می باشد) به سیستم عامل باز می گرداند. این کار توسط بیانیه return صورت می پذیرد.

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

سپس، در داخل بدنه تابع (function)، ما با استفاده از بیانیه return تعیین خواهیم کرد که تابع مربوطه چه مقدار خاصی را به تابع فراخوانی کننده اش (caller) باز گرداند. مقدار واقعی بازگشت داده شده توسط یک تابع را مقدار بازگشتی (return value) می نامند.

بیایید نگاهی به تابع ساده زیر بیندازیم، در این مثال تابع ما یک مقدار از نوع integer باز می گرداند :

#include "iostream"

// int means the function returns an integer value to the caller
int return5()
{
// this function returns an integer, so a return statement is needed
return 5; // we're going to return integer value 5 back to the caller of this function
}

int main()
{
std::cout << return5() << std::endl; // prints 5
std::cout << return5() + 2 << std::endl; // prints 7

return5(); // okay: the value 5 is returned, but is ignored since main() doesn't do anything with it

std::cin.get();
return 0;
}

در اولین مرتبه ای که تابع return5 فراخوانی می شود، تابع مربوطه مقدار 5 را به تابع فراخوانی کننده (caller) باز می گرداند، که این مقدار هم توسط شیء std::cout چاپ خواهد شد.

در دومین مرتبه ای که تابع return5 فراخوانی می شود، تابع مربوطه مقدار 5 را به تابع فراخوانی کننده (caller) باز می گرداند و در عبارت (expression) موجود در فراخوانی کننده این عدد با عدد 2 جمع می شود و در نتیجه مقدار 7 در صفحه چاپ خواهد شد.

در سومین مرتبه ای که تابع return5 فراخوانی می شود، تابع مربوطه مقدار 5 را به تابع فراخوانی کننده (caller) باز می گرداند. با این حال، در تابع main اتفاقی نمی افتد، چرا که این مقدار در صفحه چاپ نمی شود و اساساً کاری با آن صورت نمی پذیرد.

توجه : مقادیر بازگشتی تا زمانی که با استفاده از شیء std::cout چاپ نشوند، در خروجی صفحه نمایش ظاهر نخواهند شد. در این مثال هم در سومین مرتبه فراخوانی این اتفاق افتاده است و در نتیجه هیچ مقداری چاپ نمی شود.

مقادیر بازگشتی از نوع void


توابع الزامی ندارند تا حتماً مقداری را بازگردانند. برای اینکه به کامپایلر بگوییم، یک تابع (function) بدون مقدار بازگشتی میباشد از نوع void استفاده می شود. بیایید مجدداً نگاهی به تابع doPrint که در ابتدای این فصل نوشتیم بیندازیم.

void doPrint() // void is the return type
{
std::cout << "In doPrint()" << std::endl;
// This function does not return a value so no return statement is needed
}

این تابع یک خروجی از نوع void دارد، نوع void به این معنا می باشد که تابع مربوطه به تابع فراخوانی کننده آن مقداری را باز نخواهد گرداند. از آنجا که توابع از نوع void مقدار بازگشتی ندارند، نیازی هم به بیانیه return نخواهند داشت.

در اینجا یک مثال دیگر از تابع با خروجی از نوع void آورده ایم :

#include "iostream"

// void means the function does not return a value to the caller
void returnNothing()
{
std::cout << "Hi" << std::endl;
// This function does not return a value so no return statement is needed
}

int main()
{
returnNothing(); // okay: function returnNothing() is called, no value is returned
std::cout << returnNothing(); // error: this line will not compile. You'll need to comment it out to continue.

std::cin.get();
return 0;
}

در اولین مرتبه فراخوانی تابع returnNothing، تابع مربوطه متن “Hi” را چاپ می کند و هیچ مقداری را به تابع فراخوانی کننده (caller) باز نخواهد گرداند. کنترل اجرای برنامه مجدداً به تابع main باز می گردد و برنامه ادامه پیدا می کند.

دومبن فراخوانی تابع returnNothing اساساً کامپایل نحواهد شد. تابع returnNothing مقدار void را که به معنای بدون مقدار بازگشتی می باشد، بر می گرداند. با این حال تابع main سعی خواهد کرد تا این مقدار nothing (هیچ چیز) را به شیء std::cout جهت چاپ ارسال کند. شیء std::cout نمی تواند مقدار nothing (هیچ چیز) را مدیریت کند، برای همین کامپایلر این خط از کد را به عنوان یک خطا شناسایی خواهد کرد. برای اینکه برنامه شما اجرا شود لازم است تا این خط را کامنت کنید.

به خاطر داشته باشید که در مواقعی که نوع خروجی تابع از نوع void باشد، تنهای کاری که می توانید در ارتباط با آن تابع انجام بدهید اینست که خروجی آن را نادیده بگیرید.

بازگشت به تابع main


در حال حاضر شما از نظر مفهومی می دانید که در واقع تابع main چگونه کار می کند. وقتی برنامه شما اجرا می شود، سیستم عامل تابع main را فراخوانی می کند. اجرای برنامه از اولین بیانیه در تابع main آغاز می گردد. بیانیه های (statements) موجود در تابع main به ترتیب اجرا می شوند. در نهایت تابع main مقداری را به سیستم عامل باز می گرداند که معمولاً این مقدار عدد 0 می باشد. به همین دلیل است که تابع main به صورت int main معرفی می گردد، چرا که نوع خروجی آن عدد و از نوع integer می باشد.

چرا مقداری به سیستم عامل باز گردانده می شود؟ این مقدار کد وضعیت (status code) نامیده می شود، و به سیستم عامل یا هر برنامه دیگری که برنامه شما را فراخوانی می کند، اطلاع می دهد که برنامه به درستی اجرا شده است یا خیر. به صورت توافقی معنای عدد 0 این می باشد که برنامه با موفقیت اجرا شده است و هر عددی غیر از صفر به معنای وقوع یک خطا در اجرای برنامه می باشد.

توجه داشته باشید که استاندارد ++C واضحاً بیان می دارد که نوع خروجی تابع main الزاماً باید از نوع int باشد. با این حال، اگر مقدار 0 را در تابع main باز نگردانید، کامپایلر از طرف شما مقدار 0 را باز خواهد گرداند. با این ترتیب، بهتر است که واضحاً مقدار 0 را در تابع main باز گردانید، هم به خاطر اینکه قصد خود را نشان دهید و هم به این خاطر که با سایر توابع سازگاری داشته باشد، چرا که در سایر توابع شما الزام دارید که مقدار بازگشتی را برگردانید.

در حال حاضر، شما الزاماً باید تابع main خود را بعد از همه توابع دیگر معرفی کنید. البته در درس های آینده در این مورد بیشتر صحبت خواهیم کرد.

چند نکته دیگر در مورد مقادیر بازگشتی (return values)


اولین نکته اینست که اگر تابعی نوع بازگشتی اش void نباشد، باید واضحا با یک بیانیه return مقدار بازگشتی تابع با توجه به نوع آن، بازگردانده شود. تنها استثناء از این قاعده تابع main می باشد که در صورتی که بیانیه return نداشته باشد، کامپایلر به صورت اتوماتیک عدد 0 را خودش باز خواهد گرداند.

نکته دوم اینست که در هر تابع، به محض اینکه اجرای برنامه به بیانیه return برسد، مقدار بازگشتی تابع به تابع فراخوانی کننده (caller) برگردانده می شود و سایر کدهای موجود در تابع که بعد از بیانیه return باشند، توسط کامپایلر نادیده گرفته شده و اجرا نخواهند شد.

یک تابع، تنها می تواند یک مقدار واحد را به تابع فراخوانی کننده اش باز گرداند. با این وجود، تابع مربوطه ممکن است از هر منطقی برای تعیین مقدار بازگشتی اش استفاده نماید. ممکن است صرفاً یک عدد برگردانده شود، مانند عدد 5. ممکن است مقدار یک متغیر باز گردانده شود و یا حتی یک عبارت باز گردانده شود که آن عبارت به یک مقدار واحد قابل ارزیابی باشد. یا ممکن است یک مقدار واحد را از مجموعه یک سری مقادیر ممکن، برگرداند که درنهایت باز هم یک مقدار واحد باز گردانده خواهد شد.

در مورد اینکه تابع فقط یک مقدار واحد را باز خواهد گرداند، موارد دیگری نیز قابل ذکر می باشند که در درس های پیشرفته تر همین دوره آموزشی به صورت عمیقتر مورد بحث قرار خواهیم داد.

در نهایت توجه داشته باشید که هر تابعی در تعریف معنای مقدار بازگشتی اش آزاد می باشد. برخی توابع مقدار بازگشتی شان را به صورت کد وضعیت (status code) معرفی می کنند که نمایانگر موفقیت یا شکست اجرای تابع می باشد. توابع دیگری یک مقدار محاسبه شده یا یک مقدار انتخاب شده را باز می گردانند. توابع دیگری مقدار nothing (هیچ چیز) را باز می گردانند. در مجموع آنچیزی را که یک تابع باز می گرداند و معنا و مفهوم مقدار بازگشتی آن توسط برنامه نویسی که آن تابع را نوشته است، تعیین خواهد شد. به دلیل همین گستردگی دامنه مقادیر بازگشتی و گستردگی معنا و مفهوم آنها، ایده خوبی است که در هر تابع کامنت هایی مبنا بر مقادیر خروجی و معنا و مفهوم هر کدام از مقادیر خروجی ممکن ذکر کنید.

استفاده مجدد از توابع (Reusing functions)


یک تابع را می توان بارها فراخوانی کرد، این مساله در مواقعی که می خواهید یک کار مشخص را بیش از یک بار انجام بدهید بسیار مفید است.

#include "iostream"

// getValueFromUser will read a value in from the user, and return it to the caller
int getValueFromUser()
{
std::cout << "Enter an integer: "; // ask user for an integer
int a; // allocate a variable to hold the user input
std::cin >> a; // get user input from console and store in variable a
return a; // return this value to the function's caller (main)
}

int main()
{
int x = getValueFromUser(); // first call to getValueFromUser
int y = getValueFromUser(); // second call to getValueFromUser

std::cout << x << " + " << y << " = " << x + y << std::endl;

std::cin.get();
std::cin.get();
return 0;
}

این برنامه خروجی زیر را تولید خواهد کرد :

Enter an integer: 5
Enter an integer: 7
5 + 7 = 12

در این مورد، تابع main دو مرتبه متوقف می شود، در واقع هر مرتبه به ازاء فراخوانی تابع getValueFromUser یک بار تابع main متوقف می گردد. توجه داشته باشید که در هر مورد، مقداری که در متغیر a که در تابع getValueFromUser ذخیره می شود، به تابع فراخوانی کننده (caller) آن که تابع main می باشد، باز گردانده می شود. این مقدار یک بار به متغیر x و بار دیگر به متغیر y که در تابع main معرفی شده اند، منتسب خواهد شد.

توجه داشته باشید که تنها تابع main نیست که می تواند سایر توابع را فراخوانی کند. هر تابعی می تواند توابع دیگر را فراخوانی نماید.

#include "iostream"

void printA()
{
std::cout << "A" << std::endl;
}

void printB()
{
std::cout << "B" << std::endl;
}

// function printAB() calls both printA() and printB()
void printAB()
{
printA();
printB();
}

// Definition of main()
int main()
{
std::cout << "Starting main()" << std::endl;
printAB();
std::cout << "Ending main()" << std::endl;

std::cin.get();
return 0;
}

این برنامه خروجی زیر را تولید خواهد کرد :

Starting main()
A
B
Ending main()

توابع تو در تو (Nested functions)


در زبان برنامه نویسی ++C نمی توان توابع را در داخل توابع دیگر معرفی کرد. در واقع زبان ++C امکان معرفی توابع به صورت تو در تو (Nested) را نمی دهد. از این رو برنامه زیر از نظر ++C غیر قانونی می باشند :

#include "iostream"

int main()
{
int foo() // this function is nested inside main(), which is illegal.
{
std::cout << "foo!" << std::endl;
return 0;
}
foo();

std::cin.get();
return 0;
}

روش مناسب و صحیح برای نوشتن برنامه بالا به شکل زیر می باشد :

#include "iostream"

int foo() // no longer inside of main()
{
std::cout << "foo!" << std::endl;
return 0;
}

int main()
{
foo();

std::cin.get();
return 0;
}

برای مشاهده فهرست آموزش های این دوره آموزشی بر روی لینک زیر کلیک کنید :


آموزش قبلی : آموزش زبان ++C : مروری بر دستورات cout، cin و endl

آموزش بعدی : آموزش زبان ++C : مروری بر پارامترهای توابع و آرگومان ها (function parameters and arguments)



نمایش دیدگاه ها (0 دیدگاه)

دیدگاه خود را ثبت کنید:

انتخاب تصویر ویرایش حذف
توجه! حداکثر حجم مجاز برای تصویر 500 کیلوبایت می باشد.