PDA

View Full Version : heap memory چیست؟



mehdimdp
شنبه 07 فروردین 1389, 01:46 صبح
سلام
وقت همگی بخیر
لطفا اگه اطلاعاتی در زمینه با حافظه heap دارین در اینجا مطرح کنید.
با تشکر

amin1softco
شنبه 07 فروردین 1389, 14:27 عصر
والا گوگل چیز خوبیه ........



اختصاص حافظه داینامیک در C++


Dynamic Memory Allocation in C++



یکی از مباحث مهم و پرکاربرد در C++ تخصیص حافظه داینامیک برای متغیر ها می باشد. در حالت معمولی وقتی متغیری داخل برنامه تعریف می شود، آن متغیر داخل حافظه Stack قرار میگیرد. اما با اختصاص حافظه به صورت داینامیک برای یک متغیر آن متغیر داخل حافظه Heap قرار میگیرد. اختصاص حافظه داینامیک در زبان C++ کاربرد های زیادی دارد که در ادامه با برخی از آنها آشنا خواهیم شد. برای شروع بهتر است نگاهی به مفهوم حافظه های Stack و Heap داشته باشیم.
حافظه Stack: حافظه در داخل برنامه های C++ به دو بخش تقسیم می شود: حافظه Stack و حافظه Heap .حافظه Stack برای نگهداری متغیر های معمولی و نگهداری اطلاعات توابع در هنگام فراخوانی استفاده می شود. به طور کلی می توان گفت مهمترین کاربرد حافظه Stack در فراخوانی توابع و کنترل آنها می باشد. برای این که با طرز کار حافظه Stakc بیشتر آشنا شوید، می توان این بخش از حافظه را به ستونی از بشقاب های رو هم قرار گرفته تشبیه کرد. آخرین بشقابی که وارد شده، اول از همه از ستون بشقاب ها خارج می شود. این حالت به LIFO-Last In First Out معروف است. این مسئله در مورد توابع نیز سازگار است. زمانی که تابعی فراخوانی می شود این تابع به همراه تمامی متغیرهای محلی خودش در داخل حافظه Stack قرار می گیرد. با فراخوانی یک تابع جدید این تابع بر روی تابع قبلی قرار میگیرد و کار به همین صورت ادامه پیدا می کند. در حقیقت می توان گفت که بالاترین تابع در حافظه Stack تابعی است که هم اکنون در حال اجرا می باشد. زمانی که کار فراخوانی یک تابع تمام شد آن تابع به همراه تمام متغیرهای مربوطه از داخل حافظه Stack خارج می شود. برای روشن تر شدن موضوع حالت زیر را در نظر بگیرید. کلیه برنامه ها C++ با فراخوانی تابع main شروع میشوند. پس اولین تابعی که داخل Stack قرار میگیرد تابع main است. حال فرض کنید که بعد از تابع main تابع func1 و از داخل تابع func1 تابع func2 فراخوانی شود. در این حالت ابتدا تابع func1 در حافظه Stack روی تابع main و سپس تابع func2 بر روی تابع func1 میگیرد که حالت حافظه Stack به صورت زیر در می آید:

Stack Memory -> main() | func1 | fun2

پس از آنکه فراخوانی تابع func2 به پایان رسید این تابع از داخل حافظه Stack خارج شده و کنترل به تابع func1 باز می گردد و stack به صورت زیر در می آید:

Stack Memory -> main() | fun1

زمانی که فراخوانی تابع func1 به پایان برسد این تابع از حافظه stack خارج شده و کنترل به تابع main باز میگردد و این روند به همین صورت ادامه دارد تا زمانی که کار فراخوانی تابع main تمام شده و برنامه به اتمام رسد. با اتمام فراخوانی تابع main این تابع از از Stack خارج شده و کنترل به سیستم عامل باز می گردد. برای آشنایی بیشتر با ساختار Stack می توانید به یکی از کتابهای ساختمان داده ها مراجعه کنید.

حافظه Heap: حافظه Heap بخشی جداگانه از حافظه است که هیچ وابستگی به تابع های فراخوانی شده برنامه ندارد. متغیرها به صورت معمولی در این بخش قرار نمی گیرند و باید برنامه نویس به صورت دستی متغیرها رو داخل این بخش تعریف کند. به همین دلیل مدیریت حافظه Heap داخل برنامه باید به صورت دستی توسط برنامه نویس انجام شود. متغیرهای تعریف شده داخل حافظه Heap با اتمام فراخوانی یک تابع از بین نمی روند و تا زمانی که برنامه نویس خود این متغیر را از داخل حافظه Heap پاک نکند باقی خواهند ماند.

