সি (C) স্ট্রিং এবং ক্যারেক্টার (C Strings & Characters)
স্ট্রিংগুলো (Strings): দ্য হার্ড ওয়ে বা সবচেয়ে কঠিন পদ্ধতি (The Hard Way)
বেশিরভাগ প্রোগ্রামিং ভাষার (languages) স্ট্রিংগুলোই (strings) মূলত নিজস্ব কিছু মেথড (methods) এবং সেফটি নেটসহ (safety nets) তৈরি হওয়া এক ধরনের বিল্ট-ইন টাইপ (built-in type) বা ব্যবস্থা। কিন্তু সি (C)-তে? সি-তে (C) যেকোনো স্ট্রিং (string) হলো char-এর খুব সাধারণ একটি অ্যারে বা array যার ভেতর একটি স্পেশাল ক্যারেক্টার (special character) থাকে — যাকে নাল টার্মিনেটর (null terminator) বা '\0' বলা হয় — এটি মূলত অ্যারের একেবারে শেষে (end) একজন বাউন্সারের (bouncer) মতো দাঁড়িয়ে থাকে এবং সবাইকে বলে যে "স্ট্রিংটি মূলত এখানেই শেষ (the string ends here)।"
সি-তে মূলত কোনো .length প্রপার্টি (property) নেই। এখানে মেমোরি (memory) ম্যানেজ বা পরিচালনা করার জন্য কোনো গারবেজ কালেক্টরও (garbage collector) নেই। এখানে আপনাকে মূলত আপনার নিজের ভরসাতেই থাকতে হবে, কারণ এখানকার প্রতিটি স্ট্রিং ফাংশনই (string function) এর মেমোরির (memory) ভেতর দিয়ে একের পর এক ক্যারেক্টার (character by character) করে সামনের দিকে এগোতে থাকে যতক্ষণ না পর্যন্ত এটি ওই '\0'-টিকে খুঁজে পায়। আর যদি এর নাল টার্মিনেটরটি (null terminator) মিসিং বা অনুপস্থিত (missing) থাকে? তাহলে এখানকার ওই ফাংশনটি (function) নিজের মতো করে সামনের দিকে এগিয়ে যেতেই থাকবে (keeps walking) — এবং আপনার ডেটার সীমানা পার হয়ে মেমোরিতে (memory) থাকা যেকোনো প্রকার গারবেজ বা আবর্জনার (garbage) ভেতরে গিয়ে পড়তে পারে।
স্ট্রিং তৈরি করা (Creating Strings)
এক্ষেত্রে আপনার কাছে মূলত প্রধান দুটি (two main) উপায় বা অপশন আছে:
char greeting[] = "Hello";— এর মাধ্যমে এখানকার কম্পাইলারটি (compiler) মূলত ৬-উপাদানের (6-element) একটি অ্যারে বা array তৈরি করে (৫টি বর্ণ বা letters +'\0')char greeting[6] = {'H','e','l','l','o','\0'};— ম্যানুয়াল (manual), একের পর এক ক্যারেক্টার (character by character) করে
এখানে খেয়াল করুন যে এর সাইজটি (size) হলো ৬ (6), ৫ (5) নয়। কারণ এখানকার ওই নাল টার্মিনেটরেরও (null terminator) মূলত নিজের জন্য একটি আলাদা লকারের (own locker) প্রয়োজন পড়ে।
স্ট্রিং তৈরি করা এবং প্রিন্ট করা (Creating and Printing Strings)
স্ট্রিং.এইচ (string.h) টুলবক্স বা Toolbox
যেহেতু সি স্ট্রিংগুলো (C strings) হলো মূলত সাধারণ কিছু অ্যারে (arrays), তাই এগুলোর তুলনা করার (compare) জন্য আপনি কোনো ==-কে ব্যবহার করতে পারবেন না, এমনকি এগুলোকে কনক্যাটেনেট বা জোড়া লাগানোর (concatenate) জন্যও কোনো + ব্যবহার করা যাবে না। তাই এর বদলে, আপনাকে <string.h>-এর ভেতরের কিছু নির্দিষ্ট ফাংশন (functions) ব্যবহার করতে হবে:
strlen(s)— এটি মূলত এর লেন্থ বা দৈর্ঘ্য (length) রিটার্ন করে বা ফেরত দেয় (তবে'\0'-কে কাউন্ট বা গণনা করে না)strcpy(dest, src)— এটি মূলতsrc-টিকেdest-এর ভেতরে কপি (copies) করে দেয়strcat(dest, src)— এটি মূলতsrc-টিকেdest-এর একেবারে শেষে (end) অ্যাপেন্ড বা যুক্ত (appends) করে দেয়strcmp(a, b)— এটি মূলত যেকোনো দুটি স্ট্রিংয়ের (two strings) তুলনা (compares) করে: যদি তারা সমান (equal) হয় তবে এটি 0 রিটার্ন করে,a < bহলে নেগেটিভ (negative) রিটার্ন করে, এবং যদিa > bহয় তবে এটি পজিটিভ (positive) রিটার্ন করে
এখানকার প্রতিটি ফাংশনই (Each of these) মূলত যতক্ষণ পর্যন্ত ওই '\0'-টিকে খুঁজে না পায়, ততক্ষণ পর্যন্ত ক্যারেক্টার বাই ক্যারেক্টার (character by character) করে সামনের দিকে এগিয়ে যেতে থাকে। তার মানে হলো এখানকার প্রতিটি ফাংশনই O(n) — আর এর মানে হলো আপনার স্ট্রিংটি (string) যত বড় হবে, এগুলো মূলত তত বেশি সময় (time) নেবে।
কার্যকর বা অ্যাকশনে থাকা string.h ফাংশনগুলো (string.h Functions in Action)
strcpy এবং strcat মূলত কখনোই চেক বা যাচাই (check) করে না যে এখানকার গন্তব্য বা ডেস্টিনেশনটি (destination) আসলে যথেষ্ট বড় (big enough) কি না। আপনি যদি ভুল করে কোনো ১০-ক্যারেক্টারের (10-character) বাফারের (buffer) ভেতর একটি ১০০-ক্যারেক্টার (100-character) বিশিষ্ট বড় স্ট্রিংকে (string) কপি বা copy করেন, তবে এটি পরবর্তীতে মেমোরির (memory) যা কিছু পাবে, তার ওপরেই অত্যন্ত আনন্দের (happily) সাথে ওভাররাইট (overwrites) করে ফেলবে — এর ফলে হয় পুরো ডেটাটি নষ্ট (corrupting data) হয়ে যেতে পারে, কিংবা এটি হয়তো প্রোগ্রামটিকে ক্র্যাশ (crashing) করে দিতে পারে, বা এমনকি কোনো বড় ধরনের সিকিউরিটি হোলও (security hole) তৈরি করতে পারে। তাই সবসময় একটি নির্দিষ্ট সাইজ লিমিট বা size limit দিয়ে এখানকার strncpy এবং strncat ফাংশনগুলোকে ব্যবহার করুন, বা আরও ভালো হয় যদি আপনি এর বদলে snprintf-কে ব্যবহার করেন।ক্যারেক্টার ফাংশনগুলো (Character Functions)
যেকোনো সাধারণ বা একক ক্যারেক্টারগুলোর (Individual characters) জন্য <ctype.h>-এর ভেতরে তাদের নিজস্ব কিছু টুলকিট (toolkit) আছে:
toupper('a')মূলত'A'রিটার্ন করে বা ফেরত দেয়tolower('Z')মূলত'z'রিটার্ন করে বা ফেরত দেয়isdigit('5')মূলত নন-জিরো বা non-zero (ট্রু বা true) রিটার্ন করেisalpha('x')মূলত নন-জিরো বা non-zero (ট্রু বা true) রিটার্ন করে
সবসময় মনে রাখবেন: সি-এর (C) যেকোনো char-ই হলো মূলত একটি ছোট ইনটিজার (small integer)। 'A' হলো 65, 'a' হলো 97, '0' হলো 48। তাই চাইলে আপনি সরাসরি ক্যারেক্টারগুলোর (characters) ওপরেও বিভিন্ন ধরনের গণিত বা math কষতে পারেন!
ম্যানুয়াল ইটারেশন (Manual Iteration) এবং ক্যারেক্টার ফাংশনগুলো (Character Functions)
স্ট্রিং লিটারাল (String Literals) বনাম চ্যার অ্যারে (Char Arrays)
এদের মধ্যে খুব ছোট কিন্তু গুরুত্বপূর্ণ কিছু পার্থক্য (subtle but important difference) রয়েছে:
char name[] = "Alice";— এটি মূলত স্ট্যাকের (stack) ওপরে একটি পরিবর্তনযোগ্য বা মিউটেবল (mutable) অ্যারে তৈরি করে। আপনি চাইলে এখানকার ক্যারেক্টারগুলোকে (characters) খুব সহজেই পরিবর্তন (change) করতে পারেন।char *name = "Alice";— এই পয়েন্টারটি মূলত মেমোরির (memory) একটি নির্দিষ্ট বা স্পেশাল সেকশনে সেভ (stored) থাকা একটি রিড-ওনলি (read-only) স্ট্রিং লিটারালকে (string literal) পয়েন্ট (points) করে। এটিকে পরিবর্তন (Modifying) করতে যাওয়ার মানে হলো একটি আনডিফাইন্ড বিহেভিয়ার বা অনির্ধারিত আচরণের (undefined behavior) জন্ম দেওয়া।
আপনার যদি কখনোই কোনো স্ট্রিং-কে (string) মডিফাই বা পরিবর্তন (modify) করার দরকার পড়ে, তবে অবশ্যই সেটিকে অ্যারে ফরমে বা array form-এ ব্যবহার করবেন। আর যদি এটিকে শুধুমাত্র রিড (read) করার প্রয়োজন পড়ে (যেমন printf-এ পাস করানো), তবে একটি লিটারালের পয়েন্টারই (pointer to a literal) যথেষ্ট বা ঠিক আছে (fine)।