جایگاه اشاره گرها در زبان C

بعد از آشنایی با مبحث آرایه ها در این جلسه به موضوع اشاره گرها و جایگاه اشاره گرها در زبان C، خواهیم پرداخت. اشاره گر متغیری است که آدرس متغیر دیگری را نگه می دارد که اصطلاحا گفته می شود دارد به آن متغیری اشاره می کند. به دو عملگر * (address-of operator) و & (indirection operator) با اشاره گرها نیاز می شود. اشاره گرها روش قدرتمند و انعطاف پذیری برای کار با انواع داده ها در برنامه نظیر آرایه ها، رشته ها و ساختمان ها است.

جایگاه اشاره گرها در زبان C

۱ تعریف اشاره گر ها

اشاره گرها، یکی از ویژگی های قدرتمند زبان C و ++C هست که آنها ها را از سایر زبان های برنامه نویسی مثل پایتون و جاوا متفاوت میکند. از اشاره گرها برای دسترسی مستقیم  به حافظه استفاده می شود. اکثر مبتدیان در درک مناسب اشاره گرها مشکل دارند. متغیرهای استاندارد برچسب هایی هستند که برای تعیین بخش هائی از حافظه کنار گذاشته می شوند تا داده از نوع خاصی را ذخیره کنند درحالیکه یک اشاره گر به بخش هایی از حافظه که توسط متغیر دیگری اشغال شده اشاره می کند. یک متغیر استاندارد یک مقدار را ذخیره می کند اما یک اشاره گر آدرس محلی در حافظه را ذخیره می کند.در دانش برنامه نویسی ، اشاره گر به نوعی از داده می گویند که به محل ذخیره داده ای دیگر بر روی حافظه اشاره می کند و به محتویات آن داده دسترسی دارد .

 چرا از اشاره گر ها در زبان برنامه نویسی استفاده می کنیم؟

 از اشاره گرها به صورت عمده برای تسریع در روند برنامه ، مخصوصاً تغییر محتوای داده های دیگر ، تخصیص حافظه به داده ها به صورت هوشمند و پویا ، دسترسی و تغییر محتویات داده های تابع استفاده می شود . ایجاد و استفاده از اشاره گرها ، شیوه ای در زبان C می باشد که آن را به زبان های سطح پائین ، نزدیک می نماید و از این روی درک مفهوم اشاره گر کمی نسبت به مباحث دیگر ، زمان بیشتری می برد . اما فراموش نکنید که اگر مفهوم اشاره گر را درک نکنید و نخواهید از آن در برنامه نویسی خود استفاده کنید ، در واقع قادر به نوشتن برنامه های روزمرّه کامپیوتری نخواهید بود و تنها قابلیت های کوچکی از زبان C را می توانید به کار ببندید. پس، مزیت استفاده از اشاره گر ها عبارتند از :

  • بهبود کارائی بسیاری از توابع
  • دسترسی آسان به عناصر آرایه ها
  • تسهیل انجام کاربارشته هاو آرایه ها
  • انتقال آرایه هاورشته ها به توابع
  • ارسال آرگومان ها ازطریق فراخوانی با ارجاع
  • تخصیص حافظه پویا
  • ایجاد ساختمان داده هائی نظیرلیستهای پیوندی

۱-۱– متغیر های اشاره گر

اشاره گر می تواند در متغیری ذخیره شود .اما ,با اینکه اشاره گر یک آدرس حافظه است و آدرس حافظه نیز یک عدد است ,ولی نمی توان آن را در متغیرهایی از نوع int ، double و یا غیره ذخیره کرد. متغیری که می خواهد اشاره گر را ذخیره کند باید هم نوع با اشاره گر باشد. این متغیر ها را متغیرهای اشاره گر گویند. برای تعریف متغیرهای اشاره گر در زبان c به صورت زیر عمل می شود:

; متغیر∗  نوع

data_type* pointer_variable_name;
int* p;

متغیر های اشاره گر

در تعریف متغیر اشاره گری که بخواهد آدرس متغیرهایی را نگه داری کند, باید نوع متغیر اشاره گری را هم نوع با آن متغیر ها در نظر گرفت و در کنار متغیر اشاره گر,علامت ∗ را قرار داد. در زبان C اشاره گرها را با اپراتور مشخص می کنند. اشاره گر ها خود نوعی متغیر هستند پس باید قبل از هر کاری آن ها را تعریف کرد. در زیر تعریف چند اشاره گر به عنوان نمونه آورده شده است:

int *ptr1,*ptr2;
char *ch1, ch2;
float *fptr;