با تعاریفی که بالا شد، می توانیم به مبحث اختصاص داینامیک حافظه در C++ بپردازیم. در حالت عادی ما متعیر ها را به صورت زیر تعریف می کنیم:




C++ Code
int myInt = 20;


با این تعریف متغیر myInt داخل حافظه Stack قرار میگیرد که در بالا خواص مربوط به حافظه Stack مطرح شدند. حال اگر ما بخواهیم این متغیر داخل حافظه Heap قرار بگیرد به چه صورت باید عمل کنیم؟ انجام این کار به راحتی و بوسیله دستور new امکان پذیر است.
برای اینکه بتوانیم متغییر myInt را که در بالا تعریف کردیم داخل حافظه Heap قرار دهیم از دستور زیر استفاده می کنیم:




C++ Code
int* myInt = new int(10);


همچنین دستور بالا را می توان به صورت زیر نیز نوشت:




C++ Code

int* myInt;
myInt = new int(10);


با نوشتن هر یک از دستورات بالا متغیر myInt به جای اینکه در حافظه Stack قرار بگیرد، در حافظه Heap فضایی برای آن اختصاص داده خواهد شد. اما نکته مهمی که باید به آن توجه شود این است که متغیر myInt در ابتدا به عنوان یک اشاره گر تعریف شده است. پس برای استفاده از مقدار آن باید از علامت * که به آن dereference نیز می گویند استفاده کرد. فرض کنیم که بخواهیم مقدار myInt را بر روی صفحه چاپ کنیم. باید به صورت زیر بنویسیم:




C++ Code
cout << *myInt << endl;


اما همانطور که در بالا اشاره شد، بعد از اختصاص حافظه داینامیک برای یک متغیر، خود برنامه نویس باید به صورت دستی این متغیر را از حافظه پاک کند. این کار با دستور delete امکان پذیر است. برای مثال، برای حذف متغیر myInt به صورت زیر می نویسیم:




C++ Code
delete myInt;


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




C++ Code

int main()

{

int arraySize;

cout << "Enter array size: ";

cin >> arraySize;

int* myList = new int[arraySize];

delete [] myList;

return 0;
}


در قطعه کد بالا اندازه آرایه از کاربر پرسیده شده و آرایه ای با اندازه وارد شده ایجاد می شود. با هر بار اجرای برنامه کاربر می تواند یک سایز دلخواه را وارد کند و برنامه آرایه ای با اندازه وارد شده ایجاد کند. به دستوری که آرایه را از حافظه پاک می کند توجه کنید. در صورتی که متغیر تعریف شده آرایه باشد، برای پاک کردن آن از حافظه Heap حتما" باید بعد از دستور delete علامت [] قرار گیرد تا برنامه بداند که باید یک آرایه را از حافظه Heap پاک کند.
مطلب بعدی اختصاص حافظه داینامیک برای آرایه های چند بعدی است. اختصاص حافظه داینامیک برای آرایه های چند بعدی کمی پیچیده تر از اختصاص حافظه داینامیک برای آرایه های یکی بعدی است. برای اختصاص حافظه داینامیک آرایه چند بعدی باید هر یک از خانه های آرایه تعریف شده، خود نیز اشاره گر باشند. یعنی برای هر یک از خانه های آرایه دوباره تخصیص حافظه مجدد صورت گیرد. برای اینکه مسئله روشن تر شود به مثال زیر توجه کنید.
فرض کنید می خواهیم داخل یک برنامه آرایه ای دو بعدی ایجاد کنیم و این آرایه باید داخل حافظه Heap قرار گیرد. تعداد سطر ها و ستون ها در هنگام اجرای برنامه از کاربر پرسیده میشود و بر اساس مقادیر ورودی آرایه مورد نظر ایجاد می شود. قطعه کد زیر این کار را انجام میدهد. به نحوه تعریف متغیر myList در خط هفتم توجه کنید، این متغیر آرایه ای است که هر خانه از آن خود از نوع اشاره گر تعریف شده است:




C++ Code








1


2


3


4


5


6





7





8


9





10





11


12


13

int main()

