স্ট্রাকচার বা struct হলো এক ধরনের কাস্টম ফর্মের (custom form) মতো — যার ফিল্ডগুলো (fields) আপনি নিজে ডিজাইন (design) করবেন, এবং সি (C) মূলত সেগুলোকে আপনার জন্য পূরণ করে দেবে
আপনার নিজস্ব ডেটা টাইপ তৈরি বা ডিজাইন করা (Designing Your Own Data Type)
ধরুন, আপনি মূলত একটি স্টুডেন্ট বা ছাত্রছাত্রীদের ডেটাবেস (student database) তৈরি করতে চাইছেন। যেখানে প্রতিটি স্টুডেন্ট বা ছাত্রের একটি নাম (name), বয়স (age), এবং একটি জিপিএ (GPA) থাকবে। এক্ষেত্রে আপনি চাইলে এর জন্য তিনটি আলাদা আলাদা অ্যারে (three separate arrays) ব্যবহার করে কাজ চালাতে পারেন — যেমন একটি নামের জন্য, একটি বয়সের জন্য এবং আরেকটি জিপিএর জন্য — কিন্তু ব্যাপারটি দেখতে ঠিক এমন হবে যেন আপনি একজন ব্যক্তির আইডি (ID), তার ছবি (photo), এবং ঠিকানাকে (address) তিনটি সম্পূর্ণ আলাদা আলাদা ফাইলিং ক্যাবিনেটে (filing cabinets) সংরক্ষণ করছেন। এটি একটি অগোছালো বা মেসি (Messy) কাজ।
এক্ষেত্রে একটি স্ট্রাকচার (struct) আপনাকে এ সম্পর্কিত সমস্ত ডেটাগুলোকে একটি মাত্র প্যাকেজে (single package) বান্ডেল বা একসাথে যুক্ত (bundle) করে রাখার সুযোগ দেয়। ব্যাপারটিকে আপনি ঠিক একটি কাস্টম ফর্ম (custom form) ডিজাইন করার মতো ভাবতে পারেন: যেখানে ফিল্ডগুলো (fields) আপনি নিজে বেছে নেবেন (pick), এবং সি (C) মূলত আপনার প্রয়োজনীয়তা অনুযায়ী সেগুলোর যত খুশি তত কপি (copies) তৈরি করে দেবে।
একটি স্ট্রাকচার ডিফাইনিং (Defining) এবং ব্যবহার করা (Defining and Using a Struct)
#include <stdio.h>
#include <string.h>
struct Student{
char name[50];
int age;
float gpa;
};
int main(){
struct Student alice;
strcpy(alice.name,"Alice");
alice.age=20;
alice.gpa=3.85;
printf("Name: %s\n", alice.name);
printf("Age: %d\n", alice.age);
printf("GPA: %.2f\n", alice.gpa);
return0;
}
Output
Name: Alice
Age: 20
GPA: 3.85
typedef — দ্য শর্টহ্যান্ড বা শর্টকাট পদ্ধতি (typedef — The Shorthand)
কোডের সব জায়গায় বারবার struct Student লেখাটা বেশ দ্রুতই আপনার কাছে বিরক্তিকর বা পুরনো (old) মনে হতে পারে। এক্ষেত্রে এই typedef মূলত আপনাকে এর সহজ একটি অ্যালিয়াস বা উপনাম (alias) তৈরি করার সুযোগ দেয়, যার ফলে আপনি এর বদলেই শুধুমাত্র Student লিখেই আপনার কাজ চালাতে পারবেন। এটিকে আপনি ওই লেবেল মেকারের (label maker) জন্য আপনার ফর্মের একটি সংক্ষিপ্ত নাম (short name) দেওয়ার মতো ভাবতে পারেন।
এই typedef (টাইপডেফ) জীবনকে অনেক বেশি সহজ বা ইজিয়ার (Easier) করে দেয় (typedef Makes Life Easier)
#include <stdio.h>
#include <string.h>
typedef struct {
char name[50];
int age;
float gpa;
}Student;
int main(){
// এখানে এখন আর "struct Student" লেখার কোনো দরকার নেই (No need)!
Student bob;
strcpy(bob.name,"Bob");
bob.age=22;
bob.gpa=3.42;
printf("%s is %d years old with a %.2f GPA\n",
bob.name, bob.age, bob.gpa);
return0;
}
Output
Bob is 22 years old with a 3.42 GPA
ডট বা বিন্দু (.) বনাম অ্যারো বা তীর (->) (Dot (.) vs Arrow (->))
যখন আপনার কাছে কোনো সাধারণ এক্সিস্ট বা স্ট্রাকচারের ভ্যারিয়েবল (struct variable) থাকবে, তখন এর ফিল্ডগুলোকে অ্যাক্সেস (access) করার জন্য ডট অপারেটর (dot operator) ব্যবহার করুন: যেমন alice.name। কিন্তু আপনি যখন ওই স্ট্রাকচারটিকে (struct) পয়েন্টার (pointer) হিসেবে ব্যবহার করবেন, তখন আপনাকে এর অ্যারো অপারেটর (arrow operator) ব্যবহার করতে হবে: যেমন ptr->name।
এখানকার এই অ্যারোটি (arrow) মূলত এই (*ptr).name-এরই একটি শর্টহ্যান্ড (shorthand) — যা সবার প্রথমে পয়েন্টারটিকে (pointer) ডিরেফারেন্স (dereferencing) করে এবং তারপরই এর ফিল্ডটিকে অ্যাক্সেস (access) করে। তবে এখানকার এই অ্যারোটি বা তীরটি (arrow) দেখতে আরও অনেক বেশি ক্লিনার বা পরিষ্কার (cleaner)।
অ্যারো অপারেটর (Arrow Operator)-এর সাহায্যে পয়েন্টার টু স্ট্রাকচার (Pointer to Struct)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[50];
int age;
}Person;
voidbirthday(Person*p){
p->age++;// পয়েন্টার-টু-স্ট্রাকচার বা pointer-to-struct-এর জন্য অ্যারো অপারেটর (Arrow operator)
printf("Happy birthday, %s! You're now %d.\n",
p->name, p->age);
}
int main(){
Person john;
strcpy(john.name,"John");
john.age=29;
printf("Before: %s is %d\n", john.name, john.age);
birthday(&john);// কপি (copying) হওয়া এড়াতে বা avoid করতে এখানে পয়েন্টার (pointer) পাস (Pass) করুন
printf("After: %s is %d\n", john.name, john.age);
return0;
}
Output
Before: John is 29
Happy birthday, John! You're now 30.
After: John is 30
Note: যখন আপনি কোনো ফাংশনটির (function) কাছে একটি সম্পূর্ণ স্ট্রাকচারকে (struct) পাস (pass) করেন, তখন সি (C) মূলত ওই সম্পূর্ণ স্ট্রাকচারটিরই (ENTIRE struct) একটি কপি (copies) তৈরি করে — অর্থাৎ এখানকার প্রতিটি বাইটকেই (byte) সে কপি করে। যদি আপনার স্ট্রাকচারটির (struct) মধ্যে ১০০০-ক্যারেক্টার (1000-char) বিশিষ্ট কোনো অ্যারে (array) থাকে, তবে এর প্রতিটি কলের (call) সময়ই এটি মূলত ১০০০+ বাইটকে (1000+ bytes) কপি (copied) করতে থাকবে। তাই বড় স্ট্রাকচারগুলোর (large structs) ক্ষেত্রে, সবসময় সেগুলোর বদলে শুধু এর পয়েন্টারটিকে (pointer) পাস (pass) করানো উচিত।
নেস্টেড স্ট্রাকচারগুলো বা স্ট্রাকচারের ভেতরে স্ট্রাকচার (Nested Structs)
যেকোনো স্ট্রাকচারই (Structs) মূলত তার নিজের ভেতর অন্যান্য স্ট্রাকচারগুলোকেও (other structs) থাকতে পারে — অনেকটা কোনো সাব-সেকশন (sub-section) থাকা একটি ফর্মের মতো। যেমন ধরুন যেকোনো Person বা ব্যক্তির গঠনের (struct) ঠিক ভেতরে একটি Address বা ঠিকানার কাঠামো (struct) রাখলে, সেটি সব জিনিসগুলোকে বেশ সুন্দর করে গুছিয়ে রাখতে পারে।
নেস্টেড স্ট্রাকচার বা Nested Structs
#include <stdio.h>
#include <string.h>
typedef struct {
char street[100];
char city[50];
int zip;
}Address;
typedef struct {
char name[50];
Address home;// একটি স্ট্যাকচারের ভেতরে আরেকটি স্ট্রাকচার (Struct inside a struct)
}Person;
int main(){
Person p;
strcpy(p.name,"Carol");
strcpy(p.home.street,"123 Main St");
strcpy(p.home.city,"Springfield");
p.home.zip=62704;
printf("%s lives at %s, %s %d\n",
p.name, p.home.street,
p.home.city, p.home.zip);
return0;
}
Output
Carol lives at 123 Main St, Springfield 62704
অ্যারে অফ স্ট্রাকচারস বা স্ট্রাকচারের অ্যারে (Array of Structs)
আপনার কি কোনো ক্লাস রোস্টার (class roster) দরকার? নাকি কোনো ইনভেন্টরি লিস্ট (inventory list)? তাহলে খুব সহজেই এমন স্ট্রাকচারগুলোর একটি লিস্ট বা অ্যারে (array) তৈরি করে ফেলুন। এখানকার প্রতিটি উপাদানই (element) হলো মূলত একেকটি সম্পূর্ণ রেকর্ড (complete record) — একেবারে কোনো স্প্রেডশিটের একটি সারির (row) মতো।
স্ট্রাকচারের অ্যারে (Array of Structs)
#include <stdio.h>
#include <string.h>
typedef struct {
char name[50];
int score;
}Player;
int main(){
Player leaderboard[3];
strcpy(leaderboard[0].name,"Alice");
leaderboard[0].score=950;
strcpy(leaderboard[1].name,"Bob");
leaderboard[1].score=870;
strcpy(leaderboard[2].name,"Carol");
leaderboard[2].score=920;
printf("=== লিডারবোর্ড (Leaderboard) ===\n");
for(int i =0; i <3; i++){
printf("%d. %-10s %d pts\n",
i +1, leaderboard[i].name,
leaderboard[i].score);
}
return0;
}
Output
=== Leaderboard ===
1. Alice 950 pts
2. Bob 870 pts
3. Carol 920 pts
Note: C++ বা Java-এর ক্লাসগুলোর মতো সি-এর (C) স্ট্রাকচারগুলোর (Structs) ভেতরে কোনো প্রকার মেথডের (methods) (ফাংশন) সাপোর্ট বা ব্যবস্থা থাকে না। তাই আপনি যদি এমন কোনো ফাংশনকে (function) পেতে চান যা ওই স্ট্রাকচারটির (struct) ওপরে কাজ করবে, তবে আপনাকে ওই স্ট্রাকচারটি (বা এর একটি পয়েন্টারকে) একটি প্যারামিটার বা parameter হিসেবে ওই ফাংশনে পাস করাতে হবে।
চ্যালেঞ্জ
ছোট কুইজ
কোনো স্ট্রাকচার বা struct-এর সাথে typedef ব্যবহার করলে এটি মূলত কী কাজ করে?