در این پست در مورد مفاهیم مربوط به اشاره گر ها صحبت می کنیم. پیش نیاز این مطلب قسمت هفتم آموزش است که انواع توابع در آن شرح داده شده اند. گفتنیست که تمام زبان های سطح بالا اکثرا با اشاره گرها یا همان pointer ها سروکار دارند. با ما همراه شوید🙏
مفهموم اشاره گر
برای روشن شدن موضوع بیایید مثال زیر را بررسی کنیم:
1 2 3 4 5 6 7 8 9 10 |
int main() { int a; a = 16; cout << a << endl; cout << &a << endl; getchar(); return 0; } |
1 2 3 |
16 0000002AD46FF7C4 |
یک متغیر به نام a تعریف کردیم که مقدار اولیه آن 16 است. سپس مقدار و همچنین آدرس متغیر که با علامت & بدست می آید را بر روی کنسول نشان دادیم. هر متغیر آدرسی دارد که نشان دهنده مکان ذخیره آن در حافظه است. پس بار دیگر تاکید میکنیم که a یک مقدار است ولی a& یک آدرس است.
خب بریم سراغ تعریف اشاره گر ؛ اشاره گر متغیری است که مقدار داخل آن تنها و تنها یک آدرس از حافظه Ram است. اشاره گره با علامت کلیدی * تعریف می شود و تنها می تواند درون خود یک آدرس ذخیره کند.
1 2 3 4 5 6 7 8 9 10 11 12 |
int main() { int a; int *p; a = 16; p = &a; cout << a << endl; cout << p << endl; getchar(); return 0; } |
1 2 3 |
16 0000002AD46FF7C4 |
همانطور که گفتیم بعد از تعریف a یک اشاره گره به نام p از نوع int تعریف کردیم. چون p اشاره گر است پس فقط می توان آدرس به آن انتساب داد. در خط ششم آدرس متغیر a درون p ریخته می شود. در نهایت هم مقادیر a و p که به ترتیب مقدار و آدرس هستند چاپ می شود.
کاربرد اشاره گر
اما این اشاره گر چه کاربردی دارد؟ به مثال زیر دقت کنید:
1 2 3 4 5 6 7 8 9 10 11 |
int main() { int a = 12; int *p; p = &a; *p = 18; cout << a << endl; getchar(); return 0; } |
خب به نظرتون چه عددی چاپ میشه؟ مطمئننا 12 نیست. قطعا 18 است. برنامه رو خط به خط تریس میکنیم. یک متغیر به نام a تعریف کردیم که مقدار اولیه آن 12 است. بعد یک اشاره گر تعریف کردیم و در خط بعد آدرس متغیر a را درون آن ریختیم. در خط بعد نوشتیم ;p = 18* . این به این معنی است که جایی که p به آن اشاره می کند (که میشه همون متغیر a) مقدارش 18 شود. به عبارتی دیگر با این روش متغیر a بصورت غیر مستقیم مقدار دهی شد.
در شکل بالا p یک اشاره گر است که مقدار آن آدرس متغیر a است(فلش سبز رنگ). دقت شود که اشاره گر p خود نیز یک آدرس در حافظه رم دارد که می توان آن آدرس را در اشاره گری دیگر ذخیره کرد (کاربرد آن در آرایه های دو بعدی است که در آینده به آن می پردازیم).
مفهوم Call by value
در جلسه هفتم در مورد توابع صحبت کردیم. توابعی که نوشتیم، پارامترهای ورودی آن ها call by value بودند. یعنی اینکه موقع صدا زدن تابع، یک کپی از مقادیر برای تابع ارسال میشد. به عنوان مثال:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void change(int x, int y){ x = 2; y = 3; } int main() { int a = 12; int b = 19; change(a, b); cout << a << endl; cout << b << endl; getchar(); return 0; } |
1 2 3 |
12 19 |
دو متغیر به نام a و b که مقادیر اولیه 12 و 19 دارند تعریف شد. تابعی به نام change داریم که دو مقدار ورودی می گیرد و آن ها را به ترتیب به 2 و 3 تغییر می دهد. اما از آنجایی که هنگام صدا زدن تابع change مقادیر بصورت call by value به تابع فرستاده می شوند (به صورت ساده تر کپی آن ها فرستاده می شود)، با تغییر دادن x و y مقادیر a و b تغییر نمی کند. در نهایت ما همان مقادیر 12 و 19 را مشاهده میکنیم.
مفهوم Call by reference
برعکس مفهموم call by value است و دقیقا خود متغیر را به تابع می فرستد نه یک کپی از آن. به این صورت که موقع صدا زدن تابع آدرس متغیر به تابع ارسال می شود که با اعمال تغییرات بر روی آن بصورت غیر مستقیم بر روی متغیر اثر می کند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void change(int *x, int *y){ *x = 2; *y = 3; } int main() { int a = 12; int b = 19; change(&a, &b); cout << a << endl; cout << b << endl; getchar(); return 0; } |
1 2 3 |
2 3 |
با اندکی دقت متوجه می شویم که بجای (change(a, b از (change(&a, &b استفاده کردیم که با اینکار آدرس ها ارسال می شوند. از طرفی دیگر پارامترهای ورودی تابع change هم باید بصورت اشاره گر تعریف شوند تا بتوان آدرس به آنها ارسال کرد.
اما راه دیگر برای استفاده از مفهموم call by reference که بسیار ساده تر از این روش است گذاشتن علامت & در تعریف پارامتر ورودی تابع است. با اینکار کامپایلر تمام علامت گذاری ها را انجام می دهد و بصورت اتوماتیک آدرس متغیرها را ارسال می کند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void change(int &x, int &y){ x = 2; y = 3; } int main() { int a = 12; int b = 19; change(a, b); cout << a << endl; cout << b << endl; getchar(); return 0; } |
1 2 3 |
2 3 |
با این کار هر متغیری که به تابع فرستاده شود، هر تغییر در پارامتر ورودی دقیقا بر روی آن تاثیر می گذارد. مفهموم call by reference بسیار بسیار کاربردی است و در آینده تمامی توابعی که در بخش پردازش تصویر با آن سر و کار داریم با call by reference کار می کنند.
امیدوارم این قسمت هم مفید بوده باشه. با رزدینو در قسمت نهم آموزش همراه باشید 😍😊