شما اینجا هستید

خانه

آموزش ++C - اشاره گر (Pointer) - قسمت دوم

در قسمت قبل دیدیم که چگونه می توان یک آدرس (اشاره گر) در زبان C تعریف نمود.در این قسمت ادامه مباحث قبل را پی می گیریم و چند مثال را بررسی می کنیم.

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

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

در این جا یک مثال را بررسی می کنیم:

void swap_numbers(int a, int b)

{

int temp = a;

a = b;

b = temp;

}

int main()

{

int a = 12;

int b = 15;

cout << a;

cout << b;

swap_numbers(a,b);

cout  << a;

cout << b;

 

return 0;

}

اگر برنامه فوق را اجرا کنید، مشاهده می کنید مقادیر a , b هیچ تغییری نمی کند. دلیل آن هم واضح است. چرا که متغیر های a,b محلی هستند و هرکدام در بلوک خودشان معنا دارند. یعنی متغیر a تابع  swap_numbers با متغیر a تابع main متفاوت است. در نتیجه با مقدار دهی متغیر a در تابع swap_numbers ، با خارج شدن از تابع مقدار a تابع main همچنان مقدار قبلی خود را دارد.

حال می خواهیم با مفهوم اشاره گر ها این مسئله را اصلاح کنیم:

void real_swap_numbers(int *a, int *b)

{

int temp = *a;

*a = *b;

*b = temp;

}

 

int main()

{

int a = 12;

int b = 15;

cout << a;

cout << b;

real_swap_numbers(&a,&b);

cout  << a;

cout << b;

 

return 0;

}

اگر تابع real_swap_numbers را بجای  swap_numbers در تابع main صدا بزنید، مشاهده می کنید که مقادیر a,b عوض شده اند. کاری که در این تابع انجام داده ایم به این صورت است که،

بجای این که مقدار 2 متغیر a , b را به تابع swap پاس دهیم، آدرس های آن ها را پاس داده ایم. در نتیجه در تابع swap وقتی مقدار آن آدرس ها را قرار می دهیم، از تابع که خارج شویم و متغیر های محلی نیز پاک شوند، باز هم مقادیر خانه های a,b تابع main تغییر کرده اند.

12 3426F97D a

15 6563E65E b

فرض کنید خانه های a,b در حافظه به صورت فوق باشند، در این صورت زمانی که شما ادرس های آن ها را به هر تابعی پاس دهید، این آدرس ها کماکان به همان دو خانه خاص در حافظه اشاره دارد.

در نتیجه وقتی شما می نویسید *a = *b   در واقع یعنی مقدار خانه b را ( که می شود 15) در مقدار خانه با آدرس   3426F97D قرار دهد. در نتیجه می شود:

 

15 3426F97D a
15 6563E65E b

و با قرار دادن عدد temp ( که 12 است) در خانه b به نتیجه نهایی می رسیم:

15 3426F97D a
12 6563E65E b

 حال وقتی از تابع swap خارج می شویم، متغیر های محلی پاک می شوند ( یعنی a , b, temp). پاک شدن متغیر ها هم تاثیری بر مقادیر خانه های 3426F97D و 6563E65E ندارد.( چون a , b , temp خودشان متغیر های دیگری هستند و ربطی به این 2 ندارند.)

 

به طور خلاصه یکی از مهمترین کاربرد های اشاره گر که در مثال بالا یک نمونه ساده از آن را مشاهده کردید، این است که می توان  برای یک فضای ثابت از حافظه چندین متغیر تعریف نمود که همه آن متغیرها به همان خانه ثابت اشاره داشته باشند.

مثلا اگر بگوییم:

int a = 12;

int * a_ptr = &a;

int *a_ptr2 = &a;

// , ....

یعنی متغیر های a_ptr و a_ptr2 دو اشاره گر ( یا آدرس) هستند که هر دو مقدار یک آدرس یعنی آدرس خانه a را دارند. اصطلاحا هر دو متغیر a_ptr2 و a_ptr به یک خانه اشاره دارند.

در نتیجه این اشاره گر ها هر جای برنامه که باشند، داخل هر تابعی، محلی یا global، باز هم با تغییر مقدار اشاره گر ها ( یعنی a_ptr* یا a_ptr2*) مقدار خانه مورد نظرشان ( یعنی a) تغییر می کند.

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