{

int rows = 0;

int cols = 0;

cout << "rows: ";

cin >> rows;

cout << "cols: ";

cin >> cols;



int** myList = new int*[rows];



for(int i = 0; i < rows; i++)

myList[i] = new int[cols];



myList[1][2] = 7;



for( int i = 0 ; i < ROWS ; i++ )

delete [] dynamicArray[i] ;

delete [] dynamicArray;



return 0;
}


در خط های 1 تا 6 متغیرهای rows و cols تعریف شده اند و داخل آنها مقادیر مربوط به سطر و ستون که توسط کاربر وارد می شود قرار میگیرد. تعریف خط 7 بسیار مهم است که در بالا نیز در مورد آن توضیح داده شد. در این خط از برنامه تعداد سطر های آرایه تعریف می شوند که هر یک خود از نوع اشاره گر اند. در خط 8 و 9 با یک حلقه ستون های مربوط به آرایه دو بعدی ایجاد می شوند. برای مثال اگر کاربر مقدار 4 برای تعداد سطرها و تعداد 5 برای تعداد ستون ها را وارد کند در خط هفتم آرایه ای با 4 خانه ایجاد می شود که هر یک از خانه های آن خود تعریف یک اشاره گر از نوع int هستند و با دستورات خط های 8 و 9 برای هر یک از خانه های آرایه حافظه ای با تعداد خانه های cols اختصاص داده میشود. دستور خطوط 11، 12 و 13 برای پاک سازی حافظه بسیار مهم است و نباید فراموش شود. ابتدا آرایه های مربوط به هر یک از خانه های myList پاک شده و سپس خود آرایه myList از داخل حافظه پاک می شود.
اختصاص حافظه برای آرایه های بیشتر از 2 بعد پیچیده تر هستند.
یکی دیگر از کابردهای اختصاص حافظه داینامیک ایجاد آرایه هایی با تعداد خانه های بسیار بالا است. در حالت عادی اگر بخواهیم آرایه ای با تعداد خانه های بسیار بالا مثلا 500000 خانه ایجاد کنیم ممکن است با پیغام Stack Overflow مواجه شویم. در حالی که با اختصاص حافظه داینامیک برای یک آرایه همچین پیغامی دریافت نخواهیم کرد. البته این مورد کاربرد های زیادی ندارد و به ندرت استفاده می شود یا شاید به هیچ عنوان مورد استفاده واقع نشود.

mehdimdp
یک شنبه 08 فروردین 1389, 00:41 صبح
سلام
از لطف شما متشکرم
و اما یک سئوال این مباحث فقط در C کاربرد دارند؟
مثلا در زبانهای یرنامه نویسی امروزه مثل دلفی و یا net. این مباجث حافظه پویا کاربرد دارند یا خیر؟؟؟

mehdimdp
یک شنبه 08 فروردین 1389, 01:05 صبح
و اما یک سئوال دیگه
من یه جورایی متوجه این خط نمی شم:


int** myList = new int*[rows];

اول اینکه منظور از **int چیه؟
یعنی یک اشاره گر دو بعدی ؟؟؟
با تشکر

r00tkit
یک شنبه 08 فروردین 1389, 01:19 صبح
int** myList = new int*[rows];

یعنی اشاره گر از نوع اشاره گر



مثلا در زبانهای یرنامه نویسی امروزه مثل دلفی و یا net. این مباجث حافظه پویا کاربرد دارند یا خیر؟؟؟

بله
http://msdn.microsoft.com/en-us/library/aa664786%28VS.71%29.aspx

mehdimdp
دوشنبه 23 فروردین 1389, 21:07 عصر
سلام
سایز و اندازه حافظه heap رو از کجا می شه فهمید؟
آیا برای همه برنامه ها یکسان هست؟
یعنی اگر 10 تا برنامه روی یک سیستم اجرا بشود. هرکدام یک هیپ و استک جداگانه دارند؟؟؟
یا کلا هر برنامه برای خودش یک محدوده ی خاصی را دارد.؟؟؟؟

khafan_bat
سه شنبه 24 فروردین 1389, 01:55 صبح
با سلام


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



text (code) segment
stack segment
heap segment



بخش کد جایی هستش که در واقع خود برنامه در اونجا قرار میگیره. (در اصل کد کامپایل شده ی برنامه به زبان ماشین. این بخش محل قرار گیری برنامه است)

بخش stack از حافظه جایی هست که متغیر ها ی اتوماتیک و توابع اونجا قرار میگیرند به علاوه ی data ها