کامپایلر برای هر کدام از متغیرها فضایی در حافظه اختصاص می دهد. فرض کنید متغیر a در خانه ۱۰۰  و متغیر a_ptr که اشاره گر می باشد در خانه ۲۰۰ از حافظه قرار دارند. با دستور اول محتویات خانه ۱۰۰ از حافظه که به متغیر a اختصاص داده شده است برابر ۲۰ قرار می گیرد. با دستور دوم اشاره گر a_ptr تعریف شده و آدرس ۲۰۰ حافظه به آن اختصاص داده می شود. دقت کنید چون اشاره گر a_ptr قرار است به متغیرa که  از نوع کاراکتر می باشد اشاره کند باید خودش هم کاراکتر تعریف شود. با دستور سوم آدرس متغیر a که همان ۱۰۰ می باشد در متغیر a_ptr یا همان آدرس ۲۰۰ ذخیره می شود.

نکته: با استفاده از یک اشاره گر می توان داده را از حافظه خواند و یا آن را در آدرسی از حافظه نوشت.

int main(){
 int var1;
 char var2[]={1,2,3,4};
 int *ptr1;
 char *ptr2;
 ptr1=&var1;
 ptr2=&var2[2];
 *ptr1=50;
 *ptr2=10;
 var2[1]=*ptr1;
 printf ( "\n var1 equals: %d", var1);
 printf ( "\n var2[1] equals: %d", var2[1]);
 printf ( "\n var2[2] equals: %d", var2[2]);
}

