تابع در ++C

تابع در ++C

برنامه‌ها واقعی و تجاری بسيار بزرگ‌تر از برنامه‌هايی هستند که تاکنون بررسی کرديم. براي اين که برنامه‌هاي بزرگ قابل مديريت باشند، برنامه‌نويسان اين برنامه‌ها را به زيربرنامه‌هايي بخش‌بندي مي‌کنند. اين زيربرنامه‌ها تابع یا Function ناميده مي‌شوند. توابع را مي‌توان به طور جداگانه کامپايل و آزمايش نمود و در برنامه‌هاي مختلف دوباره از آن‌ها استفاده کرد.

رئوس مطالب این جلسه

  • توابع كتابخانه‌ای ++C استاندارد
  • توابع ساخت كاربر
  • برنامۀ آزمون
  • اعلان‌ها و تعاريف تابع
  • كامپايل جداگانۀ توابع
  • متغيرهاي محلی، توابع محلی
  • تابع void
  • توابع بولی
  • توابع ورودی/خروجی (I/O)
  • ارسال به طريق ارجاع (آدرس)
  • ارسال‌ از طريق‌ ارجاع‌ ثابت‌
  • توابع‌ بی‌واسطه

توابع كتابخانه‌اي ++C استاندارد

كتابخانۀ ++C استاندارد مجموعه‌ای است که شامل توابع‌ از پيش تعريف شده و ساير عناصر برنامه است‌. اين توابع و عناصر از طريق «سرفايل‌ها» یا هدرها قابل دستيابی‌اند. قبلا برخي از آن‌ها را استفاده كرده‌ايم‌: ثابت INT_MAX که در <climits> تعريف شده ، تابع ()sqrt که در <cmath> تعريف شده است و… .

تابع جذر ()sqrt

ريشۀ دوم يك عدد مثبت‌، جذر آن عدد است. تابع مانند يک برنامۀ کامل، داراي ‌روند ورودی – پردازش – خروجی است هرچند که پردازش، مرحله‌اي پنهان است. يعنی نمی‌دانيم که تابع روی عدد 2 چه اعمالی انجام می‌دهد که 41421/1 حاصل می‌شود.

مثال: برنامۀ سادۀ زير، تابع از پيش تعريف شدۀ جذر را به کار می‌گيرد:

