পয়েন্টার এবং রেফারেন্স (Pointers & References)
জিপিএস (GPS) এবং ডাকনাম (Nickname)
ধরুন আপনি ৪২ এলম স্ট্রিটে (42 Elm Street) থাকেন। একটি পয়েন্টার (pointer) হলো অনেকটা ওই ঠিকানাটিকে (address) কোনো একটি স্টিকি নোটে (sticky note) লিখে রাখার মতো — এটি কখনোই আপনার বাড়িটিকে নিজের ভেতরে ধারণ করে না (contain), এটি শুধু অন্য কাউকে বলে দেয় যে আপনাকে ঠিক কোথায় পাওয়া যাবে (where to find it)। অন্যদিকে একটি রেফারেন্স (reference) হলো অনেকটা আপনার কোনো একটি ডাকনামের (nickname) মতো — যদি আপনার বন্ধুরা আপনাকে "অ্যালেক্সান্ডার" নামের পরিবর্তে শুধু "অ্যাল" নামে ডাকে, তবে তারা কিন্তু ওই একই মানুষটির কথাই বলছে। এক্ষেত্রে আপনার ওই জিপিএসের মতো কোনো ঠিকানা বা অ্যাড্রেসের (address) প্রয়োজন নেই, শুধু আরেকটি নামের (another name) প্রয়োজন।
সি++ (C++)-এর ক্ষেত্রে মেমোরির সাথে সরাসরি কাজ করার জন্য মূলত এই দুটি ধারণা — তথা পয়েন্টার (pointers) এবং রেফারেন্সই (references) — হলো এর প্রধান ভিত্তি বা ফাউন্ডেশন (foundation)। চলুন এগুলো সম্পর্কে আরও বিস্তারিত জেনে নেওয়া যাক।
র পয়েন্টার (Raw Pointers): * এবং &
পয়েন্টার (pointer) হলো এমন এক ধরনের ভ্যারিয়েবল (variable) যা মূলত মেমোরির একটি ঠিকানা বা অ্যাড্রেসকে (memory address) স্টোর করে বা সংরক্ষণ করে রাখে। আপনি চাইলে * দিয়ে একটি পয়েন্টার ডিক্লেয়ার বা ঘোষণা (declare) করতে পারেন এবং & দিয়ে তার মেমোরির ঠিকানাটি (address) পেতে পারেন।
int* p— "এখানকার p হলো একটি int-এর পয়েন্টার (pointer to an int)"&x— "এটির মানে হলো x-এর ঠিকানা বা address"*p— "p-তে থাকা ওই ঠিকানার ভেতরের আসল মানটি বা value" (একে ডি-রেফারেন্সিং বা dereferencing বলা হয়)
এক্ষেত্রে আপনি &-কে "আপনি কোথায় থাকেন (where do you live)?" জিজ্ঞেস করা হিসেবে এবং *-কে "আমাকে আপনার ওই ঠিকানাটিতে যেতে দিন (let me visit that address)" বলা হিসেবে চিন্তা করতে পারেন।
বেসিক পয়েন্টার বা Pointer Basics
রেফারেন্স (References): একই জিনিসের অন্য নাম (Another Name for the Same Thing)
একটি রেফারেন্সকে (reference) মূলত & চিহ্নের সাহায্যে ঘোষণা (declared) করা হয় (হ্যাঁ, এটি ওই একই বা same symbol — তবে এখানে কন্টেক্সট বা context ভিন্ন)। একবার এটি কোনো কিছুর সাথে যুক্ত (bound) হয়ে গেলে, এর ওই রেফারেন্সটিই (reference) মূলত তার আসল বা মূল ভ্যারিয়েবল (original variable) হয়ে যায়। তাই এখানে পরিচালনা করার মতো কোনো আলাদা মেমোরি ঠিকানা (address) বা ডি-রেফারেন্সিংয়ের (dereferencing) প্রয়োজন পড়ে না।
বেসিক রেফারেন্স (Reference Basics)
পয়েন্টার ভার্সন (Pointer Version) বনাম (vs) রেফারেন্স ভার্সনের (Reference Version) অদলবদল (Swap)
কোনো কিছুর অদলবদল করা বা সোয়াপ (swap) করার একটি সাধারণ বা ক্লাসিক (classic) ফাংশন মূলত এই দুই ভার্সনের পার্থক্যটিকে খুব সুন্দরভাবে ফুটিয়ে তোলে। পয়েন্টারের (pointers) ক্ষেত্রে আপনাকে এর ঠিকানাগুলোকে (addresses) পাস (pass) করতে হয় এবং ডি-রেফারেন্স (dereference) করতে হয়। কিন্তু রেফারেন্সের (references) ক্ষেত্রে এর সিনট্যাক্সটি (syntax) অনেক বেশি ক্লিনার (cleaner) বা পরিষ্কার — এখানকার কম্পাইলারটিই (compiler) আপনার জন্য এর ভেতরের বিভিন্ন অপ্রত্যক্ষ (indirection) কাজগুলোকে সামলে (handles) নেয়।
পয়েন্টার (Pointers) বনাম (vs.) রেফারেন্সের (References) অদলবদল (Swap)
nullptr — এটি কোনো সাধারণ NULL নয় (Not NULL)
আধুনিক সি++ (C++)-এর ক্ষেত্রে NULL বা 0-এর পরিবর্তে সব সময় nullptr ব্যবহার করুন। nullptr মূলত টাইপের দিক থেকে অত্যন্ত নিরাপদ (type-safe) — এটি নির্দিষ্টভাবে একটি নাল (null) পয়েন্টার, যেখানে NULL-টি হলো মূলত ছদ্মবেশধারী (wearing a disguise) একটি সাধারণ ইন্টিজার (integer) 0, যা বিভিন্ন ফাংশন ওভারলোডে (function overloads) অস্পষ্টতার (ambiguity) কারণ হতে পারে।
nullptr চেক (Check) করা
কনস্ট্যান্ট (const) পয়েন্টার বনাম (vs.) পয়েন্টার টু কনস্ট্যান্ট (Pointer to const)
এই বিষয়টি প্রথমদিকে সব সময়ই আপনাকে দ্বন্দ্বে ফেলে দিতে পারে। তাই এর ডিক্লারেশন বা ঘোষণাকে (declaration) সব সময় ডান থেকে বাম দিকে (right to left) পড়ার চেষ্টা করুন:
const int* p— এটি একটি কনস্ট্যান্ট ইন্ট বা const int-এর দিকে নির্দেশ করা একটি পয়েন্টার (আপনি এখানকার মানটি বা value পরিবর্তন করতে পারবেন না, শুধু পয়েন্টারটিকে অন্য কোনো ঠিকানায় পরিবর্তন বা change করতে পারবেন)int* const p— এটি একটি ইন্ট বা int-এর দিকে নির্দেশ করা একটি কনস্ট্যান্ট বা const পয়েন্টার (আপনি এখানকার মানটি বা value পরিবর্তন করতে পারবেন, কিন্তু পয়েন্টারটিকে অন্য কোনো ঠিকানায় পরিবর্তন বা change করতে পারবেন না)const int* const p— এটি একটি কনস্ট্যান্ট ইন্ট বা const int-এর দিকে নির্দেশ করা একটি কনস্ট্যান্ট বা const পয়েন্টার (আপনি এর কোনো কিছুই পরিবর্তন করতে পারবেন না)
কনস্ট্যান্ট বা const পয়েন্টারের বিভিন্ন ধরন (Variations)
পয়েন্টার (Pointer) বনাম (vs.) রেফারেন্স (Reference): কখন কোনটি ব্যবহার করবেন (When to Use Which)
এখানে এর একটি দ্রুত নির্দেশিকা বা গাইড (guide) দেওয়া হলো:
| বৈশিষ্ট্য (Feature) | পয়েন্টার (Pointer) | রেফারেন্স (Reference) |
|---|---|---|
| নাল বা null হতে পারে কি না? | হ্যাঁ (Yes) | না (No) |
| নতুন করে অ্যাসাইন (reassigned) করা যায় কি না? | হ্যাঁ (Yes) | না (তৈরির সময় যুক্ত থাকে বা bound at creation) |
| সিনট্যাক্স ওভারহেড (Syntax overhead)? | অনেক বেশি (* এবং &) | অনেক কম (স্বয়ংক্রিয় বা automatic) |
| গাণিতিক কাজগুলো (Arithmetic) করা যায় কি না? | হ্যাঁ (p++, p+3) | না |
সাধারণ নিয়ম বা রুল অব থাম্ব (Rule of thumb): আপনার যখন সুযোগ বা সাধ্য থাকবে তখন রেফারেন্স (references) ব্যবহার করুন, আর যখন সত্যিই এর প্রয়োজন পড়বে ঠিক তখনই কেবল পয়েন্টার (pointers) ব্যবহার করুন। বিভিন্ন ঐচ্ছিক মানের (optional values) (যেমন নালেবল বা nullable), ডায়নামিক মেমোরির (dynamic memory) ক্ষেত্রে এবং লিঙ্কড লিস্টের (linked lists) মতো বিভিন্ন ডেটা স্ট্রাকচারের (data structures) ক্ষেত্রে মূলত এই পয়েন্টারগুলো (pointers) ব্যবহার করার প্রয়োজন পড়ে。