স্ট্রাকচার (Structure)
Jul 14, 2013
স্ট্রাকচার হল কতগুলো স্ট্যান্ডার্ড ডাটা টাইপের সমষ্টিতে প্রোগ্রামারের নিজের সৃষ্টি করা ডাটা টাইপ। অন্যান্য ডাটা টাইপগুলো যেমন একটি নির্দিষ্ট ধরনের ডাটা রাখতে পারে, সেখানে স্ট্রাকচার একাধিক ধরনের একাধিক ডাটা রাখতে পারে। এই ডাটা টাইপটি প্রোগ্রামার নিজে তার প্রয়োজনমত তৈরি করে। এই ডাটা টাইপ অন্যান্য স্ট্যান্ডার্ড ডাটা টাইপের সমন্বয়ে তৈরি করা হয় বলে একে বলে Derived Data Type।
অনেক সময় কোন প্রবলেম সলভ করতে গেলে দেখা যায় যে কোন বিশেষ ধরনের বস্তুর জন্য কতগুলো করে তথ্য ভেরিয়েবলে রাখতে হয় যেগুলো দিয়ে পরবর্তিতে বিভিন্ন হিসাব করা হয়। সেই সব ক্ষেত্রে স্ট্রাকচার ব্যবহার করলে সবকিছু অনেক গোছানো থাকে। যেমন ধরা যাক একটি ক্লাসের 100 জন ছাত্রের নাম, রোল, বয়স এই তিনটা তথ্য ইনপুট নিয়ে রাখতে হবে। তাহলে তাহলে প্রতিটা ছাত্রের নামের জন্য একটা char অ্যারে, রোল এবং বয়সের জন্য দুটি int ভেরিয়েবল ডিক্লেয়ার করতে হবে। তাহলে 100 জন ছাত্রের জন্য এগুলোর প্রতিটার 100 এলিমেন্টের অ্যারে ডিক্লেয়ার করতে হবে।
char name[100][30];
int roll[100], age[100];
কিন্তু এইভাবে কাজ করা অনেক ঝামেলা। একদিকে দেখতেই কেমন যেন লাগে, তার উপর জিনিসটা গোছানো না। এছাড়া যদি এরকম অনেক ধরনের জিনিস থাকে যাদের সম্পর্কে তথ্য রাখতে হবে, অনেক হিসাব থাকে, তখন একেবারে হিজিবিজি অবস্থা হয়ে যাবে! এই ব্যাপারটাকেই অনেকখানি সুবিন্যস্ত এবং সুন্দর করে লেখা যায় স্ট্রাকচার ব্যবহার করলে। যেমন উপরের সমস্যাটার জন্যই একটা স্ট্রাকচার ডিক্লেয়ার করলে তা নিম্নরূপ হবেঃ
struct student {
char name[30];
int roll;
int age;
};
এখন সিন্ট্যাক্স দেখা যাক। প্রথমে struct দিয়ে বলা হচ্ছে যে আমরা একটা স্ট্রাকচার ডিফাইন করতে যাচ্ছি। অর্থাৎ আমাদের এই নতুন ডাটা টাইপটা কিরকম হবে। যেহেতু এটা কোন স্ট্যান্ডার্ড ডাটা টাইপ না, এটায় বিভিন্ন টাইপের ডাটা থাকতে পারে, সেহেতু কি কি ডাটা থাকবে তা বলে দিতে হবে। এরপর যে student লেখা, এটা হল আমাদের নতুন ডাটা টাইপের নাম। আমরা যখন কোন ভেরিয়েবল ডিক্লেয়ার করি তখন ভেরিয়েবলের আগে ডাটা টাইপের নাম লেখা হয়। যেমন, int i; char c; এখানে int বা char হল ডাটা টাইপের নাম। সেরকম আমাদের নতুন এই ডাটা টাইপের নাম হল student।
এরপর { } এর মধ্যে আমরা কি কি ডাটা রাখব এই নতুন টাইপের অধীনে সেটা লিখে দিলাম। আমাদের student ডাটা টাইপের মধ্যে তিনটা তথ্য রাখতে হবে, নাম, রোল এবং বয়স। তাই নতুন এই ডাটা টাইপের মধ্যে কি কি ভেরিয়েবল থাকবে, সেটি কিরকম হবে তা আমরা এখানে বলে দিলাম। এরপর একটা সেমিকোলন দিয়ে শেষ করে দিলাম।
লক্ষ্যনীয়, এখানে যা যা করা হল এতক্ষণ, সবকিছু কেবল একটা ডাটা টাইপ ডিফাইন করল যার নাম student। এখনও কোন ভেরিয়েবল তৈরি করা হয়নি, অতএব কোন মেমরি বরাদ্দ করা হয়নি, এবং তাই এখনও এটি নিয়ে কোন কাজ করা যাবে না। এখন আমাদেরকে এরকম 100 জন ছাত্রের তথ্য রাখতে হবে। তাই আমরা এই student টাইপের 100 এলিমেন্ট বিশিষ্ট একটা অ্যারে ডিক্লেয়ার করব।
struct student stdnt[100];
এখানে আমরা একটা অ্যারে ডিক্লেয়ার করলাম। এটা অন্যান্য যেকোন অ্যারে এর মতই। পার্থক্য হল এটা একটা স্ট্রাকচারের অ্যারে। আর তাই এখানে শুরুতে ডাটা টাইপ বলার সময় পুরো কথাটা লিখতে হয়, struct student । এই ভেরিয়েবল ডিক্লেয়ারের কাজটা আলাদা করে না করে স্ট্রাকচার ডিফাইন করার সাথে সাথেই করা যায়।
struct student {
char name[30];
int roll;
int age;
} stdnt[100];
এই কোডটি উপরের দুইটা কোডের মতই কাজ করবে। এখানে স্ট্রাকচার ডিফাইন করার সাথে সাথেই ভেরিয়েবল ডিক্লেয়ার করা হয়ে গেল। একাধিক ভেরিয়েবল ডিক্লেয়ার করতে হলে কমা দিয়ে দিয়ে আলাদা করে দিলেই হবে।
এখন যেহেতু স্ট্রাকচারের ভেরিয়েবল ডিক্লেয়ার করা হয়ে গেল, তাই ভেরিয়েবলগুলো নিয়ে এখন কাজ করা যাবে। ভেরিয়েবলগুলোর প্রতিটির মধ্যে তিনটি করে মেম্বার আছে, name, roll এবং age । এই মেম্বারগুলো ব্যবহার করার জন্য, অর্থাৎ এতে ইনপুট নিতে বা এর থেকে আউটপুট পেতে বা এই মানগুলো ব্যবহার করার জন্য বিশেষ সিন্ট্যাক্স রয়েছে।
এখানে আমাদের একেকটি ভেরিয়েবল হল stdnt[0], stdnt[1], … , stdnt[99] এগুলো। স্ট্রাকচারের অন্তর্ভুক্ত কোন একটি মেম্বার এক্সেস করতে হলে . (ডট) ব্যবহার করতে হয়। যেমন,
stdnt[0].roll = 10;
অতএব 100 জন ছাত্রের নাম, রোল, বয়স ইনপুট নেওয়া যাবে এভাবে।
int i;
for( i = 0; i < 100; i++ ) {
scanf( "%s %d %d", stdnt[i].name, &stdnt[i].roll, &stdnt[i].age );
}
এখান থেকে খুব সহজেই বোঝা যাচ্ছে যে স্ট্রাকচারের প্রতিটা মেম্বার একেকটা ভেরিয়েবল হিসেবে কাজ করে, এবং সেইজন্য কেবল স্ট্রাকচার ভেরিয়েবলটার নাম দিয়ে সাথে ডট দিয়ে মেম্বারটি লিখে দিলেই হয়ে যায়। এভাবে করে অন্য যেকোন ভেরিয়েবল নিয়ে যেভাবে কাজ করা যায় সেভাবেই কাজ করা যাবে।
আমরা চাইলে এই স্ট্রাকচারের একটি ভেরিয়েবল ডিক্লেয়ার করার সময় অন্যান্য ভেরিয়েবলের মত সেটিকে initialize করতে পারি। int i = 0; লিখলে i নামক int ডিক্লেয়ার হওয়ার সাথে সাথে এতে 0 এসাইন করা হয়ে যায়। একই কাজ স্ট্রাকচারে করা যায়। ধরা যাক একটি স্ট্রাকচার টাইপ ডিফাইন করব আমরা যেটি হবে একটি box এর স্ট্রাকচার। এতে তিনটা মেম্বার থাকবে যারা একটি box এর দৈর্ঘ্য, প্রস্থ এবং উচ্চতা হবে।
struct box {
int length;
int width;
int height;
};
এখন এই box টাইপের একটি ভেরিয়েবল b ডিক্লেয়ার করব আমরা যেটির length, width এবং height তিনটি ভেরিয়েবলকেই আমরা 0 দিয়ে initialize করব।
struct box b = { 0, 0, 0 };
এভাবে যেকোন স্ট্রাকচার ভেরিয়েবলকে initialize করা যায়। সেজন্য যতগুলো মেম্বার আছে তার প্রতিটার মান ক্রমানুসারে { } এর মধ্যে কমা দিয়ে আলাদা করে লিখে দিলেই হয়ে যাচ্ছে।
স্ট্রাকচারের একটা বড় সুবিধা হচ্ছে একটা স্ট্রাকচারকে আরেকটা স্ট্রাকচারে এসাইন করা যায় যদি দুইটা একই টাইপের হয়। অর্থাৎ ধরা যাক আমাদের box টাইপের দুটি স্ট্রাকচার ভেরিয়েবল আছে, b1 এবং b2 । তাহলে আমরা b1 = b2 এভাবে করে b2 স্ট্রাকচারকে b1 এ এসাইন করতে পারি। এতে যা হবে তা হল, b2 এর সবগুলা মেম্বারে যা যা মান আছে সেগুলো b1 এর অনুরূপ মেম্বারগুলোতে এসাইন হয়ে যাবে।
আরেকটা ব্যাপার হল যেকোন ডাটা টাইপের যেমন পয়েন্টার ডিক্লেয়ার করা যায় তেমনি স্ট্রাকচারেরও পয়েন্টার ডিক্লেয়ার করা যায়। যেমন box টাইপের একটা পয়েন্টার ডিক্লেয়ার করা যায় এভাবে,
struct box *b;
এভাবে আমরা b নামক একটা পয়েন্টার ডিক্লেয়ার করলাম। কিন্তু যেহেতু মেমরি এলোকেট করা হয়নি তাই এটি নিয়ে এখনও কাজ করা যাবে না। এই ব্যাপারে বুঝতে হলে পয়েন্টার সম্পর্কে ভালো ধারণা থাকা প্রয়োজন। এই লেখাটি পড়লে আশা করি অনেকখানি পরিষ্কার হয়ে যাবে। এখন আমরা মেমরি এলোকেট করে নিব, তারপর কাজ করব।
b = ( struct box * ) malloc( sizeof( struct box ) );
এই b যেহেতু ভেরিয়েবল না, পয়েন্টার, তাই এর মাধ্যমে মেম্বার এক্সেস করতে হলে . (ডট) এর পরিবর্তে -> (তীর চিহ্ন) ব্যবহার করতে হয়। যেমন,
b->length = 10;
scanf( "%d", &b->width );
printf( "%d", b->height );
মূলত এগুলোই হল স্ট্রাকচারের বেসিক। বিভিন্ন ডাটা স্ট্রাকচারে এই স্ট্রাকচার ব্যবহার করা হয়। যেমন স্ট্যাক, কিউ, ট্রি এইসব ডাটা স্ট্রাকচারের মূল ভিত্তি হল স্ট্রাকচার। তাই স্ট্রাকচার কিভাবে ব্যবহার করতে হয়, এবং কিভাবে এটি কাজ করে তা জানা থাকা গুরুত্বপূর্ণ।