براي اجراي يك تابع مانند تابع ()sqrt کافي است نام آن تابع به صورت يک متغير در دستورالعمل مورد نظر استفاده شود، مانند بالا. اين کار فراخوانی تابع يا احضار تابع گفته می‌شود. بنابراين وقتي كد (sqrt(x اجرا شود، تابع ()sqrt فراخوانی می‌گردد. عبارت x درون پرانتز آرگومان يا پارامتر واقعی فراخوانی ناميده می‌شود. در چنين حالتي می‌گوييم كه x توسط «مقدار» به تابع فرستاده می‌شود. لذا وقتي x=3 است، با اجراي کد (sqrt(x تابع ()sqrt فراخوانی شده و مقدار 3 به آن فرستاده می‌شود. تابع مذکور نيز حاصل 1.73205 را به عنوان پاسخ برمی‌گرداند. اين فرايند در نمودار زير نشان داده شده است.

تابع در ++C

 متغيرهای x و y در تابع ()main تعريف شده‌اند. مقدار x که برابر با 3 است به تابع ()sqrt فرستاده می‌شود و اين تابع مقدار 1.73205 را به تابع ()main برمی‌گرداند. جعبه‌ای كه تابع ()sqrt را نشان می‌دهد به رنگ تيره است، به اين معنا كه فرايند داخلی و نحوۀ کار آن قابل رويت نيست.

توابع مثلثاتی در ++C

برنامه زیر از سرفايل <cmath> استفاده‌ می‌كند. هدف اين است که صحت رابطۀ Sin2x=2SinxCosx به شکل تجربي بررسی شود.

برنامۀ مقدار x را در ستون اول، مقدار Sin2x را در ستون دوم و مقدار 2SinxCosx را در ستون سوم چاپ‌ می‌كند. خروجی نشان می‌دهد که براي هر مقدار آزمايشی x، مقدار Sin2x با مقدار 2SinxCosx برابر است.

بيشتر توابع معروف رياضی كه در ماشين‌حساب‌ها هم وجود دارد در سرفايل <cmath> تعريف شده است. بعضی از اين توابع در جدول زير نشان داده شده:

نام تابع

شرح

مثال

acos(x)

کسينوس معکوسx  (به راديان)acos(0.2) مقدار 1.36944 را برمی‌گرداند

asin(x)

سينوس معکوس x (به راديان)asin(0.2) مقدار 0.201358 را برمی‌گرداند

atan(x)

تانژانت معکوس x (به راديان)atan(0.2) مقدار 0.197396 را برمی‌گرداند

ceil(x)

مقدار سقف x (گرد شده)ceil(3.141593) مقدار 4.0 را برمی‌گرداند

cos(x)

کسينوس x (به راديان)cos(2) مقدار -0.416147 را برمی‌گرداند

exp(x)

تابع نمايي x (در پايه e)exp(2) مقدار 7.38906 را برمی‌گرداند

fabs(x)

قدر مطلق xfabs(-2) مقدار 2.0 را برمی‌گرداند

floor(x)

مقدار کف x (گرد شده)floor(3.141593) مقدار 3.0 را برمی‌گرداند

log(x)

لگاريتم طبيعي x (در پايه e)log(2) مقدار 0.693147 را برمی‌گرداند

log10(x)

لگاريتم عمومي x (در پايه 10)log10(2) مقدار 0.30103 را برمی‌گرداند

pow(x,p)

x به توان ppow(2,3) مقدار 8.0 را برمی‌گرداند

sin(x)

سينوس x (به راديان)

sin(2) مقدار 0.909297 را برمی‌گرداند

sqrt(x)

جذر x

sqrt(2) مقدار 1.41421 را برمی‌گرداند

tan(x)

تانژانت x (به راديان)

tan(2) مقدار -2.18504 را برمی‌گرداند

توجه داشته باشيد که هر تابع رياضی يک مقدار از نوع double را برمی‌گرداند. اگر يك نوع صحيح به تابع فرستاده شود، قبل از اين كه تابع آن را پردازش کند، مقدارش را به نوع double‌ ارتقا می‌دهد.

فایل های سرآیند یا هدر در ++C

بعضي از سرفايل‌های كتابخانۀ ++C استاندارد که کاربرد بيشتری دارند در جدول زير آمده است:

نام سرفايل

شرح

<assert>

تابع <assert> را تعريف می‌کند

<ctype>

توابعی را براي بررسی کاراکترها تعريف می‌کند

<cfloat>

ثابت‌های مربوط به اعداد مميز شناور را تعريف می‌کند

<climits>

محدودۀ اعداد صحيح را روی سيستم موجود تعريف می‌کند

<cmath>

توابع رياضی را تعريف می‌کند

<cstdio>

توابعی را براي ورودی و خروجی استاندارد تعريف می‌کند

<cstdlib>

توابع کاربردی را تعريف می کند

<cstring>

توابعی را برای پردازش رشته‌ها تعريف می‌کند

<ctime>

توابع تاريخ و ساعت را تعريف می‌کند

اين سرفايل‌ها از كتابخانۀ‌ C استاندارد گرفته شده‌اند. استفاده از آن‌ها شبيه استفاده از سرفايل‌های ++C استاندارد (مانند <iostream> ) است. برای مثال اگر بخواهيم تابع اعداد تصادفي ()rand را از سرفايل <cstdlib> به كار ببريم، بايد دستور پيش‌پردازندۀ زير را به ابتداي فايل برنامۀ‌ اصلي اضافه کنيم:

#include <cstdlib>

توابع ساخت كاربر

گرچه توابع بسيار متنوعی در کتابخانۀ‌ ++C استاندارد وجود دارد ولی اين توابع برای بيشتر وظايف‌ برنامه‌نويسی كافی نيستند. علاوه بر اين برنامه‌نويسان دوست دارند خودشان بتوانند توابعی را بسازند و استفاده نمايند.

تابع ()cube

يك مثال ساده از توابع ساخت كاربر:

اين تابع، مكعب يك عدد صحيح ارسالی به آن را برمی‌گرداند. بنابراين فراخواني (cube(2 مقدار 8 را برمی‌گرداند.

قسمت های تابع ساخت كاربر

يك تابع ساخت كاربر دو قسمت دارد:

1-عنوان  2- بدنه.

عنوان يك تابع به صورت زير است:

(فهرست‌ پارامترها) نام‌ نوع‌ بازگشتی

int cube(int x)

{

… بدنه تابع

}

بدنۀ تابع، يك بلوك كد است كه در ادامۀ عنوان آن مي‌آيد. بدنه شامل دستوراتي است كه بايد انجام شود تا نتيجۀ مورد نظر به دست آيد. بدنه شامل دستور return است كه پاسخ نهايي را به مكان فراخواني تابع برمي‌گرداند.

نوع بازگشتي تابع ()cube که در بالا تعريف شد، int است. نام آن cube می‌باشد و يک پارامتر از نوع int به نام x دارد. يعنی تابع ()cube يک مقدار از نوع int می‌گيرد و پاسخی از نوع int تحويل می‌دهد.

دستور return دو وظيفۀ عمده دارد. اول اين که اجرای تابع را خاتمه می‌دهد و دوم اين که مقدار نهايی را به برنامۀ فراخوان باز می‌گرداند. دستور return به شکل زير استفاده می‌شود:

return expression;

به جای expression هر عبارتي قرار می‌گيرد که بتوان مقدار آن را به يک متغير تخصيص داد. نوع آن عبارت بايد با نوع بازگشتی تابع يکی باشد. عبارت int main که در همۀ برنامه‌ها استفاده کرده‌ايم يک تابع به نام «تابع اصلي» را تعريف می‌کند. نوع بازگشتي اين تابع از نوع int است. نام آن main است و فهرست پارامترهاي آن خالی است؛ يعني هيچ پارامتری ندارد.

برنامۀ آزمون

وقتي يک تابع مورد نياز را ايجاد کرديد، فوراً بايد آن تابع را با يک برنامۀ ساده امتحان کنيد. چنين برنامه‌ای برنامۀ آزمون ناميده می‌شود. برنامۀ آزمون يک برنامۀ موقتي است که بايد «سريع و کثيف» باشد؛ يعني: لازم نيست در آن تمام ظرافت‌های برنامه‌نويسي – مثل پيغام‌هاي خروجی، برچسب‌ها و راهنماهای خوانا – را لحاظ کنيد.  تنها هدف اين برنامه، امتحان کردن تابع و بررسي صحت کار آن است.

مثال: يك برنامۀ آزمون براي تابع ()cube – کد زير شامل تابع ()cube و برنامۀ آزمون آن است:

برنامۀ حاضر اعداد صحيح را از ورودی می‌گيرد و مكعب آن‌ها را چاپ می‌كند اين تا 5 مرحله تکرار می شود.

هر عدد صحيحي که خوانده می‌شود، با استفاده از کد (cube(n به تابع ()cube فرستاده می‌شود. مقدار بازگشتی از تابع، جايگزين عبارت (cube(n گشته و با استفاده از cout در خروجی چاپ می‌شود.

مي‌توان رابطۀ بين تابع ()main و تابع ()cube را شبيه اين شکل تصور نمود:

تابع cube

مثال: يك برنامۀ آزمون براي تابع ()max – تابع زير دو پارامتر دارد. اين تابع از دو مقدار فرستاده شده به آن، مقدار بزرگ‌تر را برمی‌گرداند:

دستور return

توابع می‌توانند بيش از يک دستور return داشته باشند. مثلا تابع ()max را مانند اين نيز می‌توانستيم بنويسيم:

در اين کد هر دستور return که زودتر اجرا شود مقدار مربوطه‌اش را بازگشت داده و تابع را خاتمه می‌دهد. دستور return نوعی دستور پرش است (شبيه دستور break ) زيرا اجرا را به بيرون از تابع هدايت می‌کند. اگرچه معمولا return در انتهای تابع قرار می‌گيرد، می‌توان آن را در هر نقطۀ ديگری از تابع قرار داد.

اعلان‌ها و تعاريف تابع

به دو روش مي توان توابع را تعريف نمود:

  1. توابع قبل از تابع ()main به طور كامل با بدنه مربوطه آورده شوند.
  2. راه ديگري که بيشتر رواج دارد اين گونه است که ابتدا تابع اعلان شود، سپس متن برنامۀ اصلی ()main بيايد، پس از آن تعريف کامل تابع قرار بگيرد.

اعلان تابع با تعريف تابع تفاوت دارد. اعلان تابع، فقط عنوان تابع است که يک سميکولن در انتهای آن قرار دارد. تعريف تابع، متن کامل تابع است که هم شامل عنوان است و هم شامل بدنه. اعلان تابع شبيه اعلان متغيرهاست. يک متغير قبل از اين که به کار گرفته شود بايد اعلان شود. تابع هم همين طور است با اين فرق که متغير را در هر جايي از برنامه مي‌توان اعلان کرد اما تابع را بايد قبل از برنامۀ اصلي اعلان نمود.

در اعلان تابع فقط بيان می‌شود که نوع بازگشتی تابع چيست، نام تابع چيست و نوع پارامترهاي تابع چيست. همين‌ها براي کامپايلر کافي است تا بتواند کامپايل برنامه را آغاز کند. سپس در زمان اجرا به تعريف بدنۀ تابع نيز احتياج می‌شود که اين بدنه در انتهای برنامه و پس از تابع ()main قرار می‌گيرد.

فرق بين آرگومان و پارامتر

پارامترها متغيرهايی هستند که در فهرست پارامتر يک تابع نام برده می‌شوند. پارامترها متغيرهاي محلی براي تابع محسوب می‌شوند؛ يعني فقط در طول اجرای تابع وجود دارند. آرگومان‌ها متغيرهايی هستند که از برنامۀ اصلي به تابع فرستاده می‌شوند.

مثال: تابع ()max با اعلان‌ جدا از تعريف آن – اين برنامه همان برنامۀ آزمون تابع ()max در مثال قبلی است. اما اين‌جا اعلان تابع بالای تابع اصلي ظاهر ‌شده و تعريف تابع بعد از برنامۀ اصلي آمده است:

توجه كنيد كه پارامترهای x و y در بخش عنوان تعريف تابع آمده‌اند (طبق معمول) ولي در اعلان تابع وجود ندارند.

كامپايل جداگانۀ توابع

اغلب اين طور است که تعريف و بدنۀ توابع در فايل‌های جداگانه‌اي قرار می‌گيرد. اين فايل‌ها به طور مستقل کامپايل می‌شوند و سپس به برنامۀ اصلی که آن توابع را به کار می‌گيرد الصاق می‌شوند. توابع کتابخانۀ ++C استاندارد به همين شکل پياده‌سازی شده‌اند و هنگامي که يکي از آن توابع را در برنامه‌هايتان به کار می‌بريد بايد با دستور راهنمای پيش‌پردازنده، فايل آن توابع را به برنامه‌تان ضميمه کنيد. اين کار چند مزيت دارد:

  • اولين مزيت «مخفی‌سازي اطلاعات» است.
  • مزيت ديگر اين است که توابع مورد نياز را می‌توان قبل از اين که برنامۀ اصلی نوشته شود، جداگانه آزمايش نمود.
  • سومين مزيت اين است که در هر زمانی به راحتی می‌توان تعريف توابع را عوض کرد بدون اين که لازم باشد برنامۀ اصلی تغيير يابد.
  • چهارمين مزيت هم اين است که می‌توانيد يک بار يک تابع را کامپايل و ذخيره کنيد و از آن پس در برنامه‌های مختلفی از همان تابع استفاده ببريد.

تابع ()max را به خاطر بياوريد. براي اين که اين تابع را در فايل جداگانه‌اي قرار دهيم، تعريف آن را در فايلي به نام max.cpp ذخيره مي‌کنيم. فايل max.cpp شامل کد زير است:

حال كافي است عبارت: <include <test.cpp# را به اول برنامه اصلی وقبل از ()main اضافه كنيم:

نحوۀ کامپايل کردن فايل‌ها و الصاق آن‌ها به يکديگر به نوع سيستم عامل و نوع کامپايلر بستگی دارد. در سيستم عامل ويندوز معمولا توابع را در فايل‌هايي از نوع DLL کامپايل و ذخيره می‌کنند و سپس اين فايل را در برنامۀ اصلی احضار می‌نمايند. فايل‌هاي DLL را به دو طريق ايستا و پويا مي‌توان مورد استفاده قرار داد. برای آشنايي بيشتر با فايل‌هاي DLL به مرجع ويندوز و کامپايلرهاي ++C مراجعه کنيد.

متغيرهای محلی، توابع محلی

متغير محلی، متغيری است که در داخل يک بلوک اعلان گردد. اين گونه متغيرها فقط در داخل همان بلوکی که اعلان می‌شوند قابل دستيابی هستند. چون بدنۀ تابع، خودش يک بلوک است پس متغيرهای اعلان شده در يک تابع متغيرهای محلي براي آن تابع هستند. اين متغيرها فقط تا وقتی که تابع در حال کار است وجود دارند. پارامترهای تابع نيز متغيرهای محلي محسوب می‌شوند.

مثال: تابع فاكتوريل – اعداد فاكتوريل را در مثال قبل تر ديديم. فاكتوريل عدد صحيح n برابر است با:

n! = n(n-1)(n-2)..(3)(2)(1)

تابع زير، فاکتوريل عدد n را محاسبه می‌کند‌:

اين تابع دو متغير محلي دارد: n و f پارامتر n يک متغير محلی است زيرا در فهرست پارامترهاي تابع اعلان شده و متغير f نيز محلي است زيرا درون بدنۀ تابع اعلان شده است.

همان گونه که متغيرها می‌توانند محلي باشند، توابع نيز می‌توانند محلي باشند. يک تابع محلي تابعي است که درون يک تابع ديگر به کار رود. با استفاده از چند تابع ساده و ترکيب آن‌ها مي‌توان توابع پيچيده‌تري ساخت. به مثال زير نگاه کنيد.

در رياضيات، تابع جايگشت را با (p(n,k نشان می‌دهند. اين تابع بيان می‌کند که به چند طريق مي‌توان k عنصر دلخواه از يک مجموعۀ n عنصری را کنار يکديگر قرار داد. براي اين محاسبه از رابطۀ زير استفاده می‌شود:

تابع جایگشتی در ++C

اين تابع، خود از تابع ديگری که همان تابع فاکتوريل است استفاده کرده است. شرط به کار رفته در دستور if براي محدود کردن حالت‌هاي غير ممکن استفاده شده است. در اين حالت‌ها، تابع مقدار 0 را برمي‌گرداند تا نشان دهد که يک ورودي اشتباه وجود داشته است.

تابع void

لازم نيست يك‌ تابع‌ حتما مقداری را برگرداند. در ++C براي مشخص کردن چنين توابعی از کلمۀ کليدي void به عنوان نوع بازگشتی تابع استفاده می‌کنند يک تابع void تابعی است که هيچ مقدار بازگشتي ندارد. از آن‌جا كه يك تابع void مقداري را برنمی‌گرداند، نيازي به دستور return نيست ولي اگر قرار باشد اين دستور را در تابع void قرار دهيم، بايد آن را به شکل تنها استفاده کنيم بدون اين که بعد از کلمۀ return هيچ چيز ديگري بيايد. در اين حالت دستور return فقط تابع را خاتمه می دهد.

توابع بولی

در بسياري از اوقات لازم است در برنامه، شرطي بررسی شود. اگر بررسی اين شرط به دستورات زيادي نياز داشته باشد، بهتر است که يک تابع اين بررسی را انجام دهد. اين کار مخصوصا هنگامی که از حلقه‌ها استفاده می‌شود بسيار مفيد است. توابع بولی فقط دو مقدار را برمي‌گردانند: true يا false .

اسم توابع بولي را معمولا به شکل سوالي انتخاب می کنند زيرا توابع بولی هميشه به يک سوال مفروض پاسخ بلی يا خير می‌دهند.

توابع ورودی/خروجی (I/O)

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

قبلا نمونه‌اي از توابع خروجي را ديديم. تابع ()PrintDate در مثال های قبلی هيچ چيزی به برنامۀ اصلی برنمی‌گرداند و فقط براي چاپ نتايج به کار می‌رود. اين تابع نمونه‌ای از توابع خروجي است؛ يعني توابعی که فقط براي چاپ نتايج به کار می‌روند و هيچ مقدار بازگشتی ندارند.

توابع ورودی

توابع ورودی نيز به همين روش کار می‌کنند اما در جهت معکوس. يعنی توابع ورودی فقط برای دريافت ورودی و ارسال آن به برنامۀ اصلی به کار می‌روند و هيچ پارامتري ندارند. مثال بعد يک تابع ورودی را نشان می‌دهد.

مثال: تابعی برای دريافت سن كاربر – تابع سادۀ زير، سن کاربر را درخواست می‌کند و مقدار دريافت شده را به برنامۀ اصلی می‌فرستد. اين تابع تقريباً هوشمند است و هر عدد صحيح ورودی غير منطقی را رد می‌کند و به طور مکرر درخواست ورودی معتبر می‌کند تا اين که يک عدد صحيح در محدودۀ 7 تا 120 دريافت دارد:

تا اين‌ لحظه‌ تمام‌ پارامترهايی كه‌ در توابع‌ ديديم‌ به‌ طريق‌ مقدار ارسال‌ شده‌اند. يعني‌ ابتدا مقدار متغيری که در فراخوانی تابع ذکر شده برآورد می‌شود و سپس اين مقدار به پارامترهاي محلی تابع فرستاده می‌شود.  مثلا در فراخواني (cube(x ابتدا مقدار x برآورد شده و سپس اين مقدار به متغير محلي n در تابع فرستاده می‌شود و پس از آن تابع کار خويش را آغاز می‌کند. در طی اجرای تابع ممکن است مقدار n تغيير کند اما چون n محلی است هيچ تغييری روی مقدار x نمی‌گذارد. پس خود x به تابع نمی‌رود بلکه مقدار آن درون تابع کپی می‌شود.

تغيير دادن اين مقدار کپی شده درون تابع هيچ تاثيري بر x اصلی ندارد. به اين ترتيب تابع می‌تواند مقدار x را بخواند اما نمی‌تواند مقدار x را تغيير دهد. به همين دليل به x يک پارامتر «فقط خواندنی» می‌گويند. وقتی ارسال به وسيلۀ مقدار باشد، هنگام فراخوانی تابع می‌توان از عبارات استفاده کرد. مثلا تابع ()cube را می‌توان به صورت (cube(2*x-3 فراخوانی کرد يا به شکل ((cube(2*sqrt(x)-cube(3 فراخوانی نمود. در هر يک از اين حالات، عبارت درون پرانتز به شکل يک مقدار تکی برآورد شده و حاصل آن مقدار به تابع فرستاده می‌شود.

ارسال به طريق ارجاع‌ (آدرس)

ارسال به طريق مقدار call by value باعث می‌شود که متغيرهای برنامۀ اصلی از تغييرات ناخواسته در توابع مصون بمانند. اما گاهي اوقات عمداً می‌خواهيم اين اتفاق رخ دهد. يعني می‌خواهيم که تابع بتواند محتويات متغير فرستاده شده به آن را دست‌کاري کند. در اين حالت از ارسال به طريق ارجاع call by reference ‌استفاده می‌کنيم.

برای اين که مشخص کنيم يک پارامتر به طريق ارجاع ارسال می‌شود، علامت & را به نوع پارامتر در فهرست پارامترهای تابع اضافه می‌کنيم. اين باعث می‌شود که تابع به جاي اين که يک کپی محلي از آن آرگومان ايجاد کند، خود آرگومان محلی را به کار بگيرد. به اين ترتيب تابع هم می‌تواند مقدار آرگومان فرستاده شده را بخواند و هم می‌تواند مقدار آن را تغيير دهد. در اين حالت آن پارامتر يک پارامتر «خواندنی-نوشتنی» خواهد بود. هر تغييری که روي پارامتر خواندنی-نوشتنی در تابع صورت گيرد به طور مستقيم روی متغير برنامۀ اصلي اعمال می‌شود. به مثال زير نگاه کنيد.

مثال: تابع‌ ()swap -تابع‌ كوچك‌ زير در مرتب کردن داده‌ها کاربرد فراوان دارد:

هدف‌ اين تابع جابجا کردن دو عنصری است که به آن فرستاده می‌شوند. برای اين منظور پارامترهای x و y به صورت پارامترهای ارجاع تعريف شده‌اند:

float& x, float& y

عملگر ارجاع‌

عملگر ارجاع‌ & موجب‌ می‌شود كه‌ به جاي x و y آرگومان‌های ارسالی قرار بگيرند. برنامۀ آزمون و اجراي آزمايشی آن در زير آمده است:

وقتي‌ فراخواني (swap(a,b اجرا می‌شود، x به a اشاره می‌کند و y به b. سپس متغير محلي temp اعلان می‌شود و مقدار x (که همان a است) درون آن قرار می‌گيرد. پس از آن مقدار y (که همان b است) درون x (يعني a) قرار می‌گيرد و آنگاه مقدار temp درون y (يعني b) قرار داده می‌شود. نتيجۀ نهايی اين است که مقادير a و b با يکديگر جابجا می شوند. شکل زیر نشان می‌دهد که چطور اين جابجايی رخ می‌دهد:

ارسال با ارجاع

به‌ اعلان‌ تابع‌ ()swap دقت کنيد:

void swap(float&, float&)

اين اعلان شامل عملگر ارجاع‌ & براي‌ هر پارامتر است‌. برنامه‌نويسان c عادت دارند که عملگر ارجاع & را به عنوان پيشوند نام متغير استفاده کنند (مثل float &x) در ++C فرض می کنيم عملگر ارجاع & پسوند نوع است (مثل float& x) به هر حال کامپايلر هيچ فرقی بين اين دو اعلان نمی‌گذارد و شکل نوشتن عملگر ارجاع کاملا اختياری و سليقه‌ای است.

 مثال‌ ارسال‌ به‌ طريق‌ مقدار و ارسال‌ به‌ طريق‌ ارجاع‌

مثال‌ زیر ارسال‌ به‌ طريق‌ مقدار call by value و ارسال‌ به‌ طريق‌ ارجاع‌ call by reference را انجام می دهد. اين‌ برنامه، تفاوت‌ بين‌ ارسال‌ به طريق‌ مقدار و ارسال‌ به طريق‌ ارجاع‌ را نشان‌ می‌دهد:

تابع ()f دو پارامتر دارد که اولی به طريق مقدار و دومی به طريق ارجاع ارسال می‌شود. فراخواني (f(a,b باعث می‌شود که a از طريق‌ مقدار به‌ x ارسال شود و b از طريق‌ ارجاع‌ به‌ y فرستاده شود.

تفاوت call by value و call by reference تفاوت call by value و call by reference

در جدول‌ زير خلاصۀ تفاوت‌های بين ارسال از طريق مقدار و ارسال از طريق ارجاع آمده است.

    ارسال از طريق ارجاع     ارسال از طريق مقدار

int& x;

int x;

   پارامتر x يک ارجاع است      پارامتر x يک متغير محلی است
   x مترادف با آرگومان است      x يک کپی از آرگومان است
    می‌تواند محتويات آرگومان را تغيير دهد      تغيير محتويات آرگومان ممکن نيست
   آرگومان ارسال شده از طريق ارجاع فقط بايد يک متغير باشد   آرگومان ارسال شده از طريق مقدار مي‌تواند يک ثابت، يک متغير يا يک     عبارت باشد
   آرگومان خواندنی-نوشتنی است    آرگومان فقط خواندنی است

يكی‌ از مواقعی‌ كه‌ پارامترهای‌ ارجاع‌ مورد نياز هستند جايی‌ است‌ كه‌ تابع‌ بايد بيش از يك‌ مقدار را بازگرداند. دستور return فقط می‌تواند يك‌ مقدار را برگرداند. بنابراين‌ اگر بايد بيش از يك‌ مقدار برگشت داده‌ شود، اين‌ كار را پارامترهای‌ ارجاع‌ انجام‌ می‌دهند.

ارسال‌ از طريق‌ ارجاع‌ ثابت

‌ارسال پارامترها به طريق ارجاع دو خاصيت مهم دارد:

  • اول اين که تابع مي‌تواند روي آرگومان واقعي تغييراتی بدهد
  • دوم اين که از اشغال بی‌مورد حافظه جلوگيری می‌شود.

روش ديگری نيز براي ارسال آرگومان وجود دارد:

ارسال از طريق ارجاع ثابت. اين روش مانند ارسال از طريق ارجاع است با اين فرق که تابع نمی‌تواند محتويات پارامتر ارجاع را دست‌کاری نمايد و فقط اجازۀ خواندن آن را دارد. براي اين که پارامتري را از نوع ارجاع ثابت اعلان کنيم بايد عبارت const را به ابتدای اعلان آن اضافه نماييم.

مثال: ارسال‌ از طريق‌ ارجاع‌ ثابت‌ – سه طريقه ارسال پارامتر در تابع زير به کار رفته است:

در تابع فوق اولين‌ پارامتر يعني x از طريق مقدار ارسال می‌شود، دومين پارامتر يعني y از طريق ارجاع و سومين پارامتر نيز از طريق ارجاع ثابت. برنامۀ آزمون و يک اجراي آزمايشي از مثال قبل:

تابع‌ فوق پارامترهای‌ x و y را مي‌تواند تغيير دهد ولي‌ قادر نيست پارامتر z را تغيير دهد. تغييراتي که روي x صورت می‌گيرد اثري روي آرگومان a نخواهد داشت زيرا a از طريق مقدار به تابع ارسال شده. تغييراتی که روي y صورت می‌گيرد روي آرگومان b هم تاثير می‌گذارد زيرا b از طريق ارجاع به تابع فرستاده شده. ارسال به طريق ارجاع ثابت بيشتر براي توابعی استفاده می‌شود که عناصر بزرگ را ويرايش می‌کنند مثل آرايه‌ها يا نمونۀ کلاس‌ها که در جلسه‌‌های بعدي توضيح آن‌ها آمده است. عناصری که از انواع اصلی هستند (مثل int يا float) به طريق مقدار ارسال می‌شوند به شرطي که قرار نباشد تابع محتويات آن‌ها را دست‌کاری کند.

توابع‌ بی‌واسطه inline

تابعي که به شکل بی‌واسطه تعريف می‌شود، ظاهری شبيه به توابع معمولی دارد با اين فرق که عبارت inline در اعلان و تعريف آن قيد شده است. مثال‌ زیر تابع‌ ()cube به شکل بی‌واسطه‌ یا inline است. اين‌ همان‌ تابع‌ ()cube مثال‌ قبلی است‌:

تنها تفاوت‌ اين‌ است‌ كه‌ كلمۀ‌ كليدی‌ inline در ابتداي عنوان تابع ذکر شده. اين‌ عبارت به‌ كامپايلر می گويد كه‌ در برنامه به جاي (cube(n کد واقعي (n)*(n)*(n) را قرار دهد.استفاده از توابع بی‌واسطه می‌تواند اثرات منفي داشته باشد. مثلا اگر يک تابع بی‌واسطه داراي 40 خط کد باشد و اين تابع در 26 نقطه مختلف از برنامۀ اصلی فراخوانی شود، هنگام کامپايل بيش از هزار خط کد به برنامۀ اصلی افزوده می‌شود. همچنين تابع بی‌واسطه می تواند قابليت انتقال برنامۀ شما را روی سيستم‌های مختلف کاهش دهد. به برنامۀ آزمون زير نگاه کنيد:

اين برنامه هنگام کامپايل به شکل زير درمی‌آيد، گويی اصلا تابعی وجود نداشته:

وقتی‌ كامپايلر کد واقعی تابع را جايگزين فراخوانی آن می‌کند، می‌گوييم که تابع بی‌واسطه، باز می‌شود.

مطالب زیر را حتما بخوانید

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

این سایت از اکیسمت برای کاهش هرزنامه استفاده می کند. بیاموزید که چگونه اطلاعات دیدگاه های شما پردازش می‌شوند.