تخصیص حافظه پویا در برنامه نویسی C (کاربرد، نکات و مثال)

در این آموزش با تخصیص حافظه پویا در زبان C با استفاده از توابع کتابخانه ای استاندارد ()malloc()، calloc()، free و ()realloc آشنا خواهید شد.
تخصیص حافظه پویا در زبان C، مکانیزمی است که به برنامهنویس اجازه میدهد در حین اجرا و روشن بودن برنامه (Runtime)، بسته به نیازهای لحظهای سیستم، بخشهایی از حافظه را از سیستمعامل درخواست کرده و به تصرف خود درآورد، و هر زمان که دیگر به آنها نیازی نداشت، آن فضا را برای استفاده سایر برنامهها آزاد کند.
ویژگیهای بنیادین این مکانیزم
-
انعطافپذیری در زمان اجرا (Runtime Flexibility): در حالت عادی، اندازه متغیرها و آرایهها باید پیش از کامپایل شدن برنامه و به طور ثابت تعیین شود. اما تخصیص پویا این محدودیت را میشکند و به برنامه اجازه میدهد حجم دقیق حافظه مورد نیاز خود را دقیقاً در همان لحظهای که دادهها (مثلاً از طرف کاربر یا یک فایل) وارد سیستم میشوند، محاسبه و دریافت کند.
-
استفاده از منطقه آزاد حافظه (Heap): این نوع حافظه در بخش وسیع و مستقلی از رم سیستم به نام منطقه «هیپ» (Heap) رزرو میشود. برخلاف حافظه محلی توابع (Stack) که به شدت محدود است و به صورت خودکار مدیریت میشود، منطقه هیپ فضای بسیار گستردهتری را برای پردازش دادههای عظیم و سنگین در اختیار برنامه قرار میدهد.
-
وابستگی مطلق به اشارهگرها: زمانی که برنامهنویس حافظه پویا درخواست میکند، سیستمعامل هیچ نامی روی آن فضا نمیگذارد تا شما بتوانید مثل یک متغیر عادی صدایش بزنید. سیستمعامل فقط «آدرسِ نقطه شروعِ» آن بلوک خام از حافظه را تحویل میدهد. بنابراین، این فضاهای تازه متولد شده تنها و تنها از طریق اشارهگرها قابل دسترسی، ردیابی و مدیریت هستند.
-
مسئولیت و مدیریت دستی (Manual Management): بزرگترین تفاوت حافظه پویا در زبان C این است که سیستم هیچگونه زبالهروبِ خودکاری (Garbage Collector) ندارد. حافظهای که به صورت پویا دریافت میشود، تا ابد در تصرف برنامه میماند مگر اینکه برنامهنویس شخصاً و صراحتاً دستور آزادسازی آن را صادر کند. اگر برنامهنویس این کار را فراموش کند، پدیدهای مخرب به نام «نشت حافظه» (Memory Leak) رخ میدهد که به مرور تمام منابع سیستم را بلعیده و باعث فلج شدن دستگاه میشود.
هدف و فلسفه استفاده
فلسفه اصلی تخصیص پویای حافظه، سازگاری با عدم قطعیتِ دنیای واقعی و مصرف بهینه و قطرهچکانیِ منابع است.
در دنیای واقعی، یک نرمافزار پیش از اجرا هرگز نمیداند با چه حجمی از اطلاعات مواجه خواهد شد. اگر برنامهنویس برای احتیاط، از همان ابتدا یک حافظه ثابت و عظیم را برای برنامه رزرو کند، بخش زیادی از منابع سیستم قفل شده و به هدر میرود. از طرفی اگر فضای کوچکی در نظر بگیرد، برنامه با ورود دادههای بزرگتر دچار فروپاشی میشود.
مکانیزم تخصیص پویا، این دوراهی را به طور کامل از بین میبرد. این قابلیت به نرمافزار اجازه میدهد در زمان شروع به کار، کاملاً سبک و بدون مصرف حافظه اضافه اجرا شود و تنها زمانی که واقعاً نیاز بود، به اندازه دقیق و میلیمتریِ نیاز خود فضا دریافت کند. این رویکردِ «دریافت بر اساس تقاضا»، راز اصلی برنامههای نوشته شده با زبان C در مقیاسپذیری بینهایت آنهاست؛ برنامههایی که میتوانند از ریزتراشههای بسیار ضعیف تا ابرکامپیوترهای غولپیکر، با بالاترین بازدهی ممکن سختافزار کار کنند.
همانطور که می دانید، آرایه مجموعه ای از چند خانه متوالی و ثابت در حافظه است. هنگامی که اندازه یک آرایه تعریف شود، نمیتوانید آن را تغییر دهید.
گاهی اوقات ممکنه اندازه آرایه تعریف شده کافی نباشد. برای حل این مشکل می توانید حافظه را به صورت دستی و پویا در زمان اجرای برنامه تخصیص دهید. این به عنوان تخصیص حافظه پویا شناخته می شود.
برای تخصیص حافظه پویا از توابع کتابخانه ای malloc()، calloc()، free() و ()realloc استفاده می شود. این توابع در فایل هدر
()malloc در C
نام “malloc” نشان دهنده تخصیص حافظه است.
تابع ()malloc بلوکی از حافظه از چند بایت مشخص را ذخیره می کند.این تابع یک اشاره گر از نوع void را برمی گرداند که میتواند به هر نوعی از اشاره گرها تبدیل شود.
ساختار()malloc
ptr = (نوع*) malloc(اندازه);
مثال
ptr = (float*) malloc(100 * sizeof(float));
در این دستور ۴۰۰ بایت از حافظه تخصیص می دهد چون اندازه نوع float برابر با ۴ بایت است. اشاره گر ptr آدرس اولین بایت از حافظه تخصیص یافته را در خود نگه می دارد.
اگر نتواند حافظه ای تخصیص دهد، یک اشاره گر NULL برمی گرداند.
()calloc در C
نام “calloc” نشان دهنده تخصیص پیوسته است.
تابع malloc() پس از تخصیص حافظه، آن را بدون مقداردهی اولیه رها می کند. در حالیکه تابع ()calloc حافظه را تخصیص داده سپس همه عناصر را با صفر مقداردهی اولیه می کند.
ساختار()calloc
ptr = (نوع*)calloc(تعداد, اندازه);
مثال
ptr = (float*) calloc(25, sizeof(float));
این دستور ۲۵ عنصر از نوع float را در یک فضای پیوسته روی حافظه تخصیص می دهد.
()free در C
حافظه پویا تخصیص داده شده با ()calloc یا ()malloc آزاد نمی شود مگر اینکه برای آزادسازی فضا به طور صریح از تابع ()free استفاده کنید.
ساختار()free
free(ptr);
این دستور فضای اختصاص داده شده در حافظه با اشاره گر ptr را آزاد می کند.
مثال ۱:()malloc و ()free
// محاسبه مجموع اعداد وارد شده توسط کاربر
#include
#include
int main()
{
int n, i, *ptr, sum = 0;
printf(“Enter number of elements: “);
scanf(“%d”, &n);
ptr = (int*) malloc(n * sizeof(int));
// اگر حافظه تخصیص داده نشده است
if(ptr == NULL)
{
printf(“Error! memory not allocated.”);
exit(0);
}
printf(“Enter elements: “);
for(i = 0; i < n; ++i)
{
scanf(“%d”, ptr + i);
sum += *(ptr + i);
}
printf(“Sum = %d”, sum);
// آزادسازی حافظه
free(ptr);
return 0;
}
در اینجا تعداد n خانه از نوع int به صورت پویا از حافظه تخصیص داده شده است.
مثال ۲: ()calloc و ()free
// برنامه محاسبه مجموع اعداد وارد شده توسط کاربر
#include
#include
int main()
{
int n, i, *ptr, sum = 0;
printf(“Enter number of elements: “);
scanf(“%d”, &n);
ptr = (int*) calloc(n, sizeof(int));
if(ptr == NULL)
{
printf(“Error! memory not allocated.”);
exit(0);
}
printf(“Enter elements: “);
for(i = 0; i < n; ++i)
{
scanf(“%d”, ptr + i);
sum += *(ptr + i);
}
printf(“Sum = %d”, sum);
free(ptr);
return 0;
}
()realloc در C
اگر حافظه پویا تخصیص یافته کم یا بیش از حد نیاز باشد، می توانید اندازه حافظه تخصیص داده شده قبلی را با استفاده از تابع ()realloc تغییر دهید.
ساختار ()realloc
ptr = realloc(ptr, x);
در اینجا، ptr با اندازه جدید x دوباره تخصیص داده می شود.
مثال ۳: ()realloc
#include
#include
int main()
{
int *ptr, i , n1, n2;
printf(“Enter size: “);
scanf(“%d”, &n1);
ptr = (int*) malloc(n1 * sizeof(int));
printf(“Addresses of previously allocated memory: “);
for(i = 0; i < n1; ++i)
printf(“%un”,ptr + i);
printf(“nEnter the new size: “);
scanf(“%d”, &n2);
// تخصیص دوباره حافظه
ptr = realloc(ptr, n2 * sizeof(int));
printf(“Addresses of newly allocated memory: “);
for(i = 0; i < n2; ++i)
printf(“%un”, ptr + i);
free(ptr);
return 0;
}
خروجی
Enter size: 2
Addresses of previously allocated memory:26855472
۲۶۸۵۵۴۷۶
Enter the new size: 4
Addresses of newly allocated memory:26855472
۲۶۸۵۵۴۷۶
۲۶۸۵۵۴۸۰
۲۶۸۵۵۴۸۴

برای تخصیص حافظه پویا برای یک ساختار داده چه باید کرد؟
پاسخ