লেখক পরিচিতি
লেখকের নাম:
আহমেদ ওয়াহিদ মাসুদ
মোট লেখা:৯৮
লেখা সম্পর্কিত
পাবলিশ:
২০১৪ - সেপ্টেম্বর
সহজ ভাষায় প্রোগ্রামিং সি/সি++
প্রোগ্রামিংয়ে মেমরি নিয়ে কাজ করা ঝুঁকিপূর্ণ। কারণ, মেমরির কোড যদি নিয়মমতো না হয়, তাহলে মেমরির ডাটা নষ্ট হওয়ার সম্ভাবনা থাকে। তাই আধুনিক ল্যাঙ্গুয়েজে মেমরির কাজের ওপর সতর্কতা অবলম্বন করা হয়েছে, যাতে ইউজারের মেমরি নিয়ে কোনো কাজ করার দরকার না হয়। সি-তে এসব মেমরির কাজের জন্য কিছু ফাংশন আছে। এ লেখায় এগুলো নিয়ে আলোচনা করা হয়েছে।
স্ট্যাটিক ও ডায়নামিক মেমরি অ্যালোকেশন : প্রোগ্রামে যেসব ভেরিয়েবল ডিক্লেয়ার করা হয়, প্রোগ্রাম চলার সময় সেসব ভেরিয়েবলের টাইপ অনুযায়ী প্রয়োজনীয় সংখ্যক বাইট মেমরিতে নির্ধারিত হয়। যেমন, একটি ক্যারেকটার ভেরিয়েবল ডিক্লেয়ার করা হলে প্রোগ্রাম চলার সময় এর জন্য মেমরিতে ১ বাইট জায়গা দখল করা হবে। সুতরাং, এই প্রোগ্রাম চলার সময় এই ভেরিয়েবলে কোনো ডাটা রাখা হলে মেমরির ওই নির্দিষ্ট জায়গায়ই রাখা হবে এবং ডাটা পড়ার ক্ষেত্রেও একই ঘটনা হবে। আবার যদি কোনো অ্যারে ডিক্লেয়ার করা হয়, তাহলে এর জন্যও একইভাবে জায়গা নির্ধারণ করা হবে এবং কাজে লাগানো হবে। যদিও অ্যারের প্রতিটি এলিমেন্টের জন্য মেমরি ডিক্লেয়ার করা হয়নি।
প্রোগ্রাম লেখার সময় যে পদ্ধতিতে এসব ভেরিয়েবল ডিক্লেয়ার করা হয় এবং একই সাথে এদের জন্য যায়গা নির্ধারণ করা হয়, তাকে বলে স্ট্যাটিক মেমরি অ্যালোকেশন। এ পদ্ধতিতে ইউজার নিজেই ঠিক করেন, কোন ধরনের ভেরিয়েবল ব্যবহার করা হবে এবং কম্পাইলার নিয়ম অনুযায়ী সেই ভেরিয়েবলের জন্য জায়গা নির্ধারণ করে নেয়। যদিও এ ক্ষেত্রে ইউজার জানতে পারেন ভেরিয়েবলের জন্য কী পরিমাণ জায়গা ব্যবহার হচ্ছে। এই স্ট্যাটিক পদ্ধতির একটি বড় অসুবিধা আছে। ধরা যাক, প্রোগ্রামে কোনো নির্দিষ্ট ডাটা টাইপের এক হাজার ডাটা নিয়ে কাজ করতে হবে। এ ক্ষেত্রে প্রোগ্রামে ওই ডাটা টাইপের এক হাজার এলিমেন্ট সহকারে একটি অ্যারে ডিক্লেয়ার করা যায়। কিন্তু প্রোগ্রাম লেখার সময় দেখা গেল এক হাজার ভেরিয়েবল দিয়ে কাজ সম্পূর্ণ হচ্ছে না, বরং দুই হাজার ভেরিয়েবল দরকার। ইউজার এ ক্ষেত্রে এক হাজারের জায়গায় দুই হাজার দিয়ে অ্যারে ডিক্লেয়ার করে দিতে পারেন। কিন্তু আরও দেখা গেল, প্রোগ্রাম চলার পর কখনও ৫০টি, আবার কখনও ১০০টি ভেরিয়েবল ব্যবহার হচ্ছে। আবার কখনও সবগুলোই ব্যবহার হচ্ছে। অর্থাৎ কতগুলো ভেরিয়েবল হবে, তার কোনো নিশ্চয়তা নেই। এখন যদি ৫০টি ভেরিয়েবল ব্যবহার হয়, তাহলে বাকি ১৯৫০টি ভেরিয়েবল অব্যবহৃত অবস্থায় থাকবে। অর্থাৎ ওই অব্যবহৃত ভেরিয়েবলগুলো যে মেমরি দখল করে আছে, তা কোনো কাজে লাগবে না। অন্য কেউ সেই অব্যবহৃত মেমরি ব্যবহারও করতে পারবে না। এখানে শুধু দুই হাজার ভেরিয়েবলের কথা বলা হলো। কিন্তু বড় ধরনের সফটওয়্যার, যেমন আধুনিক কোনো গেমের ক্ষেত্রে আরও অনেক ভেরিয়েবল লাগতে পারে। এমনকি সাধারণ ভেরিয়েবলের বদলে অনেক বড় স্ট্রাকচারের অ্যারেও লাগতে পারে। সে ক্ষেত্রে প্রোগ্রাম চলার শুরুতেই যদি এতগুলো বিশাল আকারের ভেরিয়েবল ডিক্লেয়ার করা হয়, তাহলে তা অকারণে অনেক মেমরি দখল করে রাখবে। এখন কমপিউটারে তো আর একটি করে কাজ হয় না। সবসময় অনেকগুলো প্রোগ্রাম চলতে থাকে, যার বেশিরভাগই সিস্টেম প্রোগ্রাম। এখন একটি প্রোগ্রামই যদি অনেক জায়গা দখল করে থাকে, তাহলে বাকি প্রোগ্রামগুলো তাদের প্রয়োজনের সময় জায়গা নাও পেতে পারে। এ ধরনের পরিস্থিতিতে সিস্টেম ক্র্যাশ করে অথবা কমপিউটার হ্যাং করে। এ কারণেই পুরনো উইন্ডোজগুলোতে বিশেষ করে এক্সপি বা তার আগের উইন্ডোজে কমপিউটার অনেক বেশি হ্যাং করত। কিন্তু নতুন উইন্ডোজ যেমন উইন্ডোজ ৮-এ অপারেটিং সিস্টেম এ ধরনের বিষয়গুলো আরও ভালোভাবে পরিচালনা করে বলে মেমরির জন্য সাধারণত কমপিউটার হ্যাং করে না। কিন্তু যা-ই করা হোক না কেন, একটি প্রোগ্রাম যদি লেখাই হয় এভাবে যে, সেটি অনেক জায়গা দখল করে রাখে, তাহলে কমপিউটার হ্যাং করার খুব বড় ঝুঁকি থাকে।
এ ধরনের সমস্যা দূর করার জন্য সি-তে ডায়নামিক মেমরি অ্যালোকেশনের ব্যবস্থা রাখা হয়েছে। এটি আধুনিক ল্যাঙ্গুয়েজের একটি অন্যতম বৈশিষ্ট্য। এমনকি অনেক ল্যাঙ্গুয়েজ আছে, যেখানে ভেরিয়েবল আগে থেকে ডিক্লেয়ার করার কোনো দরকার নেই। যেমন পিএইচপি বা জাভাস্ক্রিপ্ট ইত্যাদি। এসব ল্যাঙ্গুয়েজে ইউজারের যখন দরকার, তখনই কোনো একটি ভেরিয়েবল ডিক্লেয়ার করে সাথে সাথে ব্যবহার করা যাবে, যাতে এরকম মেমরি ওভারলোডের ঝুঁকি না থাকে। সি-তে ডায়নামিক্যালি মেমরি অ্যালোকেট করার জন্য সবচেয়ে বেশি যে ফাংশনটি ব্যবহার হয়, তার নাম malloc(); অর্থাৎ মেমরি অ্যালোকেশন। এটি একটি মেমরি ম্যানেজমেন্ট ফাংশন, যার প্রোটোটাইপ stdlib.h ফাইলে রাখা আছে। তাই ফাংশনটি ব্যবহার করতে হলে অবশ্যই এই হেডার ফাইল সংযোজন করে নিতে হবে। ম্যালোক ফাংশনের আর্গুমেন্ট হিসেবে যে সাইজ পাঠানো হবে, হিপ থেকে এটি সেই সাইজের জায়গা অ্যালোকেট করবে এবং একই সাথে অ্যালোকেট করা মেমরির অ্যাড্রেস রিটার্ন করবে। যেমন :
int* ptr;
ptr=(int*)malloc(sizeof(int));
এখানে ইন্টিজার ভেরিয়েবলের জন্য যে পরিমাণ মেমরির দরকার, ম্যালোক হিপ থেকে সে পরিমাণ জায়গা অ্যালোকেট করবে এবং ptr-কে সেই অ্যালোকেট করা জায়গার অ্যাড্রেস রিটার্ন করবে। খেয়াল রাখতে হবে, ম্যালোকের আর্গুমেন্ট হিসেবে শুধু একটি ইন্টিজার ভ্যালু দিতে হয়। যে ভ্যালু দেয়া হবে ম্যালোক সেই পরিমাণ জায়গাই অ্যালোকেট করবে। এখন ইউজার যদি একটি কাস্টম ভেরিয়েবল স্ট্রাকচারের মাধ্যমে ডিক্লেয়ার করেন, যার ডাটা টাইপের নাম দেয়া হলো cstm, তাহলে ম্যালোকের আর্গুমেন্ট হিসেবে sizeof(cstm) দিতে হবে। সাইজ অব ফাংশনের কাজ হচ্ছে এর আর্গুমেন্ট হিসেবে যে ডাটা টাইপ দেয়া হবে, এটি সেই ডাটা টাইপের জন্য প্রয়োজনীয় জায়গা কত বাইট তা রিটার্ন করবে। অবশ্যই বিল্টইন ডাটা টাইপের জন্য কী পরিমাণ জায়গা দরকার, তা সবারই জানা আছে। কিন্তু এই ফাংশনের প্রয়োজন হয় তখনই, যখন ইউজার কাস্টম ডাটা টাইপ ব্যবহার করেন।
উপরের উদাহরণে পয়েন্টারটি কোনো ভেরিয়েবলকে পয়েন্ট করছে না। বরং ম্যালোকের মাধ্যমে অ্যালোকেট হওয়া নামবিহীন কোনো মেমরিকে তা পয়েন্ট করছে। আর ম্যালোক ফাংশনের আগে পয়েন্টার ডাটা টাইপ ক্যাস্ট করা হয়েছে। ডাটা টাইপ ক্যাস্টিংয়ের কাজ মূলত ডাটা টাইপ কনভার্ট করা। অর্থাৎ ইউজার যদি একটি ডাবল টাইপের ভেরিয়েবলের মান দশমিক ছাড়া প্রিন্ট করতে চান, তাহলে তাকে ইন্টিজার হিসেবে ক্যাস্ট করে প্রিন্ট করতে হবে। যেমন, ডাবল টাইপ ভেরিয়েবল যদি ফনষ হয়, তাহলে printf(“%d”, (int)dbl); এভাবে টাইপ ক্যাস্টিংয়ের মাধ্যমে ডাবল টাইপ ভেরিয়েবলকে ইন্টিজার হিসেবে প্রিন্ট করা যাবে। আর ম্যালোকে শুধু সাবধানতার জন্য ক্যাস্টিং করা হয়েছে। কারণ, ম্যালোকের রিটার্ন করা মান অবশ্যই ptr টাইপের হতে হবে। মেমরির অ্যাড্রেস সবসময় ইন্টিজার হবে। আবার ইউজার চাইলে ম্যালোকের মাধ্যমে কোনো ডাটা টাইপের অ্যারেও ডিক্লেয়ার করতে পারেন। যেমন : ptr=(int*)malloc(5*sizeof(int)); এ ক্ষেত্রে হিপ থেকে একই পরিমাণ জায়গা পাঁচবার অ্যালোকেট করা হবে। এবার দেখানো হয়েছে কীভাবে এই পদ্ধতিতে কাস্টম ডাটা টাইপ অ্যালোকেট করা হয়।
struct cstm
{
int x;
double y;
};
struct cstm* ptr;
ptr=(struct cstm*)malloc(sizeof(struct cstm));
মূলত কাস্টম ডাটা টাইপের জন্যই ডায়নামিক অ্যালোকেশন সবচেয়ে বেশি ব্যবহার হয়। আর সে ক্ষেত্রে ওপরের নিয়মেই তা অ্যালোকেট করা হয়।
এটি ছাড়া আরও একটি মেমরি ম্যানেজমেন্ট ফাংশন আছে, যার নাম calloc();। এর কাজও একই ধরনের। ফাংশনটির প্রটোটাইপও একই হেডার ফাইলে রাখা আছে। ম্যালোকের মতো ক্যালোকও হিপ থেকে জায়গা অ্যালোকেট করে। তবে এদের মধ্যে পার্থক্য হলো- ম্যালোকের ক্ষেত্রে বলে দিতে হয় মোট কত বাইট জায়গা দখল করতে হবে। আর ক্যালোকের ক্ষেত্রে শুধু এলিমেন্ট সংখ্যা বলে দিলেই চলে। যেমন : ptr=(int*)calloc(10, sizeof(int));। খেয়াল রাখতে হবে এখানে আর্গুমেন্টে ১০ লেখার পর কমা দেয়া হয়েছে। অর্থাৎ ক্যালোকের আর্গুমেন্ট দুটি। আর ম্যালোকের ক্ষেত্রে এখানে ১০-এর পর গুণ চিহ্ন * ব্যবহার করতে হতো, অর্থাৎ ম্যালোকের আর্গুমেন্ট একটি।
এতক্ষণ দেখানো হলো কীভাবে ইউজার ডায়নামিক্যালি মেমরি অ্যালোকেট করতে পারেন। কিন্তু একটি উন্নতমানের প্রোগ্রামের বৈশিষ্ট্য হলো প্রোগ্রামটির যতগুলো ভেরিয়েবল দরকার, তা ডায়নামিক্যালি ডিক্লেয়ার করবে এবং কাজ শেষ হলে তা আবার ডিঅ্যালোকেট করে দেবে, যাতে অন্য প্রোগ্রাম ওই জায়গা ব্যবহার করতে পারে। সি-তে এ জন্য free(); নামে বিল্টইন ফাংশন রাখা হয়েছে। এর প্রোটোটাইপ stdlib.h এবং alloc.h এই দুটি হেডার ফাইলে রাখা আছে। তাই প্রোগ্রামে ব্যবহারের জন্য যেকোনো একটি হেডার ফাইল ইনক্লুড করলেই হবে। ফ্রি আর্গুমেন্ট হিসেবে পয়েন্টার পাঠাতে হয়। যেমন, আগের উদাহরণে ptr দিয়ে মেমরি অ্যালোকেট করা হয়েছে। তাই ওই মেমরি ডিঅ্যালোকেট করার জন্য free(ptr); লিখলেই হবে। যদিও এভাবে ফ্রি ব্যবহার করার কোনো বাধ্যকতা নেই, তবুও এটি ব্যবহার করা খুবই জরুরি। কারণ, মেমরি ডিঅ্যালোকেট করা না হলে তা ডাম্প হওয়ার সম্ভাবনা থাকে।
প্রোগ্রামের মেমরি ম্যানেজমেন্ট সবচেয়ে গুরুত্বপূর্ণ বিষয়গুলোর একটি। তাই বড় ধরনের প্রোগ্রাম বানাতে হলে অবশ্যই মেমরি সম্পর্কে ভালো ধারণা রাখতে হবে।
ফিডব্যাক : wahid_cseaust@yahoo.com