بخش heap فضایی از حافظه هست که برای ساختن متغیر های پویا یا داینامیک استفاده میشه. برنامه نویس جوری برنامه رو مینویسه که حین اجرای برنامه متغیر ها یا ارایه هایی با طول های مختلف حین اجرا ی برنامه ساخته بشن یا از بین ببرن.

نکته ای که اینجا هست اینه که حافظه ی stack از heap سریع تر هستش اما کوچیکتره !

زبان های برنامه نویسی مبتنی بر شی گرایی بیشتر با حافظه ی heap سر و کله میزنن و اشیا اونجا ساخته میشن و از بین میرن. یکی از پر قدرتمند ترین این زبان ها ++C هستش.
دیگر زبان های شی گرا : جاوا ..

http://www.maxi-pedia.com/what+is+heap+and+stack


با یکسری از ابزار ها میتونی مقدار حافظه ی heap رو مشاهده کنی ... برو به این سایتی که معرفی کردم
(http://www.nirsoft.net/utils/heap_memory_view.html)
http://www.nirsoft.net/utils/heap_memory_view.html

Salar Ashgi
جمعه 27 فروردین 1389, 22:44 عصر
و اما یک سئوال دیگه
من یه جورایی متوجه این خط نمی شم:


int** myList = new int*[rows];
اول اینکه منظور از **int چیه؟
یعنی یک اشاره گر دو بعدی ؟؟؟
با تشکر

دوست عزیز ، میدونیم که * Type یعنی یک اشاره گر به نوع Type .

دستور *int یعنی یک اشاره گر به int و دستور **int یعنی یک اشاره گر به *int ، حال خود این

*int یک اشاره گر است به int ، پس **int میشود اشاره گر به اشاره گر .

فسقل‌ترین
سه شنبه 31 فروردین 1389, 19:15 عصر
خیلی ممنون از راهنمایی‌تون.
فقط می‌شه لطف کنید، شیوه‌ی ارسال یک آرایه‌ی دو بعدی پویا(مثل همینی که نوشتید) به یه تابع رو هم توضیح بدید. من هر کاری می‌کنم ازم ایراد می‌گیره.

با تشکر

r00tkit
سه شنبه 31 فروردین 1389, 19:36 عصر
دوست عزیز ، میدونیم که * Type یعنی یک اشاره گر به نوع Type .

دستور *int یعنی یک اشاره گر به int و دستور **int یعنی یک اشاره گر به *int ، حال خود این

*int یک اشاره گر است به int ، پس **int میشود اشاره گر به اشاره گر .

سلام دقیقا من هم منظورم همین بود ولی نمی دونم این متن رو از کجا اوردم ""اشاره گر از نوع اشاره گر"" بازم ممنون که تذکر دادین

برای اطلاعات بیشتر و فرق stack و heap و مباحث مربوط به اینها به فصل سوم کتاب Reversing: Secrets of Reverse Engineering مراجعه کنید (خودم تازه دارم می خونم)

همه شاد و خوشحال باشید

Mousavmousab
یک شنبه 10 دی 1391, 15:48 عصر
با سلام

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

ayub_coder
دوشنبه 23 دی 1392, 08:49 صبح
این فضاها رو از حافظه خود کامپایلر سی برامون میگیره؟

samanff2000
دوشنبه 28 مهر 1393, 14:41 عصر
با سلام
یه سئوال برام پیش اومده و اون اینه که برای هر برنامه ای یک مقدار خافظه heap مشخص شده مثلا 1000 حالا اگه ما در زمان اجرا بخواییم میزان این رو افزایش بدیم آیا امکان داره ؟
یا بهتر بگم آیا میشه در زمان اجرا حافظه را افزایش داد ؟ و اگه ممکنه یک مثال هم ازش بگید
یه سوال دیگه هم اینکه اینجوری که من متوجه شدم سیستم میاد در زمان نیاز از حافظه stack کم میکنه و به حافظ heap اضافه میکنه ، آیا این مسئله درسته و اگه هست اینم با یه مثال بیشتر توضیح بدید .

C3phalex1n_0x
دوشنبه 28 مهر 1393, 14:51 عصر
با سلام
یه سئوال برام پیش اومده و اون اینه که برای هر برنامه ای یک مقدار خافظه heap مشخص شده مثلا 1000 حالا اگه ما در زمان اجرا بخواییم میزان این رو افزایش بدیم آیا امکان داره ؟
یا بهتر بگم آیا میشه در زمان اجرا حافظه را افزایش داد ؟ و اگه ممکنه یک مثال هم ازش بگید
یه سوال دیگه هم اینکه اینجوری که من متوجه شدم سیستم میاد در زمان نیاز از حافظه stack کم میکنه و به حافظ heap اضافه میکنه ، آیا این مسئله درسته و اگه هست اینم با یه مثال بیشتر توضیح بدید .

سلام سامان؛ به نظر من شما باید در مورد حافظه ها بیشتر مطالعه کنید، اما به هر حال، حافظه هیپ، یا به عبارت دیگر Dynamic Memory Segment در حافظه کامپیوتر قسمتی هست که می توان از آن به صورت پویا حافظه یا سل گرفت. اما این مکانیزم را شما باید در کد برنامه خود پیاده سازی کرده باشید تا بتوانید از مزایای آن بهره مند شوید. در غیر اینصورت نمی توان بعد از اینکه برنامه کامپایل و اجرا شد از این حافظه استفاده کرد. مثلا ما از این حافظه در پیاده سازی لینکد لیست ها استفاده می کنیم، چونکه هیچگاه نمی دانیم لینکد لیست ما چند عنصر خواهد داشت، به همین دلیل نیاز داریم یک حافظه پویا به خدمت بگیریم. اینجا جایی است که هیپ می تواند به ما کمک کند.

اما پشته یا Stack ماجراش کمی پیچیده تر است. از پشته به عنوان بافر یا حافظه میانی یاد می شود، اما به چه دلیل؟! در برنامه نویسی ما از توابع استفاده می کنیم. این توابع باید به شکلی منایع داخلی محدوده خودشان را کنترل کنند، بدون اینکه با داده های برنامه های دیگر تداخل داشته باشند. اینجا حافظه پشته به کمک برنامه می آد و یک حافظه بافر برای آنها فراهم می کند. همچنین شایان ذکر است، توابع درون برنامه ها باید با هم دیگر تعامل داشته باشند، که پشته این مشکل را هم رفع می کند. پشته کارایی های زیادی دارد و از حافظه هیپ کاملا مجزا هست.

StandardCode
دوشنبه 28 مهر 1393, 17:23 عصر
با سلام
یه سئوال برام پیش اومده و اون اینه که برای هر برنامه ای یک مقدار خافظه heap مشخص شده مثلا 1000 حالا اگه ما در زمان اجرا بخواییم میزان این رو افزایش بدیم آیا امکان داره ؟
یا بهتر بگم آیا میشه در زمان اجرا حافظه را افزایش داد ؟ و اگه ممکنه یک مثال هم ازش بگید
یه سوال دیگه هم اینکه اینجوری که من متوجه شدم سیستم میاد در زمان نیاز از حافظه stack کم میکنه و به حافظ heap اضافه میکنه ، آیا این مسئله درسته و اگه هست اینم با یه مثال بیشتر توضیح بدید .

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

tara-km
جمعه 14 آذر 1393, 16:10 عصر
با سلام
در رابطه با heap سوالی داشتم
دونوع حافظه که برای heap اختصاص می دهیم داریم که یکیش صریح است که با malloc فراخوانی میشود اما یکی دیگر ضمنی است که در زبان های اسکریپتی استفاده میشود. در رابطه با نوع دوم که چگونه انجام میشود متاسفانه اطلاعاتی درگوگل پیدا نکردم . خواهشنا هر کسی میداند ممنون میشم کمکم کند ؟

StandardCode
شنبه 15 آذر 1393, 00:42 صبح
با سلام
در رابطه با heap سوالی داشتم
دونوع حافظه که برای heap اختصاص می دهیم داریم که یکیش صریح است که با malloc فراخوانی میشود اما یکی دیگر ضمنی است که در زبان های اسکریپتی استفاده میشود. در رابطه با نوع دوم که چگونه انجام میشود متاسفانه اطلاعاتی درگوگل پیدا نکردم . خواهشنا هر کسی میداند ممنون میشم کمکم کند ؟
مدیریت هیپ را مفسر زبان بر عهده میگیرد و شما به عنوان برنامه نویس معاف از سر و کله زدن با آن هستید. شما فقط همینو بدونی کافیه که در این زبانها وقتی میکن فلان type به صورت reference type هست یعنی در هیپ قرار میگیره.
موفق باشید.