چهار خط اول تابع main مربوط به تعریف متغیرهاست. با این کار کامپایلر برای هر یک از متغیرها بستگی به نوع آن ها خانه های حافظه به میزان مناسب اختصاص می دهد. بدون اینکه در پاسخ مسئله تاثیری داشته باشد.دستور var2[1]=*ptr1; به این معنی است که [var2[1  را برابر محتویات آدرسی که ptr1 به آن  اشاره می کند (یعنی مقدار ۵۰) قرار بدهد.
خروجی کد بالا:

2
3
	
var1 equals: 50
var2[1] equals: 50
var2[2] equals: 10

نکته: وقتی یک اشاره گر به صورت ptr* در سمت چپ یک تساوی به کار می رود، داده در آدرسی که اشاره گر به آن اشاره می کند نوشته می شود. وقتی یک اشاره گر به صورت ptr* در سمت راست یک تساوی به کار می رود، داده از آدرسی که اشاره گر به آن اشاره می کند خوانده می شود.

#include <stdio.h>
int main(){
   int* pc;
   int c;
   c=22;
   printf("Address of c:%u\n",&c);
   printf("Value of c:%d\n\n",c);
   pc=&c;
   printf("Address of pointer pc:%u\n",pc);
   printf("Content of pointer pc:%d\n\n",*pc);
   c=11;
   printf("Address of pointer pc:%u\n",pc);
   printf("Content of pointer pc:%d\n\n",*pc);
   *pc=2;
   printf("Address of c:%u\n",&c);
   printf("Value of c:%d\n\n",c);
   return ;
}

توضیح:
خط ۴:  یک اشاره گر با نام pc و در خط ۵ نیز یک متغیر عادی با نام c ایجاد شده است. چون pc مقدار دهی اولیه نشده اند ، اشاره گر pc به آدرس خاصی اشاره نمیکند و اگر هم اشاره کند به یک آدرس random اشاره میکند. در حالی که متغیر c به یک آدرس مشخص اشاره میکند ولی دارای مقداری random است.
خط ۶: مقدار متغیر c برابر ۲۲ قرار داده شده است. یعنی عدد ۲۲ در آدرسی که متغیر c دارد ذخیره شده است. توجه داشته باشید که وقتی مینویسیم c& ( آدرس c ) باید از u% استفاده کنیم نه d% . چون آدرس همیشه یک عدد مثبت هست.
خط ۹:  آدرس متغیر c برابر اشاره گر pc قرار داده شده است. در خروجی هم همونطور که مشاهده می کنید ، ارزش ( مقدار) pc برابر آدرسی است که c در آن ذخیره شده است و محتوای pc ( یعنی pc*) برابر محتوای c یعنی ۲۲ است.
خط ۱۵: محتوای آدرسی که pc به اون اشاره میکرد تغییر کرده. مقدار این محتوا برابر ۲ شده است.

خروجی کد بالا:

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

۱-۲ عملگرهای اشاره گر

عملگر & (آدرس) و ∗(محتویات)

هر یک از این دو عملگر یک عملوند دارند . عملگر & ، آدرس عملوند خودش را مشخص می کند. عملگر محتویات جایی را مشخص می نماید که عملوندش به آن اشاره می کند.

عملگرهای اشاره گر اشاره گرها در زبان c دارای نوع اند. یعنی وقتی اشاره گری از نوع int تعریف می شود، باید به متغیر هایی از نوع int اشاره نماید و اشاره گر از نوع double باید به متغیر هایی از همین نوع اشاره نماید. اگر نوع متغیری که آدرس آن در یک اشاره گر قرار می گیرد، با نوع اشاره گر یکسان نباشد، کامپایلر زبان c خطایی را اعلام نمی کند.متغیر های اشاره گر

۲ اعمال روی اشاره گرها
تعداد اعمالی که می توان روی اشاره گر ها انجام داد بسیار کمتر از اعمالی است که روی متغیرهای دیگر می توان انجام داد.

۲-۱ عمل انتساب اشاره گرها به یکدیگر

اگر x و y دو متغیر باشند، دستور x=y آنچه را که در y قرار دارد در x قرار می دهد. این عمل را انتساب y به x گویند. دو اشاره گر را نیز می توان به یکدیگر نسبت داد. در این صورت هر دو اشاره گر به یک محل از حافظه اشاره خواهند کرد.

int table[5]={1,4,5,2,3};
int *p,*q;
p=table;
q=p;
printf(“%d”,*(q+1));

عمل انتساب اشاره گرها به یکدیگر

int *p1, *p2, *p3, a, b, c;
a = 50;
b = 100;
c = 200;
p1 = &a;  // p1 = Address a and *p1 = 50 and assume that &p1 = 10000
p2 = &b;  // p2 = Address b and *p2 = 100 and assume that &p2 = 10020
p3 = &c;  // p3 = Address c and *p3 = 200 and assume that &p3 = 10040
*p1 = *p2;   // *p1 = *p2 = 100 and &p1 = 10000 and &p2 = 10020
p1 = p3;   // &p1 = &p3 = 10040 and *p1 = *p3 = 200

در کد بالا و در خط ۵ اشاره گر p1 به جایی که متغیر a در حافظه ذخیره شده اشاره می کند بنابراین *p1 برابر با مقدار آن آدرس یعنی ۵۰ خواهد شد. در دستور خط بعدی آدرس جایی که متغیر b در آنجا ذخیره شده درون اشاره گر p2 قرار می گیرد یعنی به آن آدرس اشاره می کند و چون محتویات آن آدرس برابر با ۱۰۰ است پس مقدار *p2 برابر با ۱۰۰ می شود. در سطر ۸ با نوشتن این دستور فقط محتویات جایی که p1 به آنجا اشاره می کند درون درون جایی که p2 به آنجا اشاره می کند ریخته می شود و آدرس دو اشاره گر تغییر نخواهد کرد. اما در سطر ۹ آدرس جایی که p3 به آنجا اشاره می کند در اشاره گر p1 قرار می گیرد، بنابراین جایی که p1 به آنجا اشاره می کرد برابر با جایی می شود که p3 اشاره می کند که به ازاء این تغییر بنابراین محتویات جایی که p1 به آنجا اشاره می کرد به مقدار ۲۰۰ تغییر می کند.
۲-۲– اعمال محاسباتی جمع و تفریق
اعمال جمع و تفریق را می توان بر روی اشاره گرها انجام داد. با افزایش یک واحد و به اشاره گر به اندازه طول نوع اشاره گر به آن اضافه می شود.با کاهش یک واحد از اشاره گر به اندازه طول نوع اشاره گر از آن کم می شود.
اعمال جمع و تفریق بر روی اشاره گرها
۲-۳– عمل مقایسه اشاره گرها
گر p1 و p2 دو اشارگر باشند، با استفاده از عملگرهای رابطه ای مانند متغیر های معمولی با هم مقایسه می شوند. یعنی مقدار آدرس هر یک از اشاره گرها، عدد بزرگتری باشد، آن اشاره گر بزرگتر خواهد بود.

int table[5]={1,4,5,2,3};
int *p,*q;
p=table;
q=&table[1];
…
if(p!=q) …

مثال: با استفاده از دستورات زیر، دو اشاره گر p1 و p2 با هم مقایسه می شوند. فرض کنید p1 به محل ۱۰۰۰ حافظه و p2 به محل ۱۰۰۲ حافظه اشاره می کند. در این صورت شرط (p1==p2) ارزش نادرستی دارد.

عمل مقایسه اشاره گرها

به کاربرد دوگانه اپراتور * در زمینه اشاره گرها دقت نمائید:
 
void change(int *p,int *q)
{ int t;
t=*p;
*p=*q;
*q=t;
}

توضیح:
خط ۱: تعریف یک متغیر از نوع اشاره گر با استفاده از اپراتور *
خط ۳: دسترسی به محتوی حافظه از طریق اپراتور *

درباره نویسنده: administrator

ممکن است دوست داشته باشید

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

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