تصور کنید که اگر نخواهید بوسیله آدرس حافظه این کار را انجام دهید. در این صورت باید  به ازای هر تغییر خاص، کل بلوک حافظه ی مورد نظر را به شی یا تابع مورد نظر پاس کنید ( کپی کنید) و پس از اعمال تغییر دوباره آن را به جایی که فرا خوانی کرده اید بر گردانید و روی متغیر مربوطه کپی نمایید. ( ByRef و ByVal در زبان Visual Basic دقیقا همین مفهوم است. وقتی شما شی را ByRef ارسال می کنید همه مقادیر کپی نمی شوند بلکه فقط آدرس آن ارسال می شود، برخلاف ByVal)

این قسمت را با مثال زیر به پایان می رسانیم:

می خواهیم مثال swap را بدون استفاده از اشاره گر بنویسیم. با این فرض که خواننده با مفهوم struct آشنا است، این مثال را ارایه می کنیم:

strcut NumberPair

{

int   first;

int   second;

}

NumberPair swap_numbers_by_value(NumberPair nums)

{

NumberPair swapped;

swapped.first = nums.second;

swapped.second= nums.first;

 

return swapped;

}

 

int main()

{

int a = 12;

int b = 15;

cout << a;

cout << b;

 

NumberPair nums;

nums.first = a;

nums.second = b;

nums = swap_numbers_by_value(nums);

a = nums.first;

b = nums.second;

 

cout  << a;

cout << b;

 

return 0;

}

 

 

مثال فوق یک روش پیچیده و کند برای جا به جا کردن دو متغیر است. چرا که مقادیر ابتدا در یک بلوک حافظه بزرگ تر به نام nums ذخیره می شوند، سپس با صدا کردن تابع swap_numbers_by_value کل بلوک در متغیر محلی nums در تابع swap_numbers_by_value کپی می شوند. سپس یک بلوک حافظه جدید به نام swapped ایجاد شده و مقادیر اول و دوم به صورت برعکس داخل آن کپی می شود. سپس تابع swap_numbers_by_value بلوک حافظه swapped را به عنوان خروجی برگردانده و روی متغیر nums در تابع main کپی می شود. سپس مقادیر بلوک به متغیر های a , b داده می شوند.

مشاهده کردید که اگر نخواهیم از آدرس ها استفاده کنیم، چه مسیر پیچیده ای را باید دنبال کنیم. البته راه حل ساده تری هم با آرایه ها وجود دارد ولیکن چون آرایه ها هم به صورت داخلی از آدرس ها استفاده می کنند، باز هم جابه جایی by value نیست. در مورد ارتباط اشاره گر ها با آرایه ها در قسمت های بعدی صحبت خواهیم کرد.

 

در مورد دوستانی که با struct آشنا نیستند، در قسمت های بعدی مباحثی را هم به اشیا و شی گرایی اختصاص خواهیم داد.

 

سطح آموزشی: [3]   متوسط   (Intermediate)

 

 

 

 

دیدگاه‌ها

rajebe & ham tu mabhase esharegarha tozih bedin plz

 

آقا امیدوارم در مراحل زندگی موفق باشید.

بسیار عالی و مفید بود.

Khyli Ali bud mrc

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

 

با سلام و خسته نباشید

لطفا درباره اشاره گر ب اشاره گر هم توضیح بدید با یک مثال کاربردی

ممنون

افزودن نظر جدید

درباره من

سلام. به وب سایت شخصی من خوش آمدید.

محمد ذات خواهی هستم متولد سال 66 اهل رشت. سال 89 از دانشگاه شریف با مدرک لیسانس مهندسی شیمی و سال 91 از دانشگاه علم و صنعت ایران با مدرک ارشد کنترل و شبیه سازی فرایند فارغ التحصیل شدم، و در حال حاضر به عنوان مهندس نرم افزار در شرکت گسترش فرایند شریف هستم. از اونجایی که از دبیرستان به الگوریتم و برنامه نویسی علاقه داشتم از همون موقع تو این حیطه کار می کنم. بیشترین توانایی من در طراحی نرم افزار های مهندسی شیمی، طراحی وب و کلا کد نویسی هست. خوش حال میشم بتونم به دیگران کمک کنم خصوصا در حوزه آموزش. سعی می کنم تو این وب سایت مطالب و تجربه هایی که فکر می کنم به درد علاقه مندان به حوزه نرم افزار و برنامه نویسی بخوره قرار بدم. امیدوارم شما هم با نظرات و انتقادات و بحث های علمی تون در ارتقای محتوی علمی این وب سایت من رو یاری کنید.

 

Mohammad Zatkhahi Image

 

.

?>