اشاره گرها و آدرس های ارسالی به تابع در برنامه نویسی C

در این آموزش به کمک مثال های مختلف با شیوه ارسال آدرس ها و اشاره گرها به عنوان آرگومان به تابع آشنا خواهید شد. در برنامه نویسی C می توانید آدرس ها را به عنوان آرگومان به تابع ارسال کنید.
مفهوم ارسال آدرس به توابع (Passing Addresses to Functions) در زبان C، توابع به طور پیشفرض مانند اتاقهای پردازشی کاملاً ایزولهای هستند که وقتی اطلاعاتی را دریافت میکنند، صرفاً روی یک «کپی» از آن اطلاعات کار میکنند و دسترسی به دادههای اصلی ندارند. مکانیزم ارسال آدرس (از طریق اشارهگرها)، روشی است که در آن به جای دادنِ یک کپی از دادهها به تابع، مختصات دقیق و کلید دسترسیِ آن دادهها در حافظه فیزیکی، مستقیماً در اختیار تابع قرار میگیرد.
ویژگیهای بنیادین این مکانیزم
-
شکستن مرزهای محلی (Scope Override): متغیرهایی که در یک بخش از برنامه ایجاد میشوند، در حالت عادی برای سایر توابع کاملاً نامرئی و غیرقابل دسترس هستند. ارسال آدرس از طریق اشارهگر، این دیوارهای امنیتی را کنار میزند و به یک تابع اجازه میدهد مستقیماً به حریمِ متغیرهای دیگر نفوذ کرده و آنها را لمس کند.
-
ویرایش مستقیم و بازتاب دائمی تغییرات: در حالت پیشفرض، هر تغییری که یک تابع روی دادههای ورودی انجام دهد، پس از پایانِ کارِ تابع، همراه با خودش نابود میشود. اما وقتی تابع از طریق اشارهگر با آدرس فیزیکی کار میکند، تمام تغییرات و محاسبات به صورت دائمی و در لحظه روی اصلِ دادهها ثبت و ذخیره میشوند.
-
دور زدن محدودیتِ تکخروجی: یک قانون سختگیرانه در زبان C این است که توابع پس از پایان پردازش، تنها میتوانند یک نتیجه واحد را به عنوان خروجی بازگردانند. ارسال آدرسها این بنبست را میشکند؛ برنامهنویس میتواند آدرسِ چندین متغیر مختلف را به تابع بدهد تا تابع نتایج محاسبات گوناگون خود را مستقیماً در آن آدرسها بنویسد و عملاً توابعی با دهها خروجیِ همزمان خلق کند.
-
بهینهسازی افراطی منابع (Zero-Copy): تفاوتی ندارد که داده اصلی یک عدد ساده باشد یا یک بسته اطلاعاتی بسیار سنگین؛ آدرسِ آن داده در حافظه همیشه حجم بسیار ناچیز و ثابتی دارد (چند بایت). ارسال این مختصاتِ کوچک به جای کپی کردنِ تمامِ محتویات، بار پردازشی را به شدت کاهش داده و گلوگاههای حافظه را از بین میبرد.
هدف و فلسفه استفاده
فلسفه اصلی این طراحی، تبدیل توابع از واحدهای ایزوله و یکطرفه، به ابزارهایی تعاملی با دسترسی نامحدود به منابع سیستم است.
زبان C ذاتاً چیزی به نام «ارسال با ارجاع» (Pass by Reference) به صورت آماده و ایمن (مانند آنچه در زبانهای مدرن وجود دارد) ندارد. طراحان این زبان به جای خلق مکانیزمهای پیچیده برای ارتباطِ توابع با یکدیگر، تصمیم گرفتند مسئولیت را مستقیماً بر دوشِ برنامهنویس و اشارهگرها بگذارند.
هدف این است که برنامهنویس بتواند به جای جابهجا کردن اطلاعاتِ سنگین در بین توابعِ مختلف، دادهها را در یک نقطه از حافظه ثابت نگه دارد و فقط «نقشه دسترسی» به آن نقطه را بین توابع دست به دست کند. این هماهنگی میان اشارهگرها و توابع، کلیدِ اصلیِ تواناییِ زبان C در پردازشهای سنگین و بلادرنگ (Real-Time) در سیستمعاملها است، زیرا به توابع اجازه میدهد به صورت تیمی روی یک فضای مشترک از سختافزار کار کنند، بدون آنکه حتی یک بایت حافظه اضافی هدر برود.
برای گرفتن آدرس در تعریف تابع، از اشاره گرها استفاده می شود. به دلیل اینکه اشاره گرها می توانند آدرس را در خود ذخیره کنند.
مثال: ارسال آدرس ها به تابع
#include
void swap(int *n1, int *n2);
int main()
{
int num1 = 5, num2 = 10;
// ارسال آدرس به تابع
swap( &num1, &num2);
printf(“num1 = %dn”, num1);
printf(“num2 = %d”, num2);
return 0;
}
void swap(int* n1, int* n2)
{
int temp;
temp = *n1;
*n1 = *n2;
*n2 = temp;
}
خروجی
num1 = 10
num2 = 5
آدرس های num1 و num2 با استفاده از دستور swap(&num1, &num2); به تابع swap() ارسال می شوند.
اشاره گرهای n1 و n2 این آرگومان ها را در تعریف تابع پذیرش می کنند.
void swap(int* n1, int* n2) {
… ..
}
با تغییر *n1 و*n2 در داخل تابع ()swap ، مقادیر num1 و num2 نیز در داخل تابع ()main تغییر می کنند.
*n1 و*n2 در تابع ()swap جابجا شده اند، در نتیجه num1 و num2 نیز جابجا می شوند.
توجه داشته باشید که تابع ()swap هیچ مقداری بر نمی گرداند و نوع بازگشتی آن void است.
مثال ۲: ارسال اشاره گرها به تابع
#include
void addOne(int* ptr) {
(*ptr)++; // اضافه می کند *ptr یک واحد به
}
int main()
{
int* p, i = 10;
p = &i;
addOne(p);
printf(“%d”, *p); // 11
return 0;
}
در اینجا، مقدار ذخیره شده در i، *p ابتدا برابر ۱۰ می باشد سپس اشاره گر p به تابع ()addOne ارسال شده است. در تابع ()addOne، اشاره گر ptr این آدرس را می گیرد. در داخل تابع مقدار ذخیره شده در آدرسی که ptr به آن اشاره می کند، با دستور (*ptr)++;یک واحد افزایش می یابد. چون هر دو اشاره گر ptr و p آدرس یکسانی دارند پس *p در ()main نیز برابر با ۱۱ شده است.
