char এবং int

Standard

char এবং int নিয়ে বেশ কিছু কনফিউশন কাজ করতে দেখেছি। তাই ব্যাপারটা পরিষ্কার করা দরকার। char এবং int দুটিই হল ডাটা টাইপ। ডাটা টাইপ দিয়ে আমরা কোন একটি ভেরিয়েবলে কি থাকবে সেটি নির্দিষ্ট করে দেই। যেমন যখন আমরা একটা ভেরিয়েবল ডিক্লেয়ার করি, int x; তখন সহজ কথায় যেটি হয় তা হল, x নামের একটি ভেরিয়েবল তৈরি হয় যেখানে একটি ইন্টিজার রাখা যাবে। আবার যখন ডিক্লেয়ার করা হয় char ch; তখনও ch নামের একটি ভেরিয়েবল তৈরি হয় যেখানে একটি ক্যারেক্টার রাখা যাবে। এখানেই আসলে কনফিউশন!

যখন int দিয়ে ডিক্লেয়ার করা হয় তখন সেখানে ইন্টিজার রাখা যাবে আর যখন char দিয়ে ডিক্লেয়ার করা হয় তখন সেখানে কেবল ক্যারেক্টারই রাখা যাবে এমনটি ভুল ধারণা। int এবং char দিয়ে আসলে যেটি হয় তা হল এরা ভেরিয়েবলের জন্য নির্দিষ্ট মেমরি বরাদ্দ করে দেয়। দেখা যাক আসলে কি হয়।

কম্পিউটারকে যাই বলা হোক না কেন, ইনপুট ডিভাইস দিয়ে যাই ইনপুট দেওয়া হোক না কেন, সে সবকিছুকে নাম্বারের মাধ্যমে প্রকাশ করে। সকল প্রকার তথ্য সে নাম্বারের মাধ্যমে রাখে। এই নাম্বার হল বাইনারি নাম্বার। কারণ কম্পিউটার যেটা বুঝে তা হল হয় ভোল্টেজ আছে, নয়তো নাই। কারেন্ট প্রবাহ আছে, অথবা নাই। চৌম্বক ক্ষেত্র হলে সেখানে হয় উত্তর মেরু, নয় দক্ষিণ মেরু। অতএব যেভাবেই ডাটা রাখা হোক না কেন, দুইটি ঘটনা ঘটতে পারে। এদেরকে ০ এবং ১ দিয়ে প্রকাশ করা হয়। একারণেই কম্পিউটার শধু বাইনারি বুঝে।

যখন আমরা কোন তথ্য ইনপুট দেই তখন সেটিকে সে বাইনারিতে তার মেমরিতে রাখে। এই কারণে সে সকল তথ্যকে বাইনারি নাম্বারে কনভার্ট করে। আমরা মানুষেরা বোঝার সুবিধার কারণে এই নাম্বারগুলোকে ডেসিমেলে প্রকাশ করি। ধরা যাক যখন কোন একটি নাম্বার 5 ইনপুট দেওয়া হল। তাহলে কম্পিউটার যখন এটিকে মেমরিতে রাখবে তখন সে 5 এর বাইনারি হিসেবে একে রাখবে। সেটি হল 101। এই যে বাইনারিতে নাম্বারটিকে ইনপুট নিয়ে রাখতে হল এটার জন্য আগে থেকেই ভেরিয়েবল ডিক্লেয়ার করে মেমরি বরাদ্দ করে দেওয়া হয়। এখন যা ইচ্ছা তাই বরাদ্দ করলেই তো হবে না। কিছু স্ট্যান্ডার্ড আছে। সেই স্ট্যান্ডার্ডগুলোর মধ্যে দুইটি হল char এবং int।

যেহেতু সবকিছুকে বাইনারিতে রাখা হয় তাই মেমরির হিসাবও হয় কয়টি বাইনারি ডিজিট আছে তার উপর। ১টি ডিজিট হল ১ বিট। এরকম ৮টি ডিজিট নিয়ে হয় ১ বাইট। যখন char বা int দিয়ে ভেরিয়েবল ডিক্লেয়ার করা হয় তখন char যত বিট এবং int যত বিট হওয়ার কথা তত বিট মেমরি বরাদ্দ করা হয়। char এর সাইজ হচ্ছে ৮ বিট এবং int এর সাইজ কোথাও ১৬, কোথাও ৩২ বিট (বেশিরভাগ ক্ষেত্রেই ৩২ বিট)।

অর্থাৎ char টাইপের ভেরিয়েবল যখন আমি ডিক্লেয়ার করব তখন ৮ বিট মেমরি বরাদ্দ হয়ে যাবে। অতএব সেখানে আমি এমন কিছু ইনপুট নিয়ে রাখতে পারব যার জন্য ৮ বিট মেমরি লাগে, অর্থাৎ যাকে বাইনারিতে প্রকাশ করতে ৮টা ডিজিট লাগে। ধরা যাক আমি 0 রাখব। তাহলে 0 এর জন্য একটি বিটই যথেষ্ট। কারণ 0 কে বাইনারিতে প্রকাশ করলে 0-ই হবে। আর একেবারেই প্রাথমিক পর্যায়ের গণিতেই আমরা শিখেছি যে কোন নাম্বারের বামে যদি 0 থাকে তাহলে সেই 0 এর কোন অর্থ নেই। তাহলে 0 কে বাইনারিতে লিখলে হবে 0। আর যেহেতু আরও ৭ বিট বাকি আছে সেগুলোতে বাম পাশে ৭টি 0 বসে যাবে। অর্থাৎ হবে 0000 0000।

এখন একটা সহজ পার্মুটেশন করা যাক। এখানে ঘর আছে ৮টা। ৮টা ঘরে ২টা করে ডিজিট বসতে পারে। তাহলে মোট কত রকম পার্মুটেশন সম্ভব? 2^8 রকম, অর্থাৎ 256 রকম। অতএব ৮টি ডিজিটের সাহায্যে আমরা সর্বোচ্চ 256 টি নাম্বারকে প্রকাশ করতে পারি। যদি আমরা 0 থেকে শুরু করি, তাহলে 0-255 পর্যন্ত নাম্বার আমরা ৮ ডিজিটে প্রকাশ করতে পারব, অর্থাৎ ৮ বিট মেমরিতে রাখতে পারব।

এই যে আমরা char ডাটা টাইপ ডিক্লেয়ার করলাম, এখানে আসলে ৮ বিট মেমরির একটি ভেরিয়েবল তৈরি হল। যেহেতু আমরা দেখতে পাচ্ছি ৮ বিটে ০-২৫৫ পর্যন্ত রাখা যায়, অতএব আমরা char ডাটা টাইপের মধ্যে ০-২৫৫ এর মধ্যে যেকোন নাম্বার রাখতে পারব। এখানেই একটা কনফিউশন। এটা তো char ডাটা টাইপ, এখানে তো ক্যারেক্টার রাখা যায়, নাম্বার কিভাবে রাখে? এই কথাটার সহজ একটা উত্তর। ক্যারেক্টার ডাটা টাইপ বলে কিছু নাই, ডাটা টাইপ হল char। এর মানে ৮ বিট মেমরি। আর আগেই বলেছি, কম্পিউটার সবকিছুকে নাম্বার হিসেবে রাখে। যখন আমরা কোন ক্যারেক্টার ইনপুট নেই, তখন সে আসলে ক্যারেক্টারের জন্য একটা নাম্বার রাখে যাকে বলে ASCII কোড। প্রতিটা ক্যারেক্টারের জন্য এই কারণেই ASCII কোড দেওয়া হয়েছে। কম্পিউটার সংখ্যা বাদে কিছু রাখতে পারে না। তাকে ক্যারেক্টার ইনপুট দেওয়ার হলেও সে সেটার ASCII রাখে।

এখন int এর ব্যাপারে আসা যাক। এতক্ষণে এইটুকু বুঝে যাওয়ার কথা যে আসলে char মানে এই না যে সেখানে শুধু ক্যারেক্টার রাখা যাবে। কোথাও আসলে ক্যারেক্টার রাখা যায় না। যা রাখা যায় তা হল নাম্বার। সেই নাম্বার মেমরির মধ্যে জায়গা হল নাকি সেটি হল বিষয়। int এর মেমরি হল ৩২ বিট। অতএব এখানে 2^32 সংখ্যক নাম্বার রাখা যাবে। 0 থেকে শুরু করলে 2^32-1 পর্যন্ত নাম্বার রাখা যাবে। এখন প্রতিটা ক্যারেক্টারকে আমরা যেহেতু প্রকৃত অর্থে ASCII কোড হিসেবে রাখি, অতএব সেই ASCII কোড যদি char এ জায়গা হয় তাহলে নিঃসন্দেহে তা int এও জায়গা হবে। অতএব আমরা int এর মধ্যেও ক্যারেক্টার ইনপুট নিয়ে রাখতে পারব।

মাথায় মনে হয় তালগোল পাকিয়ে গেছে। উদাহরণ দিয়ে দেখা যাক।

[code language=”cpp”]
code:
int x;
char ch;
scanf( "%d %c", &x, &ch );
[/code]

এখানে আসলে কি হয়? প্রথমেই আমরা দুটি ভেরিয়েবল ডিক্লেয়ার করলাম, int টাইপের x এবং char টাইপের ch। অতএব দুটি ভেরিয়েবল তৈরি হল, x এ আমরা ৩২ বিটের সংখ্যা রাখতে পারব, ch এ রাখতে পারব ৮ বিট। এখন scanf এর ভিতরে আসা যাক। %d এবং %c এই দুইটি দিয়ে আসলে বোঝানো হয় যে কি ইনপুট আসছে। %d মানে সেখানে একটি ডেসিমেল ইন্টিজার ইনপুট আসছে। %c মানে হচ্ছে একটি ক্যারেক্টার ইনপুট আসছে। এখন আমরা যত ক্যারেক্টার ব্যবহার করি সেগুলোর প্রতিটির জন্য যে ASCII কোড দেওয়া হয়েছে সেগুলো ০ থেকে ১২৮ এর মধ্যে। অর্থাৎ তা ৮ বিটের মধ্যেই জায়গা হয়ে যায়। তাই ক্যারেক্টার ইনপুট নেওয়ার জন্য char টাইপ ভেরিয়েবল নেওয়া হয়। যখন আমরা A ইনপুট দিব তখন সে আসলে A এর ASCII নিয়ে ch এর মধ্যে রাখবে। কারণ তাকে %c এর মাধ্যমে বলে দেওয়া হয়েছে যে এখানে একটি ক্যারেক্টার ইনপুট দেওয়া হবে যেটির ASCII কোড নিয়ে রাখতে হবে। কিন্তু যদি %d এর জায়গায় আমি নাম্বার ইনপুট না দিয়ে A দেই তাহলে কাজ করবে না। কারণ সে একটি নাম্বার ইনপুট হিসেবে আশা করছে। তাকে বলে দেওয়া হয়নি যে ইনপুট যা দেওয়া হচ্ছে তার ASCII নিতে হবে। বলা হয়েছে একটি নাম্বার আসবে। যেহেতু A কোন নাম্বার না তাই এটি কাজ করবে না, কারণ সে মেমরিতে এটিকে রাখতে পারবে না।

এখন যদি আমি লেখি, scanf( "%c %d", &x, &ch ); তাহলে কাজ করবে? অবশ্যই কাজ করবে! কারণ x এবং ch আসলে মেমরিতে বরাদ্দকৃত কিছু জায়গা। %d এবং %c মানে কি ধরনের ডাটা আসবে। এখন আমি প্রথমে A ইনপুট দিলে %c যেহেতু আছে তাই A এর ASCII কোড ইনপুট হিসেবে যাবে। আর সকল ASCII যেহেতু ৮ বিটেই জায়গা হয়ে যায় অতএব ৩২ বিটে জায়গা না হওয়ার তো কোন মানেই হয় না! আর যখন এরপরের ইনপুট নিব তখন একটি নাম্বার ইনপুট দিব। কারণ এখানে বলা আছে %d, অর্থাৎ একটি ইন্টিজার ইনপুট হিসেবে আসবে। একটি গুরুত্বপূর্ণ বিষয় আবারও বলি, char ডাটা টাইপ মানে এই না যে সেখানে ক্যারেক্টার থাকবে। এর মানে হল এখানে ৮ বিটের কিছু থাকবে। অতএব আমরা char ডাটা টাইপের মধ্যে ৮ বিটে জায়গা হয় এমন যেকোন নাম্বার %d এর সাহায্যে ইনপুট নিতে পারব। আর যেহেতু ইতোমধ্যেই দেখেছি, ৮ বিটে ০ থেকে ২৫৫ পর্যন্ত নাম্বার থাকতে পারে, অতএব char এর মধ্যে আমরা 0 থেকে 255 পর্যন্ত যেকোন নাম্বার রাখতে পারব।

অতএব ডাটা টাইপ আসলে মেমরি নির্দিষ্ট করে। এটি দিয়ে কিছু বিট নির্দিষ্ট হয় যেখানে আমি কোন নাম্বার রাখতে পারব। আর আমরা কি ধরনের ডাটা ইনপুট বা আউটপুট দিব সেটি আসলে %d, %c, %f ইত্যাদি দিয়ে বোঝানো হয়। ভিতরে সবকিছুই নাম্বার হিসেবে থাকে, আমরা কি হিসেবে ইনপুট এবং আউটপুট দিব সেটিই আসলে % দিয়ে বলা হয়।

আরেকটা উদাহরণ দেই। একটি char টাইপ ভেরিয়েবল আছে x ।

[code language=”cpp”]
code:
scanf( "%d", &x ); // একটি নাম্বার ইনপুট নিবে। ধরা যাক ইনপুট 65 ।
printf( "%d %c", x, x ); // এখানে আউটপুট হিসেবে দেখাবে 65 A ।
[/code]

কারণ কি? কি ঘটছে? যেটি ঘটছে তা হল ইনপুট নেওয়ার সময় কম্পিউটার দেখছে যে ইনপুট আসবে %d টাইপের, অর্থাৎ একটি ইন্টিজার। যেহেতু আমরা 65 ইনপুট দিচ্ছি যা 255 এর মধ্যে অতএব তা char টাইপের মধ্যে জায়গা না হওয়ার কিছু নেই। যখন আমরা আউটপুট দিচ্ছি তখন প্রথমে দেওয়া আছে %d। অতএব এখানে একটি ইন্টিজার আউটপুট হিসেবে যাবে। এরপর যখন দেওয়া আছে %c তখন কম্পিউটার যেটি বুঝে তা হল এখানে একটি ইন্টিজার নাম্বার আসবে, যেটি কোন একটি ক্যারেক্টারের ASCII কোড। সেই কোডটি যেই ক্যারেক্টারের সেই ক্যারেক্টারকে আউটপুটে দেখাতে হবে। যেহেতু A এর ASCII কোড 65 তাই এখানে A দেখানো হচ্ছে।

অর্থাৎ আমরা কি ইনপুট দিব বা আউটপুট দিব তা আসলে বোঝানো হয় % এর পরে কি আছে তা দিয়ে। ডাটা টাইপ দিয়ে কেবল মেমরি নির্দিষ্ট করা হয়। সেই মেমরিতে যেকোন কিছু থাকতে পারে। char মানে ৮ বিট, int মানে ৩২ বিট। যদি মানুষের বয়স ইনপুট নিয়ে রাখতে হয়, তাহলে char নিলেই হয়। কারণ বয়স 255 এর বেশি হবে না। আর সেটি ৮ বিটের মধ্যেই জায়গা হয়ে যায়। শুধু শুধু ৩২ বিট নিয়ে জায়গা নষ্ট করার কোনই মানে হয় না। গুরুত্বপূর্ণ কথাটা আবার বলি, char ডাটা টাইপ মানে এই না যে সেখানে ক্যারেক্টার থাকবে। এর মানে হল এখানে ৮ বিটের কিছু থাকবে। ক্যারেক্টার নাকি ইন্টিজার ইনপুট বা আউটপুট দিব সেটি ডাটা টাইপের উপর না, সেটি নির্ভর করে % এর পর কি আছে তার উপর।

আর unsigned এর ব্যাপারটা আসলে কিছুই না। একটি ভেরিয়েবলে যে নাম্বার থাকবে সেটি যদি signed হয়, অর্থাৎ সেখানে যদি মাইনাস কোন নাম্বার থাকে তাহলে সেটি বোঝানোর জন্য একটি বিট লাগে। তাই একটি বিট খরচ হয় সাইন নির্দিষ্ট করার জন্য। আর এতক্ষণে বুঝে যাওয়ার কথা unsigned char হওয়া অসম্ভব কিছু না। যেহেতু একটি নাম্বার এই মেমরিতে থাকবে সেটি signed বা unsigned দুটিই হতে পারে।