در تپش این هفته، ماجرای فریب و تعرض در پوشش عرفانهای دروغین و رمالی را بررسی کردیم
فرض کنید یک تابع نوشتید که 2 مقدار int از ورودی گرفته و حاصل جمع آن را در مقدار اول به صورت زیر نگه میدارد:
Void add(int a،int b){
a+=a+b;
}
حال یک تابع، 2 مقدار a و b را به تابع add میدهد و تابع را به صورت زیر فراخوانی میکند.
Int a=10,b=20;
Add(a,b);
Print(a);
انتظاری که ما داریم این است که دستور (printa) مقدار 30 را در خروجی چاپ کند، اما این اتفاق نمیافتد! علت چیست؟
وقتی یک تابع فراخوانی میشود از پارامترهای ورودیای که قرار است به تابع داده شود، یک کپی تهیه و به تابع داده میشود و پس از پایان فراخوانی، آن کپی از بین میرود. سادهتر بگوییم، همین مثال بالا را ببینید وقتی تابع add فراخوانی میشود، یک کپی از 2 متغیر a و b تهیه شده و به تابع add داده میشود. سپس تابع فراخوانی میشود و زمانی که کار تابع addتمام شده و کنترل را به تابع فراخواننده میدهد، کپیهای تهیه شده از متغیرهای a و bاز بین میروند. پس هیچ وقت دستور (printa) مقدار 30 را چاپ نمیکند. خب، حال مشکل بالا را چگونه حل کنیم؟ به 2 روش میتوان این مشکل را حل کرد.
1 ـ مقدار بازگشتی؛ تابع add یک مقدار بازگشتی داشته باشد و زمانی که کارش تمام شد حاصل را به عنوان خروجی برگرداند.
این روش یک مشکل دارد و آن هم این است که دستور return حداکثر یک مقدار بر میگرداند و اجرای دستور return به منزله این است که کار تابع تمام شده است، در نتیجه هیچ وقت نمیتوان چند دستور return پشت سرهم داشت. این روش برای مثال بالا جوابگو است، اما اگر آرگومانهای ورودی تابع بیشتر از ? تا باشند و مقدار تغیر داده شده آنها برای ما مهم باشند دیگر این روش جوابگو نیست (این مشکل در زبان سی شارپ قابل حل است، یعنی یک تابع چند متغیر را برمیگرداند، پیاده سازی این روش در سی شارپ بر عهده خواننده گذاشته شده است.)
2 ـ استفاده از اشارهگرها؛ وقتی شما یک متغیر اشارهگر تعریف میکنید، این اشارهگر به یک خانه حافظه اشاره دارد و در واقع آدرس آن خانه را در خود نگه میدارد و هر تغییری در مقدار متغیر میتواند مقدار آن خانه حافظه را تغییر دهد. اما در مورد مشکل بالا، اگر پارامترهای ورودی یک تابع از نوع اشارهگر باشند، وقتی بخواهیم آن تابع را فراخوانی کنیم باید آدرس متغیر را به تابع بدهیم. پس از آن اگر هر تغییری درون تابع روی متغیرهای ورودی رخ بدهد، چون آدرس متغیر بوده، تغییرات در حافظه نوشته میشود. پس دیگر نگران از بین رفتن تغییرات نیستیم و نیازی هم به استفاده از دستور return برای بازیابی مقدار تغییر داده شده نداریم (البته این بدان معنا نیست که یک تابع هیچ مقداری را بر نگرداند، این امر به نحوه برنامهنویسی شما مربوط است). به این روش، فراخوانی از نوع ارجاع گفته میشود، یعنی به جای این که یک کپی از یک متغیر به تابع داده شود، آدرس آن یا ارجاع آن به تابع داده میشود.
بسیار خب، در زبان سی شارپ متغیرها 2 دسته هستند. دسته اولValue - Type ها هستند که دقیقا رفتاری مشابه متغیرها در زبان C یا++ C دارند. یعنی در Stack ذخیره میشوند و اگر به عنوان یک پارامتر به تابع داده شدند، یک کپی از آنها به تابع داده میشود و... دادههایValue - Type در زبان سی شارپ به صورت structure یا enumeration تعریف میشوند و در بالاترین سطح خود بعد از کلاس Object از کلاس ValueType به ارث میرسند.
نوع دیگر دادهها در زبان سی شارپ، دادههای Reference- Type هستند. این نوع دادهها رفتاری متفاوت با متغیرهایValue - Type دارند.
وقتی یک متغیر به صورت Reference-Type تعریف میشود، خود متغیر در حافظه Stack ذخیره میشود که خود یک اشارهگر است و این اشارهگر به خانههای حافظه Heap اشاره میکند. این متغیر اگر به عنوان پارامتر به یک تابع داده شود و درون تابع تغییری روی متغیر حاصل شود، در بیرون از تابع نیز دیده میشود. چراکه وقتی به یک تابع داده میشود، در واقع یک اشارهگر است که به خانههای حافظه در Heapاشاره میکند. داده Reference-Type باید از نوع class یا Interfaceیا delegate باشند.
اما سوال اصلی در اینجا این است که اگر یک متغیر Value - Type را بخواهیم به یک تابع بدهیم به طوری که فراخوانی از نوع ارجاع باشد، چه اتفاقی میافتد؟ آیا زبان سی شارپ این امکان را به ما میدهد که با یک متغیر Value - Type رفتاری شبیه نوع دادههای Reference-Type داشته باشیم؟ در مقالات بعدی به این سوال جواب داده خواهد شد.
امیربهاءالدین سبطالشیخ
در تپش این هفته، ماجرای فریب و تعرض در پوشش عرفانهای دروغین و رمالی را بررسی کردیم
گزارش «جامجم» درباره دستاوردهای زبان فارسی در گفتوگو با برخی از چهرههای ادب معاصر
معاون وزیر بهداشت: