اشاره گرها در برنامه نویسی C (به زبان ساده با مثال)

در این آموزش با مفهوم اشاره گرها در برنامه نویسی C آشنا خواهید شد. معرفی اشاره گرها، شیوه استفاده از اشاره گرها و اشتباهات رایج هنگام کار با آنها را با کمک مثال های مختلف بررسی خواهیم کرد.
اشارهگرها در زبان C متغیرهای ویژهای هستند که به جای نگهداری دادههای معمولی و ملموس (مانند اعداد یا متنها)، «آدرس فیزیکی» خانههای حافظه کامپیوتر را در خود ذخیره میکنند. اگر متغیرهای عادی را مانند صندوقچههایی حاوی اشیاء در نظر بگیریم، اشارهگرها نقشههایی هستند که مختصات و محل دقیق این صندوقچهها را در انباریِ بیانتهای حافظه نشان میدهند.
ویژگیهای بنیادین اشارهگرها
-
دسترسی غیرمستقیم: اشارهگرها به برنامهنویس اجازه میدهند از راه دور و بدون نیاز به استفاده از نامِ اصلیِ یک متغیر، به محتویات آن خانه از حافظه دسترسی پیدا کرده و آن را بخوانند یا حتی بازنویسی کنند.
-
آگاهی از نوع داده (Type Awareness): هر اشارهگر، علاوه بر ذخیره کردن یک آدرس خشک و خالی، در ذات خود میداند که دادهی مستقر در آن آدرس از چه جنسی (عدد صحیح، عدد اعشاری، ساختار و...) است. این ویژگی حیاتی به پردازنده میفهماند که از نقطه شروع آدرس، باید چند بایت را به عنوان یک تکه اطلاعات معنادار بخواند.
-
پیمایش ریاضیاتی (Pointer Arithmetic): از آنجا که اشارهگرها ماهیت آدرسمحور دارند، میتوان با اعمال ریاضیات ساده (مثل جمع و تفریق) آنها را در سطح بلوکهای حافظه به جلو یا عقب راند تا به خانههای مجاور هدایت شوند؛ قابلیتی که مبنای اصلی کار با آرایههاست.
-
کلیدِ حافظه پویا: اشارهگرها تنها مسیر و تنها پلی هستند که برنامهنویس میتواند از طریق آنها، در حین اجرا و روشن بودن برنامه، از سیستمعامل فضای حافظه جدید و موقت درخواست کند و پس از اتمام کار، آن فضا را آزاد سازد.
هدف و فلسفه استفاده
فلسفه وجودی اشارهگرها در زبان C، حذف واسطهها و اعطای کنترل مطلقِ سختافزاری به برنامهنویس است.
در بسیاری از زبانهای برنامهنویسی سطح بالا، سیستم برای جلوگیری از خطاهای انسانی، دیواری بین برنامهنویس و حافظه فیزیکی میکشد. اما زبان C با خلق اشارهگرها، این دیوار را کاملاً فرو میریزد. هدف از این طراحی، رسیدن به غایتِ سرعت و کارایی است.
بدون اشارهگرها، خلق ساختارهای داده پیشرفته و زنجیرهای (مثل درختها و گرافها) غیرممکن است و تعامل مستقیم با قطعات سختافزاری، درایورها و هسته سیستمعامل به بنبست میرسد. زبان C از طریق اشارهگرها به برنامهنویس اعتماد کامل میکند؛ اعتمادی که بالاترین سرعت ممکن در دنیای نرمافزار را به ارمغان میآورد، اما در عین حال نیازمند دقتی بینقص است، چرا که کوچکترین اشتباه در مدیریت آدرسها میتواند به فروپاشی آنیِ کل سیستم منجر شود.
اشاره گرها از ویژگی های قدرتمند زبان های برنامه نویسی C و C++ هستند. قبل از یادگیری اشاره گرها، در مورد آدرس ها در برنامه نویسی C صحبت خواهیم کرد.
آدرس ها در زبان C
فرض کنید در برنامه خود یک متغیر به نام var دارید،var & حاوی آدرس این متغیر در حافظه است. هنگام استفاده از تابع ()scanf از آن استفاده می کنیم.
scanf(“%d”, &var);
در اینجا، مقدار وارد شده توسط کاربر در آدرس متغیر var ذخیره می شود. مثال،
#include
int main()
{
int var = 5;
printf(“var: %dn”, var);
// قبل از نام متغیر می آید &
printf(“address of var: %p”, &var);
return 0;
}
خروجی
var: 5
address of var: 2686778
نکته: با اجرای کد بالا آدرس های متفاوتی روی هر سیستم نمایش داده می شود.
اشاره گرهای C
اشاره گرها (متغیرهای اشاره گر) متغیرهای خاصی هستند که به جای ذخیره مقادیر برای ذخیره آدرس استفاده می شوند.
ساختار اشاره گر
نحوه تعریف اشاره گرها در زبان C:
int* p;
در اینجا یک اشاره گر p از نوع int تعریف شده است.
به شکل های زیر نیز می توانید اشاره گرها را تعریف کنید:
int *p1;
int * p2;
مثال دیگری از تعریف اشاره گرها را ببینیم،
int* p1, p2;
یک اشاره گر با نام p1 و متغیر عادی p2 تعریف شده است.
تخصیص آدرس به اشاره گر
مثال زیر را ببینید،
int* pc, c;
c = 5;
pc = &c;
در اینجا مقدار ۵ به متغیر c و آدرس متغیر c به اشاره گر pc اختصاص داده شده است.
به دست آوردن مقدار ذخیره شده در آدرس قرار گرفته در اشاره گر
برای به دست آوردن مقدار اشاره شده توسط اشاره گرها از عملگر * استفاده می کنیم. مثال:
int* pc, c;
c = 5;
pc = &c;
printf(“%d”, *pc); // Output: 5
در اینجا آدرس c در اشاره گر pc قرار داده شده و برای به دست آوردن مقدار ذخیره شده در این آدرس از *pc استفاده شده است.
نکته: در مثال بالا pc یک اشاره گر است نه *pc. نمی توانید و نباید مشابه کد *pc = &c را بنویسید.
عملگر *، عملگر ارجاع نامیده می شود (هنگام کار با اشاره گرها). مقدار ذخیره شده در آدرس اشاره گر را برمی گرداند.
تغییر مقدار ذخیره شده در آدرس قرار گرفته در اشاره گر
بیایید یک مثال بزنیم،
int* pc, c;
c = 5;
pc = &c;
c = 1;
printf(“%d”, c); // خروجی: ۱
printf(“%d”, *pc); // خروجی: ۱
آدرس متغیر c در اشاره گر pc قرار گرفته و در ادامه مقدار c به ۱ تغییر یافته است. چون pc حاوی آدرس c می باشد در نتیجه با تغییر مقدار c، مقدار *pc نیز به طور مشابه تغییر می کند و *pc مقدار ۱ را برمی گرداند.
یک مثال دیگر،
int* pc, c;
c = 5;
pc = &c;
*pc = 1;
printf(“%d”, *pc); // خروجی: ۱
printf(“%d”, c); // خروجی: ۱
آدرس متغیر c در اشاره گر pc قرار گرفته شده سپس *pc = 1; نوشته شده است. چون pc حاوی آدرس c می باشد در نتیجه مقدار c نیز تغییر می کند و برابر با ۱ می شود.
یک مثال دیگر،
int* pc, c, d;
c = 5;
d = -15;
pc = &c; printf(“%d”, *pc); // خروجی: ۵
pc = &d; printf(“%d”, *pc); // خروجی: -۱۵
آدرس c با دستور ;pc = &c در اشاره گر pc ذخیره شده است. بنابراین، چون مقدار c برابر با ۵ است پس *pc نیز ۵ برمی گرداند.
به همین ترتیب آدرس d با دستور ;pc = &d در اشاره گر pc ذخیره شده است. به دلیل اینکه مقدار d برابر با ۱۵- است پس *pc نیز ۱۵- را نشان می دهد.
مثال: کار با اشاره گرها
#include
int main()
{
int* pc, c;
c = 22;
printf(“Address of c: %pn”, &c);
printf(“Value of c: %dnn”, c); // 22
pc = &c;
printf(“Address of pointer pc: %pn”, pc);
printf(“Content of pointer pc: %dnn”, *pc); // 22
c = 11;
printf(“Address of pointer pc: %pn”, pc);
printf(“Content of pointer pc: %dnn”, *pc); // 11
*pc = 2;
printf(“Address of c: %pn”, &c);
printf(“Value of c: %dnn”, c); // 2
return 0;
}
خروجی
Address of c: 2686784
Value of c: 22
Address of pointer pc: 2686784
Content of pointer pc: 22
Address of pointer pc: 2686784
Content of pointer pc: 11
Address of c: 2686784
Value of c: 2
توضیح برنامه
int* pc, c;

در اینجا اشاره گر pc و متغیر معمولی c، هر دو از نوع int تعریف می شوند. چون pc و c مقداردهی اولیه نشده اند در نتیجه pc یا به هیچ آدرسی اشاره نمی کند یا دارای یک آدرس تصادفی است. متغیر c آدرس دارد اما حاوی یک مقدار زباله (مقدار پاک شده) تصادفی است.
c = 22;
در متغیر c مقدار ۲۲ ذخیره شده است. در واقع این مقدار در مکان متغیر c در حافظه قرار گرفته است.
pc = &c;

آدرس متغیر c به اشاره گر pc اختصاص داده شده است.
c = 11;

مقدار متغیر c به ۱۱ تغییر یافته است.
*pc = 2;

مقدار مکانی که آدرس آن در اشاره گر pc ذخیره شده برابر با ۲ شده است.
اشتباهات رایج هنگام کار با اشاره گرها
فرض کنید می خواهید اشاره گر pc به آدرس c اشاره کند. پس،
int c, *pc;
// نه c آدرس است اما pc
pc = c; // خطا
// نه *pc آدرس است اما &c
*pc = &c; // خطا
// هر دو آدرس هستند pc و &c
pc = &c;
// هر دو مقدار هستند *pc و c
*pc = c;
مثال زیر اغلب از نظر گرامری برای افراد مبتدی گیج کننده است.
#include
int main() {
int c = 5;
int *p = &c;
printf(“%d”, *p); // 5
return 0;
}
چرا هنگام استفاده از ;int *p = &c خطا رخ نداد؛ چون دستور
int *p = &c;
معادل دستورات زیر است:
int *p:
p = &c;
در هر دو حالت یک اشاره گر با نام p (نه *p) ساخته و c& به آن اختصاص داده شده است.
برای جلوگیری از این سردرگمی می توانید از دستور زیر استفاده کنید:
int* p = &c;
با اشاره گرها آشنا شدید، در آموزش بعدی ارتباط اشاره گرها با آرایه ها را بررسی خواهیم کرد.

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