সরাসরি পর্বে যান
পর্ব ৫৫ · ৫৬-এর মধ্যে · মডিউল ৮

লিখিত ও মৌখিক (ভাইভা) প্রশ্ন — পূর্ণ নম্বরের উত্তর লেখার কৌশল

Written & Viva Questions in C — Model Answers and How to Score Well

Taught in Bangla, this page prepares you for written exams and viva: what examiners look for, a full-marks answer structure, hand-written code and memory diagrams, 20 definition questions, 15 comparisons, 16 classic programs and 32 rapid-fire viva questions.

এই পর্বে যা শিখবেন

  • একটি লিখিত প্রশ্নের পূর্ণ নম্বরের উত্তর কোন কাঠামোয় সাজাতে হয়
  • খাতায় পরিষ্কার মেমরি ডায়াগ্রাম আঁকা আর হাতে নির্ভুল C কোড লেখা
  • ভাইভায় “জানি না” পরিস্থিতি সামলানো ও পরপর আসা প্রশ্নের জবাব দেওয়া
  • পুরো কোর্সের ৮০টির বেশি ক্লাসিক লিখিত ও মৌখিক প্রশ্নের মডেল উত্তর
  • AI-কে পরীক্ষক হিসেবে ব্যবহার করা — আর তার ভুল ধরা

১ · লিখিত পরীক্ষা ও ভাইভা — পরীক্ষক আসলে কী দেখেন

আগের দুই পর্বে আপনি কোড পড়ে আউটপুট বলা আর MCQ-র অনুশীলন করেছেন। সেখানে উত্তর হয় ঠিক, নয় ভুল। লিখিত পরীক্ষা আর ভাইভা আলাদা জগৎ। এখানে একই প্রশ্নের উত্তর দিয়ে একজন পান পূর্ণ নম্বর, আরেকজন পান অর্ধেক — অথচ দুজনেই বিষয়টি “জানেন”। পার্থক্য হয় উপস্থাপনায়।

বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় C নিয়ে লিখিত প্রশ্ন সাধারণত চার রকম হয়। এই পাতাও সেভাবেই সাজানো:

ক · সংজ্ঞা ও ব্যাখ্যা

“পয়েন্টার কী?”, “Recursion ব্যাখ্যা করুন।” পরীক্ষক দেখেন আপনি ধারণাটি নিজের ভাষায় বোঝাতে পারেন কি না।

অংশ ৫

খ · পার্থক্য লিখুন

“struct ও union-এর পার্থক্য।” টেবিলে লিখলে পরিষ্কার দেখায়, পয়েন্ট গোনাও সহজ।

অংশ ৬

গ · প্রোগ্রাম লিখুন

“একটি সংখ্যা prime কি না যাচাইয়ের প্রোগ্রাম লিখুন।” যুক্তি, পরিচ্ছন্নতা আর edge case — তিনটিই দেখা হয়।

অংশ ৭

ঘ · ভাইভা

মুখে ছোট প্রশ্ন, তারপর উত্তরের সূত্র ধরে আরও গভীর প্রশ্ন। বোঝা আর মুখস্থের পার্থক্য এখানেই ধরা পড়ে।

অংশ ৮

যেকোনো লিখিত উত্তর পড়ার সময় একজন পরীক্ষক মনে মনে কয়েকটি প্রশ্নের উত্তর খোঁজেন: সংজ্ঞাটি কি নির্ভুল? শুধু মুখস্থ, নাকি বোঝা? উদাহরণ আছে কি? কোড কি সত্যিই চলবে? সীমাবদ্ধতা বা ঝুঁকির কথা কি জানে? এই পাতার প্রতিটি মডেল উত্তর এই প্রশ্নগুলো মাথায় রেখে লেখা।

কোর্সের মডিউলপর্বএই পাতায় কোথায়
১ · ভিত্তি০১–০৫ক-০১, ক-০২, ঘ-০১, ঘ-০৩
২ · ভাষার মূল যন্ত্রপাতি০৬–১৫ক-০৩, ক-০৪, ক-০৮; খ-০১, ০২, ০৩, ০৯, ১০, ১২, ১৩; গ-০১–০৬; ঘ-০২, ০৪–০৮
৩ · মেমরি ও পয়েন্টার১৬–২৩ক-০৫–০৭, ০৯–১১, ১৫, ১৬; খ-০৪–০৬; গ-০৭–১১, ১৪, ১৫; ঘ-০৯–১৪
৪ · বড় প্রোগ্রাম সাজানো২৪–৩২ক-১২–১৪; খ-০৮, ১১, ১৪; গ-১৬; ঘ-১৫–২০
৫ · ডেটা স্ট্রাকচার৩৩–৩৮ক-১৮, ক-১৯; খ-০৭; ঘ-২১–২৪
৬ · অ্যালগরিদম৩৯–৪৪ক-২০; গ-১২, গ-১৩; ঘ-২৫, ঘ-২৬
৭ · সিস্টেম প্রোগ্রামিং৪৫–৫২ক-১৭; খ-১৫; ঘ-২৭–৩২

এই পাতার কোনো প্রশ্ন কোনো নির্দিষ্ট পরীক্ষার প্রশ্নপত্র থেকে নেওয়া নয়। এগুলো এমন ধরনের প্রশ্ন, যা নানা পরীক্ষা ও ইন্টারভিউতে প্রায়ই আসে। সিলেবাস, নম্বর বণ্টন আর সময় সবসময় নিজের পরীক্ষার সর্বশেষ বিজ্ঞপ্তি থেকে মিলিয়ে নিন।

২ · পূর্ণ নম্বরের উত্তরের কাঠামো

প্রায় যেকোনো “ব্যাখ্যা করুন” ধরনের প্রশ্নের জন্য একটি পাঁচ ধাপের কাঠামো কাজ করে। একটি ভালো রান্নার রেসিপির মতো ভাবুন — উপকরণ ঠিক থাকলেও ধাপ এলোমেলো হলে খাবার ঠিক হয় না।

সংজ্ঞা → ব্যাখ্যা → চিত্র / উদাহরণ → কোড ও আউটপুট → সতর্কতা, ব্যবহার ও জটিলতা

এবার সবচেয়ে পরিচিত প্রশ্নটি দিয়ে কাঠামোটি হাতে-কলমে দেখি: “পয়েন্টার কী? উদাহরণসহ ব্যাখ্যা করুন।” নিচের ডায়াগ্রামে “পরে” চাপুন — প্রতিটি ধাপে উত্তরপত্রে নতুন অংশ যোগ হচ্ছে, আর উত্তরটি একটু একটু করে পূর্ণ নম্বরের দিকে এগোচ্ছে।

  1. সংজ্ঞাএক বাক্যে
  2. ব্যাখ্যা& আর *
  3. চিত্রবাক্স ও তীর
  4. কোডআউটপুটসহ
  5. সতর্কতা ও ব্যবহারগভীরতার প্রমাণ
উত্তরপত্রধাপ ১ — শুধু সংজ্ঞা
পয়েন্টার হলো এমন একটি ভ্যারিয়েবল, যা অন্য কোনো
ভ্যারিয়েবলের মেমরি ঠিকানা (address) ধারণ করে।

ঘোষণা:   int *p;    /* p একটি int-এর ঠিকানা রাখবে */
উত্তরপত্রধাপ ২ — ব্যাখ্যা যোগ হলো
পয়েন্টার হলো এমন একটি ভ্যারিয়েবল, যা অন্য কোনো
ভ্যারিয়েবলের মেমরি ঠিকানা (address) ধারণ করে।
ঘোষণা:   int *p;

দুটি অপারেটর:
  &x  →  x-এর ঠিকানা দেয় (address-of)
  *p  →  p যে ঠিকানায় আছে, সেখানকার মান দেয় (dereference)
টাইপ কেন লাগে: *p পড়ার সময় কত বাইট পড়বে, আর p + 1
করলে কত বাইট এগোবে — তা টাইপ থেকেই ঠিক হয়।
উত্তরপত্রধাপ ৩ — মেমরি ডায়াগ্রাম
… (সংজ্ঞা ও ব্যাখ্যা আগের মতো) …

        x                       p
   +----------+           +------------+
   |    10    | <-------- |  0x1000    |
   +----------+           +------------+
   ঠিকানা 0x1000           ঠিকানা 0x2000
(ঠিকানাগুলো কাল্পনিক — প্রতিবার চালালে বদলাতে পারে)
উত্তরপত্রধাপ ৪ — কোড ও আউটপুট
… (সংজ্ঞা, ব্যাখ্যা, ডায়াগ্রাম) …

int x = 10;
int *p = &x;          /* p-তে x-এর ঠিকানা */
*p = 20;              /* p দিয়ে x বদলানো  */
printf("%d\n", x);    /* আউটপুট: 20       */
ব্যাখ্যা: *p = 20 লেখায় x নিজেই বদলে গেছে, কারণ
p আর x একই মেমরির ঘরের দিকে তাকিয়ে আছে।
উত্তরপত্রধাপ ৫ — সম্পূর্ণ উত্তর
… (সংজ্ঞা, ব্যাখ্যা, ডায়াগ্রাম, কোড) …

ব্যবহার: ফাংশনের ভেতর থেকে মূল ভ্যারিয়েবল বদলানো (swap),
array ও string নিয়ে কাজ, malloc দিয়ে dynamic memory,
linked list ও tree-র মতো ডেটা স্ট্রাকচার।
সতর্কতা: ঘোষণার সময় NULL দিয়ে শুরু করুন; free করার পর
dangling pointer ব্যবহার করবেন না; NULL pointer-কে
dereference করা undefined behavior.
  1. ধাপ ১ — সংজ্ঞা। এক বাক্যে, নির্ভুল শব্দে। “ঠিকানা রাখে এমন ভ্যারিয়েবল” — এই মূল কথাটি না থাকলে বাকি উত্তর যত সুন্দরই হোক, শুরুতেই নম্বর কাটা যায়। সাথে ঘোষণার syntax দিলে সংজ্ঞাটি দৃঢ় হয়।
  2. ধাপ ২ — ব্যাখ্যা। এখানে দেখান যে আপনি শুধু মুখস্থ করেননি। & আর * — দুটি অপারেটরের কাজ, আর টাইপ কেন জরুরি। অনেকে এখানেই থেমে যান; তাঁরা সাধারণত অর্ধেকের মতো নম্বর পান।
  3. ধাপ ৩ — চিত্র। বাক্স আর তীর দিয়ে একটি মেমরি ডায়াগ্রাম। পরীক্ষক এক নজরে বোঝেন আপনার মাথায় ছবিটি পরিষ্কার। ঠিকানা কাল্পনিক — সেটি লিখে দিলে আরও পেশাদার দেখায়।
  4. ধাপ ৪ — কোড ও আউটপুট। ৪–৫ লাইনের ছোট, সম্পূর্ণ উদাহরণ, প্রতিটি লাইনের পাশে ছোট মন্তব্য, শেষে আউটপুট। লম্বা প্রোগ্রাম নয় — যা দেখাতে চান, শুধু সেটুকু।
  5. ধাপ ৫ — ব্যবহার ও সতর্কতা। এই অংশ আপনাকে “ভালো” থেকে “সেরা” উত্তরে নেয়। NULL pointer, dangling pointer, undefined behavior — এই শব্দগুলো দেখায় আপনি বাস্তব ঝুঁকিও জানেন। এখন উত্তরটি সম্পূর্ণ।
একটি সম্ভাব্য নম্বর-ভাগ (আমাদের পরামর্শ)

ধরুন প্রশ্নটি ১০ নম্বরের। মনে মনে এভাবে ভাগ করে লিখুন: সংজ্ঞা ২, ব্যাখ্যা ২, চিত্র ২, কোড ও আউটপুট ৩, ব্যবহার ও সতর্কতা ১. প্রতিটি পরীক্ষকের ভাগ আলাদা হতে পারে, কিন্তু পাঁচটি অংশই থাকলে কোনো ভাগেই বড় ফাঁক থাকে না।

প্রশ্ন ছোট হলে (২–৩ নম্বর) কাঠামোটি ছোট করুন: সংজ্ঞা + একটি উদাহরণ + একটি সতর্কতা। আর “পার্থক্য লিখুন” প্রশ্নে কাঠামোর বদলে টেবিল ব্যবহার করুন — অংশ ৬-এ দেখবেন।

৩ · হাতে কোড লেখা, মেমরি ডায়াগ্রাম আর সময় ভাগ

কম্পিউটারে compiler ভুল ধরিয়ে দেয়। খাতায় সেই সুবিধা নেই — পরীক্ষকই আপনার compiler. তাই হাতে লেখা কোডে কয়েকটি অভ্যাস খুব জরুরি।

হাতে কোড লেখার সাত নিয়ম

  1. পূর্ণ প্রোগ্রাম লিখুন: #include, int main(void), return 0; — তিনটিই। void main() লিখবেন না; standard C-তে hosted প্রোগ্রামের main ফেরত দেয় int.
  2. Indentation: প্রতিটি {-এর পর চার ঘর ভেতরে। খাতায় ভাঁজ করা লাইন টানলে block চোখে পড়ে।
  3. অর্থবহ নাম: a, b-এর বদলে sum, count, is_prime. পরীক্ষক কম পড়েই বোঝেন।
  4. ছোট মন্তব্য: কৌশলের লাইনে এক লাইনের মন্তব্য — /* i*i <= n পর্যন্ত দেখলেই যথেষ্ট */.
  5. Edge case লিখে দেখান: n = 0, n = 1, ঋণাত্মক সংখ্যা, খালি string. কোডে না রাখলেও নিচে এক লাইনে লিখুন “n < 2 হলে prime নয়”।
  6. শুরুর মান দিন: sum = 0, fact = 1. শুরুর মান না দেওয়া ভ্যারিয়েবলে garbage value থাকে — খাতায় এটিই সবচেয়ে সাধারণ ভুল।
  7. নমুনা ইনপুট-আউটপুট: প্রোগ্রামের নিচে লিখুন “Input: 5 → Output: 120”. এটি দেখায় আপনি নিজে চালিয়ে দেখেছেন।

Dry run — কাগজে প্রোগ্রাম চালানো

কোড লেখার পর একটি ছোট ইনপুট নিয়ে টেবিলে প্রতিটি ধাপ লিখুন। একে বলে dry run বা trace table. খাতায় এটি থাকলে পরীক্ষক নিশ্চিত হন যুক্তিটি আপনি বুঝেছেন। নিচে gcd(48, 18)-এর Euclid পদ্ধতির dry run:

ধাপabr = a % b
১481812
২18126
৩1260
৪60লুপ শেষ → উত্তর 6

মেমরি ডায়াগ্রাম আঁকার নিয়ম

  • প্রতিটি ভ্যারিয়েবল একটি বাক্স; বাক্সের ওপরে নাম, ভেতরে মান, নিচে (কাল্পনিক) ঠিকানা।
  • পয়েন্টারের বাক্সে ঠিকানা লিখুন, আর সেখান থেকে লক্ষ্যের দিকে তীর টানুন।
  • Array আঁকুন পাশাপাশি লাগানো বাক্সে, নিচে index 0, 1, 2… — contiguous memory চোখে পড়বে।
  • String-এর শেষে আলাদা একটি বাক্সে '\0' দেখান।
  • Stack আর heap আলাদা দুই কলামে রাখুন — malloc-এর মেমরি heap কলামে, পয়েন্টার ভ্যারিয়েবলটি stack কলামে।
   STACK                               HEAP
 +----------+                    +----+----+----+
 | p: 0x5a0 | -----------------> | 10 | 20 | 30 |   int *p = malloc(3 * sizeof *p);
 +----------+                    +----+----+----+
                                   [0]  [1]  [2]

সময় ভাগ

একটি সহজ সূত্র: মোট মিনিট ÷ মোট নম্বর = প্রতি নম্বরে কত মিনিট। যেমন ১৮০ মিনিটে ৬০ নম্বর হলে প্রতি নম্বরে ৩ মিনিট — ১০ নম্বরের প্রশ্নে ৩০ মিনিটের বেশি নয়। শুরুতে পুরো প্রশ্নপত্র একবার পড়ুন, সবচেয়ে আত্মবিশ্বাসের প্রশ্ন দিয়ে শুরু করুন, আর শেষ ১০ মিনিট রাখুন রিভিশনের জন্য — ;, &, return 0; বাদ পড়ল কি না দেখতে। কোনো প্রশ্নে আটকে গেলে জায়গা ফাঁকা রেখে এগিয়ে যান।

খাতায় যে ভুলগুলো সবচেয়ে বেশি নম্বর কাটায়: scanf("%d", n) (& নেই), if (x = 5), লুপে <= আর < গুলিয়ে ফেলা (off-by-one), conio.h ও getch() (standard C নয়), gets() (C11 থেকে ভাষা থেকেই বাদ), আর fflush(stdin) (standard অনুযায়ী input stream-এ এর আচরণ undefined).

৪ · ভাইভায় কথা বলার কৌশল

ভাইভা অনেকটা ক্রিকেটে DRS রিভিউয়ের মতো — প্রথম সিদ্ধান্তের পর তৃতীয় আম্পায়ার একের পর এক অ্যাঙ্গেল দেখেন। পরীক্ষকও আপনার প্রথম উত্তর শুনে সেখান থেকেই পরের প্রশ্ন বানান। তাই ভাইভার আসল দক্ষতা হলো প্রশ্নের শিকল সামলানো।

  • ছোট করে শুরু, পরে বিস্তার: প্রথমে এক–দুই বাক্যে সরাসরি উত্তর। পরীক্ষক চাইলে বিস্তারে যান।
  • Think aloud: উত্তর না জানলেও ভাবনাটা জোরে বলুন — “আমি এভাবে ভাবছি…”. পরীক্ষক আপনার যুক্তির পথ দেখতে চান।
  • সৎ থাকুন: “জানি না, তবে এভাবে ভাবছি…” বলা দুর্বলতা নয়। আন্দাজে ভুল তথ্য জোর দিয়ে বলা অনেক বেশি ক্ষতিকর।
  • বাস্তবের সাথে জুড়ুন: “Linked list কোথায় লাগে?” — “যেমন একটি মিউজিক প্লেলিস্টে মাঝখানে গান যোগ-বাদ।” বাস্তব উদাহরণ দেখায় আপনি বিষয়টি ব্যবহার করতে জানেন।
  • বোর্ড বা কাগজ চান: জটিল কিছু হলে বলুন “একটু এঁকে দেখাই?” — মেমরি ডায়াগ্রাম মুখের কথার চেয়ে শক্তিশালী।
  • প্রশ্ন না বুঝলে জিজ্ঞেস করুন: “আপনি কি 32-bit না 64-bit সিস্টেমের কথা বলছেন?” — এমন পাল্টা প্রশ্ন বোঝায় আপনি জানেন উত্তরটি পরিবেশের ওপর নির্ভর করে।

নিচের ডায়াগ্রামে একটি বাস্তবধর্মী ভাইভার শিকল দেখুন। প্রতিটি ধাপে প্রশ্ন একটু গভীরে যাচ্ছে, আর শেষ ধাপে প্রার্থী পুরোপুরি নিশ্চিত নন — তবু সঠিকভাবে সামলাচ্ছেন।

প্রশ্ন ১সহজ শুরু
পরীক্ষক: Array কী?
আপনি:   একই টাইপের কয়েকটি মান, মেমরিতে পাশাপাশি লাগানো
        ঘরে রাখা হয়; index 0 থেকে শুরু। যেমন int a[5].
প্রশ্ন ২একটু গভীরে
পরীক্ষক: তাহলে array-র নাম কি একটি পয়েন্টার?
আপনি:   না, তবে বেশিরভাগ expression-এ নামটি প্রথম
        element-এর পয়েন্টারে বদলে যায় — একে বলে array decay.
        তাই a[i] আর *(a + i) একই জিনিস।
প্রশ্ন ৩পার্থক্য যাচাই
পরীক্ষক: int a[5]; int *p = a; — sizeof(a) আর sizeof(p) কি সমান?
আপনি:   না। sizeof(a) পুরো array: 5 × 4 = 20 বাইট
        (int 4 বাইট ধরে)। sizeof(p) শুধু পয়েন্টারের মাপ —
        সাধারণ 64-bit সিস্টেমে 8 বাইট। sizeof-এর ক্ষেত্রে
        decay হয় না।
প্রশ্ন ৪বাস্তব প্রয়োগ
পরীক্ষক: ফাংশনে array পাঠালে ভেতরে sizeof কত দেখাবে?
আপনি:   পয়েন্টারের মাপ, অর্থাৎ 8. ফাংশনের parameter
        int arr[] আসলে int *arr. তাই array-র দৈর্ঘ্য আলাদা
        parameter হিসেবে পাঠাতে হয়, যেমন sum(arr, n).
প্রশ্ন ৫কঠিন — পুরো নিশ্চিত নন
পরীক্ষক: a আর &a-র মধ্যে পার্থক্য কী?
আপনি:   পুরো নিশ্চিত নই, তবে এভাবে ভাবছি — দুটোই একই
        ঠিকানা দেখাবে, কারণ array শুরু হয় প্রথম element থেকে।
        কিন্তু টাইপ আলাদা হওয়ার কথা: a হয় int *, আর &a
        হয় পুরো array-র পয়েন্টার, int (*)[5]. তাই a + 1
        এগোবে 4 বাইট, আর &a + 1 এগোবে 20 বাইট।
  1. সহজ শুরু। সংজ্ঞা সংক্ষেপে, সাথে একটি উদাহরণ। এখানে বেশি বলার দরকার নেই — পরীক্ষক পরের প্রশ্নের জন্য সূত্র খুঁজছেন।
  2. প্রচলিত ভুল ধারণা পরীক্ষা। “array মানেই পয়েন্টার” — অনেকে হ্যাঁ বলে ফেলেন। সঠিক উত্তর “না, তবে decay হয়।” এক শব্দেই বোঝা যায় আপনি গভীরে জানেন।
  3. দাবির প্রমাণ। পরীক্ষক আপনার আগের কথাটি যাচাই করছেন। উত্তরে অনুমান স্পষ্ট বলুন — “int 4 বাইট ধরে”, “64-bit সিস্টেমে”। এই সতর্কতা অভিজ্ঞতার চিহ্ন।
  4. বাস্তব প্রয়োগে টেনে আনা। এখানে বেশিরভাগ বাস্তব বাগ জন্মায় — ফাংশনের ভেতরে sizeof দিয়ে দৈর্ঘ্য বের করার চেষ্টা। সমাধানসহ বলায় উত্তরটি পূর্ণ।
  5. “জানি না” সামলানো। প্রার্থী সৎভাবে বলছেন নিশ্চিত নন, তারপর যুক্তি দিয়ে এগোচ্ছেন — আর যুক্তিটি আসলে সঠিক। নিশ্চিত না হলেও এভাবে ভেবে বলা উত্তর প্রায়ই পূর্ণ কৃতিত্ব পায়, অন্তত আংশিক তো বটেই।

ভাইভাকে ঢাকার রাস্তায় রিকশাচালককে ঠিকানা বোঝানোর মতো ভাবুন। “মিরপুর ১০” বললেই হয় না — তিনি জিজ্ঞেস করবেন “গোলচত্বরের কোন দিকে?” আপনি যত পরিষ্কার চিহ্ন দেবেন, তিনি তত কম প্রশ্ন করবেন। পরীক্ষকও তাই — শুরুর উত্তর যত নির্ভুল ও পরিষ্কার, পরের প্রশ্ন তত কম জেরা হয়ে আসে।

৫ · অংশ ক — সংজ্ঞা ও ব্যাখ্যা (২০টি প্রশ্ন)

প্রতিটি প্রশ্ন পড়ে আগে নিজে খাতায় ৫–৮ লাইনে উত্তর লিখুন, তারপর “→ উত্তর” চেপে মেলান। মডেল উত্তরগুলো ইচ্ছে করেই ছোট রাখা হয়েছে — পরীক্ষায় নম্বর বেশি হলে অংশ ২-এর কাঠামো মেনে চিত্র ও কোড যোগ করুন।

ক-০১Compiler ও interpreter-এর পার্থক্য কী? C কোনটি ব্যবহার করে? (সহজ)

Compiler পুরো source code একবারে machine code-এ অনুবাদ করে একটি আলাদা executable ফাইল বানায়; পরে সেই ফাইল যতবার খুশি চালানো যায়। Interpreter প্রোগ্রাম চলার সময় কোড পড়ে পড়ে চালায়, আলাদা ফাইল বানায় না।

ফলাফল: compiled প্রোগ্রাম সাধারণত দ্রুত চলে, আর অনেক ভুল চালানোর আগেই ধরা পড়ে; interpreted ভাষায় লেখা-পরীক্ষা দ্রুত, কিন্তু চালানো তুলনামূলক ধীর। C একটি compiled ভাষা — GCC বা Clang preprocess, compile, assemble ও link করে প্রোগ্রাম তৈরি করে। Python-এর প্রচলিত বাস্তবায়ন interpreter ব্যবহার করে (ভেতরে আগে bytecode বানিয়ে নেয়)।

ঝালিয়ে নিন: পর্ব ০৩ · টুলচেইন

ক-০২Token কী? int sum = a + 5; লাইনে কয়টি token আছে? (সহজ)

Token হলো প্রোগ্রামের ক্ষুদ্রতম অর্থবহ একক, যা compiler আলাদাভাবে চেনে। C-তে token-এর ধরন: keyword (int, while), identifier (sum), constant (5), string literal ("Dhaka"), আর operator ও punctuator (+, =, ;). অনেক পাঠ্যবইয়ে operator আর special symbol আলাদা ধরে ছয় ধরন বলা হয়; standard এগুলোকে punctuator নামে একসাথে রাখে।

int sum = a + 5; লাইনে token সাতটি: int, sum, =, a, +, 5, ;. Space ও comment token নয় — compiler এগুলো ফেলে দেয়।

ঝালিয়ে নিন: পর্ব ০৪ · প্রথম প্রোগ্রাম

ক-০৩Variable ও constant কী? C-তে constant বানানোর উপায়গুলো লিখুন। (সহজ)

Variable হলো মেমরির একটি নামযুক্ত জায়গা, যার মান প্রোগ্রাম চলার সময় বদলানো যায়; প্রতিটি variable-এর একটি টাইপ থাকে (int age = 20;). Constant এমন মান যা বদলায় না।

C-তে constant পাওয়ার উপায়: (১) literal — সরাসরি লেখা মান, যেমন 3.14, 'A'; (২) const qualifier — const int MAX = 100;, বদলানোর চেষ্টা করলে compiler error দেয়; (৩) #define PI 3.14159 — preprocessor লেখা বদলে দেয়, টাইপ থাকে না; (৪) enum — নামযুক্ত পূর্ণসংখ্যার constant. আধুনিক কোডে const বা enum সাধারণত বেশি নিরাপদ, কারণ টাইপ যাচাই হয়।

ঝালিয়ে নিন: পর্ব ০৬ · ভ্যারিয়েবল ও টাইপ

ক-০৪Storage class কী? C-এর storage class-গুলো ব্যাখ্যা করুন। (মাঝারি)

Storage class ঠিক করে একটি ভ্যারিয়েবল কোথায় দেখা যায় (scope), কতক্ষণ বাঁচে (lifetime), আর শুরুর মান না দিলে কী থাকে।

  • auto — ফাংশনের ভেতরের সাধারণ ভ্যারিয়েবলের ডিফল্ট; block শেষ হলেই শেষ; শুরুর মান অনির্ধারিত (garbage).
  • register — compiler-কে ইঙ্গিত “দ্রুত রাখো”; এর ঠিকানা (&) নেওয়া যায় না। আধুনিক compiler নিজেই সিদ্ধান্ত নেয়।
  • static — ফাংশনের ভেতরে হলে পুরো প্রোগ্রাম জুড়ে বাঁচে এবং ডাকের মাঝে মান মনে রাখে; বাইরে হলে শুধু সেই ফাইলে দেখা যায়। শুরুর মান 0.
  • extern — অন্য ফাইলে সংজ্ঞায়িত global ভ্যারিয়েবল বা ফাংশন এখানে ব্যবহারের ঘোষণা।

C11 থেকে _Thread_local যোগ হয়েছে — প্রতিটি thread-এর আলাদা কপি।

ঝালিয়ে নিন: পর্ব ১৪ · স্কোপ ও স্টোরেজ ক্লাস

ক-০৫পয়েন্টার কী? সংক্ষেপে উদাহরণসহ লিখুন। (সহজ)

পয়েন্টার এমন একটি ভ্যারিয়েবল, যা অন্য কোনো ভ্যারিয়েবলের মেমরি ঠিকানা রাখে। &x দেয় x-এর ঠিকানা (address-of), আর *p দেয় p যে ঠিকানায় আছে সেখানকার মান (dereference).

উদাহরণ: int x = 10; int *p = &x; *p = 20; — এরপর x-এর মান 20, কারণ p আর x একই ঘরের দিকে নির্দেশ করে। পয়েন্টারের টাইপ ঠিক করে *p কত বাইট পড়বে আর p + 1 কত বাইট এগোবে। ব্যবহার: ফাংশন থেকে মূল মান বদলানো, array ও string, dynamic memory, linked list. পূর্ণ নম্বরের বিস্তারিত উত্তর অংশ ২-এর ডায়াগ্রামে দেখুন।

ঝালিয়ে নিন: পর্ব ১৭ · পয়েন্টার ১

ক-০৬NULL pointer কী? এর ব্যবহার লিখুন। (সহজ)

NULL pointer এমন একটি পয়েন্টার, যা নিশ্চিতভাবে কোনো বৈধ বস্তুকে নির্দেশ করে না। NULL একটি macro (stddef.h, stdio.h, stdlib.h-এ সংজ্ঞায়িত); C23 থেকে nullptr-ও আছে।

ব্যবহার: (১) পয়েন্টার ঘোষণার সময় শুরুর মান — int *p = NULL;; (২) ব্যর্থতা জানানো — malloc ও fopen ব্যর্থ হলে NULL ফেরত দেয়, তাই সবসময় if (p == NULL) যাচাই করতে হয়; (৩) linked list-এর শেষ চিহ্ন। NULL pointer-কে dereference করা undefined behavior; Linux-এ সাধারণত segmentation fault হয়ে প্রোগ্রাম বন্ধ হয়।

ঝালিয়ে নিন: পর্ব ১৭ · পয়েন্টার ১

ক-০৭Dangling pointer কী? কীভাবে তৈরি হয় এবং কীভাবে এড়াবেন? (মাঝারি)

Dangling pointer এমন পয়েন্টার, যা এমন মেমরিকে নির্দেশ করছে যার মেয়াদ আর নেই। এটি ব্যবহার করা undefined behavior.

তৈরি হওয়ার তিন পথ: (১) free(p)-এর পরও p ব্যবহার (use-after-free); (২) ফাংশন থেকে local ভ্যারিয়েবলের ঠিকানা ফেরত দেওয়া — ফাংশন শেষ হলেই সেই stack জায়গা বাতিল; (৩) block-এর ভেতরের ভ্যারিয়েবলের ঠিকানা block-এর বাইরে ব্যবহার।

এড়ানোর উপায়: free(p); p = NULL;, local-এর ঠিকানা ফেরত না দেওয়া (দরকার হলে malloc বা caller-এর দেওয়া buffer), আর AddressSanitizer দিয়ে পরীক্ষা। তুলনা: wild pointer হলো যাকে কখনো শুরুর মানই দেওয়া হয়নি।

ঝালিয়ে নিন: পর্ব ২০ · ডায়নামিক মেমরি

ক-০৮Recursion কী? Base case না থাকলে কী হয়? (সহজ)

Recursion হলো একটি ফাংশনের নিজেকেই ডাকা, প্রতিবার সমস্যার ছোট সংস্করণ দিয়ে। এর দুটি অংশ: base case — যেখানে আর নিজেকে না ডেকে সরাসরি উত্তর দেয়; আর recursive case — যেখানে ছোট সমস্যার জন্য নিজেকে ডাকে। যেমন fact(n) = n * fact(n - 1), base case fact(0) = 1.

প্রতিটি ডাকে call stack-এ একটি নতুন frame জমে। Base case না থাকলে বা কখনো না পৌঁছালে ডাক থামে না, stack-এর জায়গা ফুরিয়ে যায় — stack overflow, সাধারণত crash. Recursion কোড ছোট ও পরিষ্কার করে (tree, divide and conquer), কিন্তু loop-এর চেয়ে বাড়তি মেমরি ও সময় খরচ হতে পারে।

ঝালিয়ে নিন: পর্ব ১৫ · রিকার্শন

ক-০৯Array ও পয়েন্টারের সম্পর্ক ব্যাখ্যা করুন। Array কি পয়েন্টার? (মাঝারি)

Array হলো একই টাইপের মানের পাশাপাশি সাজানো ব্লক; পয়েন্টার হলো একটি ঠিকানা রাখার ভ্যারিয়েবল। দুটি এক নয়, কিন্তু ঘনিষ্ঠ: বেশিরভাগ expression-এ array-র নাম তার প্রথম element-এর পয়েন্টারে রূপ নেয় (array decay). তাই a[i] আসলে *(a + i), আর ফাংশনে array পাঠালে পয়েন্টারই যায়।

পার্থক্যগুলো: sizeof a পুরো array-র মাপ দেয়, পয়েন্টারের নয়; a = b; লেখা যায় না, কিন্তু পয়েন্টারে নতুন ঠিকানা বসানো যায়; &a-র টাইপ পুরো array-র পয়েন্টার। Decay হয় না তিন জায়গায়: sizeof, &, আর string literal দিয়ে char array-র শুরুর মান দেওয়ার সময়।

ঝালিয়ে নিন: পর্ব ১৮ · পয়েন্টার ২

ক-১০C-তে string কীভাবে রাখা হয়? "Dhaka" রাখতে কত বাইট লাগে? (সহজ)

C-তে আলাদা string টাইপ নেই। String হলো char-এর array, যার শেষে একটি null terminator '\0' থাকে — এটি দেখেই strlen, printf("%s") বোঝে string কোথায় শেষ। "Dhaka"-তে অক্ষর ৫টি, তাই লাগে ৬ বাইট: char city[6] = "Dhaka";.

সতর্কতা: (১) '\0'-এর জায়গা না রাখলে string-এর শেষ খুঁজে না পেয়ে ফাংশন মেমরির বাইরে পড়ে; (২) char *s = "Dhaka"; একটি string literal-কে নির্দেশ করে, যা বদলানো undefined behavior; (৩) তুলনা == দিয়ে নয়, strcmp দিয়ে — == শুধু ঠিকানা তুলনা করে।

ঝালিয়ে নিন: পর্ব ১৯ · স্ট্রিং

ক-১১Structure ও union কী? প্রত্যেকটির একটি ব্যবহার লিখুন। (সহজ)

Structure হলো বিভিন্ন টাইপের কয়েকটি মানকে এক নামে বাঁধা নিজের তৈরি টাইপ — যেমন একজন ছাত্রের roll, নাম আর নম্বর। প্রতিটি member-এর আলাদা জায়গা থাকে, তাই সবগুলো একসাথে রাখা যায়।

Union-এ সব member একই মেমরি ভাগ করে; মাপ হয় সবচেয়ে বড় member-এর সমান (alignment-এর জন্য একটু বাড়তে পারে)। এক সময়ে শুধু একটি member-এর মান অর্থবহ। ব্যবহার: যেখানে একটি মান কয়েক রকমের যেকোনো একটি হতে পারে — যেমন একটি নেটওয়ার্ক বার্তায় হয় সংখ্যা, নয় লেখা; সাথে একটি tag রাখা হয় কোনটি চলছে বোঝাতে (tagged union). Member পড়া হয় . দিয়ে, পয়েন্টার থাকলে -> দিয়ে।

ঝালিয়ে নিন: পর্ব ২২ · struct ও union

ক-১২fopen-এর file mode-গুলো ব্যাখ্যা করুন। (মাঝারি)

File mode জানায় ফাইল কী উদ্দেশ্যে খোলা হচ্ছে:

  • "r" — শুধু পড়া; ফাইল না থাকলে fopen NULL দেয়।
  • "w" — লেখা; ফাইল না থাকলে তৈরি করে, থাকলে আগের সব লেখা মুছে ফেলে।
  • "a" — শেষে যোগ (append); আগের লেখা থাকে, নতুন লেখা সবসময় শেষে বসে।
  • "r+" — পড়া ও লেখা, ফাইল থাকতেই হবে; "w+" — পড়া ও লেখা, আগের লেখা মুছে; "a+" — পড়া ও শেষে যোগ।

শেষে b যোগ করলে ("rb", "wb") binary mode — কোনো অনুবাদ ছাড়া হুবহু বাইট। C11 থেকে "wx": ফাইল আগে থেকে থাকলে খোলা ব্যর্থ হয়। সবসময় NULL যাচাই আর কাজ শেষে fclose.

ঝালিয়ে নিন: পর্ব ২৪ · ফাইল I/O

ক-১৩Macro কী? #define SQUARE(x) x*x লেখায় কী সমস্যা? (মাঝারি)

Macro হলো #define দিয়ে দেওয়া একটি নাম, যাকে preprocessor compile-এর আগে লেখা হিসেবে বদলে দেয়। দুই রকম: object-like (#define MAX 100) আর function-like (#define SQUARE(x) ...). Macro-তে টাইপ যাচাই হয় না, ফাংশন ডাকার খরচও নেই।

SQUARE(2 + 3) বদলে হয় 2 + 3*2 + 3 = 11, 25 নয় — কারণ শুধু লেখা বসানো হয়, অগ্রাধিকার মানা হয় না। সমাধান: #define SQUARE(x) ((x) * (x)). তবু SQUARE(i++) লিখলে i দুবার বাড়ানো হয় — এটি double evaluation, আর এক expression-এ একই ভ্যারিয়েবল দুবার বদলানো undefined behavior. তাই এমন ক্ষেত্রে static inline ফাংশন ভালো।

ঝালিয়ে নিন: পর্ব ২৭ · প্রিপ্রসেসর ও ম্যাক্রো

ক-১৪Header file কী? এতে কী রাখা উচিত, কী নয়? (সহজ)

Header file (.h) হলো একটি ফাইল, যাতে ঘোষণা থাকে — কী কী ফাংশন, টাইপ ও macro পাওয়া যাবে। #include করলে preprocessor ফাইলটির লেখা সেখানে বসিয়ে দেয়। যেমন stdio.h-তে printf-এর prototype আছে, আসল কোড আছে C library-তে।

রাখুন: function prototype, struct/typedef, macro, extern ঘোষণা। রাখবেন না: ফাংশনের পূর্ণ সংজ্ঞা বা global ভ্যারিয়েবলের সংজ্ঞা — একাধিক .c ফাইল include করলে linker “multiple definition” error দেবে (ব্যতিক্রম: static inline ফাংশন)। একই header দুবার ঢোকা ঠেকাতে include guard (#ifndef … #define … #endif) ব্যবহার করুন।

ঝালিয়ে নিন: পর্ব ২৯ · মডুলার প্রোগ্রাম

ক-১৫Dynamic memory allocation কী? এর ফাংশনগুলো লিখুন। (মাঝারি)

Dynamic memory allocation মানে প্রোগ্রাম চলার সময় দরকারমতো মেমরি নেওয়া ও ফেরত দেওয়া। এই মেমরি আসে heap থেকে, আর যতক্ষণ না আপনি ফেরত দেন ততক্ষণ থাকে — ফাংশন শেষ হলেও।

  • malloc(size) — size বাইট দেয়, ভেতরের মান অনির্ধারিত।
  • calloc(n, size) — n টি element-এর জায়গা দেয়, সব বাইট শূন্য করে।
  • realloc(p, size) — আগের ব্লক বড় বা ছোট করে; দরকারে নতুন জায়গায় সরিয়ে পুরোনো ডেটা কপি করে।
  • free(p) — মেমরি ফেরত দেয়।

নিয়ম: প্রতিটি ফেরত মান NULL কি না যাচাই, প্রতিটি allocation ঠিক একবার free, আর p = realloc(p, …) না লিখে আগে অস্থায়ী পয়েন্টারে নিন — ব্যর্থ হলে আসল ঠিকানা হারাবে না।

ঝালিয়ে নিন: পর্ব ২০ · ডায়নামিক মেমরি

ক-১৬Memory leak কী? কীভাবে ধরবেন? (মাঝারি)

Memory leak হলো heap থেকে নেওয়া মেমরি কাজ শেষে free না করা, বিশেষত যখন সেই মেমরির ঠিকানাও হারিয়ে যায় — তখন আর কখনোই ফেরত দেওয়া সম্ভব নয়। যেমন লুপের ভেতরে বারবার malloc, কিন্তু free নেই; বা p = malloc(...) দুবার লেখা, প্রথম ঠিকানা হারিয়ে গেল।

ছোট প্রোগ্রাম শেষ হলে OS সব মেমরি ফেরত নেয়, তাই চোখে পড়ে না। কিন্তু দিনের পর দিন চলা সার্ভার বা এমবেডেড ডিভাইসে মেমরি ধীরে ধীরে ফুরিয়ে যায়, শেষে প্রোগ্রাম ধীর হয় বা বন্ধ হয়। ধরার উপায়: Valgrind (valgrind --leak-check=full ./prog), আর GCC/Clang-এ -fsanitize=address — Linux-এ এর সাথে leak detection-ও চালু থাকে।

ঝালিয়ে নিন: পর্ব ২০ · ডায়নামিক মেমরি, পর্ব ৩১ · ডিবাগিং

ক-১৭Undefined behavior কী? উদাহরণ দিন। এটি implementation-defined থেকে কীভাবে আলাদা? (কঠিন)

Undefined behavior (UB) হলো এমন কাজ, যার ফল সম্পর্কে C standard কোনো শর্তই দেয় না — প্রোগ্রাম ঠিক চলতে পারে, ভুল ফল দিতে পারে, crash করতে পারে, বা optimization চালু করলে আচরণ বদলে যেতে পারে। Compiler ধরে নেয় কোডে UB নেই, এবং সেই অনুমানে কোড বদলায়।

উদাহরণ: signed int overflow (INT_MAX + 1), array-র সীমার বাইরে পড়া-লেখা, NULL বা dangling pointer dereference, শুরুর মান ছাড়া local ভ্যারিয়েবলের মান ব্যবহার, আর i = i++ + 1;-এর মতো একই ভ্যারিয়েবল ক্রমহীনভাবে দুবার বদলানো।

Implementation-defined আচরণ প্রতিটি compiler নিজের মতো ঠিক করে এবং নথিতে লিখে রাখে — যেমন sizeof(int), বা char signed নাকি unsigned. Unspecified মানে কয়েকটি সম্ভাবনার যেকোনোটি, নথিভুক্ত করার দরকার নেই — যেমন ফাংশনের argument-গুলোর মূল্যায়নের ক্রম।

ঝালিয়ে নিন: পর্ব ৪৬ · Undefined Behavior ও নিরাপদ কোডিং

ক-১৮Linked list কী? Array-র তুলনায় এর সুবিধা-অসুবিধা লিখুন। (মাঝারি)

Linked list হলো node-এর একটি শিকল; প্রতিটি node-এ থাকে ডেটা আর পরের node-এর ঠিকানা। প্রথম node-এর ঠিকানা রাখে head, শেষ node-এর next হয় NULL.

struct node {
    int data;
    struct node *next;   /* পরের node-এর ঠিকানা */
};

সুবিধা: আকার চলার সময় বাড়ে-কমে; জানা node-এর পরে যোগ বা বাদ O(1), কিছু সরাতে হয় না। অসুবিধা: i-তম element পেতে শুরু থেকে হাঁটতে হয় — O(n); প্রতি node-এ পয়েন্টারের বাড়তি মেমরি; node-গুলো মেমরিতে ছড়ানো, তাই cache-এ ধীর। ধরন: singly, doubly, circular.

ঝালিয়ে নিন: পর্ব ৩৩ · লিংকড লিস্ট

ক-১৯Stack ও queue কী? বাস্তব ব্যবহার লিখুন। (সহজ)

Stack চলে LIFO নিয়মে — যে শেষে ঢোকে, সে আগে বের হয়, ঠিক থালার স্তূপের মতো। মূল কাজ push (ওপরে রাখা) আর pop (ওপর থেকে নেওয়া)। ব্যবহার: ফাংশন কলের call stack, undo, ব্র্যাকেট মেলানো, expression হিসাব, DFS.

Queue চলে FIFO নিয়মে — যে আগে আসে, সে আগে যায়, ঠিক ব্যাংকের লাইনের মতো। মূল কাজ enqueue (পেছনে যোগ) আর dequeue (সামনে থেকে নেওয়া)। ব্যবহার: প্রিন্টারের কাজের লাইন, BFS, CPU-র round-robin scheduling, নেটওয়ার্কের buffer. দুটিই array বা linked list দিয়ে বানানো যায়; array-তে queue-র জন্য circular buffer সুবিধাজনক। সব মূল কাজ O(1).

ঝালিয়ে নিন: পর্ব ৩৪ · স্ট্যাক ও কিউ

ক-২০Time complexity কী? Big-O notation উদাহরণসহ ব্যাখ্যা করুন। (মাঝারি)

Time complexity মাপে ইনপুটের আকার n বাড়লে একটি অ্যালগরিদমের ধাপের সংখ্যা কীভাবে বাড়ে — সেকেন্ডে নয়, বৃদ্ধির হারে, যাতে মেশিনের গতির ওপর নির্ভর না করে। Big-O দেয় সেই বৃদ্ধির ঊর্ধ্বসীমা, ছোট পদ ও ধ্রুবক বাদ দিয়ে: 3n² + 5n + 2 হলো O(n²).

উদাহরণ: array-র index দিয়ে পড়া O(1); binary search O(log n); linear search O(n); merge sort O(n log n); bubble sort O(n²). n = 10⁶ হলে O(n log n) মোটামুটি দুই কোটি ধাপ, কিন্তু O(n²) এক লক্ষ কোটি ধাপ — পার্থক্য সেকেন্ড বনাম ঘণ্টা। উত্তরে best, average ও worst case আলাদা করে বলুন, যেমন quicksort-এর average O(n log n) কিন্তু worst O(n²).

ঝালিয়ে নিন: পর্ব ৩৯ · কমপ্লেক্সিটি

৬ · অংশ খ — পার্থক্য লিখুন (১৫টি তুলনা)

“পার্থক্য লিখুন” প্রশ্নে টেবিলই সেরা। নিয়ম তিনটি: (১) প্রথম সারিতে দুটির এক লাইনের সংজ্ঞা; (২) প্রতিটি সারিতে একই মাপকাঠিতে তুলনা — মেমরি, গতি, ব্যবহার; (৩) টেবিলের নিচে এক লাইনের একটি ছোট উদাহরণ। অন্তত চার-পাঁচটি সারি রাখুন।

খ-০১ · while বনাম do-while (সহজ)

বিষয়whiledo-while
শর্ত যাচাইলুপে ঢোকার আগে (entry-controlled)প্রতিবার শেষে (exit-controlled)
ন্যূনতম কতবার চলে০ বার — শর্ত শুরুতেই মিথ্যা হলেঅন্তত ১ বার
Syntaxwhile (c) { … }do { … } while (c); — শেষে ; বাধ্যতামূলক
সাধারণ ব্যবহারফাইল পড়া, শর্তসাপেক্ষ পুনরাবৃত্তিমেনু দেখানো, অন্তত একবার ইনপুট নেওয়া

উদাহরণ: int i = 10; while (i < 5) i++; একবারও চলে না, কিন্তু do { i++; } while (i < 5); একবার চলে, i হয় 11. ঝালিয়ে নিন: পর্ব ১২ · লুপ

খ-০২ · break বনাম continue (সহজ)

বিষয়breakcontinue
কাজসবচেয়ে ভেতরের লুপ বা switch থেকে পুরোপুরি বেরিয়ে যায়এই পুনরাবৃত্তির বাকি অংশ বাদ দিয়ে পরের পুনরাবৃত্তিতে যায়
কোথায় চলেলুপ ও switchশুধু লুপ
for লুপে পরে কীলুপের পরের লাইনupdate অংশ (i++), তারপর শর্ত
Nested লুপেশুধু একটি স্তর থেকে বের হয়শুধু নিজের লুপের পরের ধাপে যায়

উদাহরণ: 1 থেকে 10 লুপে if (i == 5) break; ছাপে 1–4; if (i == 5) continue; ছাপে 5 বাদে বাকি সব। ঝালিয়ে নিন: পর্ব ১২ · লুপ

খ-০৩ · Call by value বনাম call by reference (মাঝারি)

বিষয়Call by value“Call by reference” (C-তে পয়েন্টার দিয়ে)
কী পাঠানো হয়মানের একটি কপিভ্যারিয়েবলের ঠিকানার একটি কপি
মূল ভ্যারিয়েবল বদলায়?নাহ্যাঁ — *p দিয়ে
ঘোষণাvoid f(int x)void f(int *x), ডাক f(&a)
বড় struct পাঠালেপুরো struct কপি হয় — ধীরশুধু ঠিকানা কপি — দ্রুত (বদলাতে না চাইলে const)

গুরুত্বপূর্ণ: C-তে আসলে শুধু pass by value আছে। পয়েন্টার পাঠালেও ঠিকানাটি কপি হয়েই যায়; সেই ঠিকানা দিয়ে মূল মান বদলানো যায় বলে একে প্রচলিতভাবে “call by reference” বলা হয়। আসল reference আছে C++-এ (int &x). খাতায় এই কথাটি লিখলে উত্তরটি অন্যদের থেকে আলাদা হয়। ঝালিয়ে নিন: পর্ব ১৩ · ফাংশন, পর্ব ১৮ · পয়েন্টার ২

খ-০৪ · struct বনাম union (সহজ)

বিষয়structunion
মেমরিপ্রতিটি member-এর আলাদা জায়গাসব member একই জায়গা ভাগ করে
মাপসব member-এর যোগফল + paddingসবচেয়ে বড় member (+ alignment-এর padding)
একসাথে কয়টি মানসবগুলোএক সময়ে একটি
ব্যবহাররেকর্ড — ছাত্র, পণ্য, লেনদেনকয়েক রকমের যেকোনো একটি মান; মেমরি বাঁচানো

উদাহরণ (সাধারণ 64-bit Linux): struct { int i; char c; double d; } — 4 + 1 + 3 (padding) + 8 = 16 বাইট; একই member নিয়ে union — 8 বাইট। ঝালিয়ে নিন: পর্ব ২২ · struct ও union

খ-০৫ · malloc বনাম calloc (সহজ)

বিষয়malloccalloc
Argumentএকটি — মোট বাইট: malloc(n * sizeof *p)দুটি — সংখ্যা ও প্রতিটির মাপ: calloc(n, sizeof *p)
শুরুর মানঅনির্ধারিত (garbage)সব বাইট শূন্য
গতিসামান্য দ্রুত হতে পারেশূন্য করার খরচ থাকতে পারে
গুণফল overflowআপনাকেই যাচাই করতে হয়ভালো বাস্তবায়ন (যেমন glibc) overflow হলে NULL দেয়
মিলদুটিই heap থেকে দেয়, ব্যর্থ হলে NULL, শেষে free লাগে

ঝালিয়ে নিন: পর্ব ২০ · ডায়নামিক মেমরি

খ-০৬ · Stack memory বনাম heap memory (মাঝারি)

বিষয়StackHeap
কী থাকেlocal ভ্যারিয়েবল, ফাংশনের parameter, return addressmalloc/calloc/realloc-এর মেমরি
কে ব্যবস্থা করেস্বয়ংক্রিয় — ফাংশন শেষে নিজেই মুক্তপ্রোগ্রামার — free দিয়ে
মাপছোট, সীমিত (Linux-এ main thread-এর জন্য প্রায়ই ৮ MB)বড় — মূলত সিস্টেমের মেমরি পর্যন্ত
গতিখুব দ্রুত — শুধু stack pointer সরেতুলনামূলক ধীর — allocator-কে খুঁজতে হয়
প্রচলিত বাগstack overflow, local-এর ঠিকানা ফেরতmemory leak, double free, use-after-free

ঝালিয়ে নিন: পর্ব ২০ · ডায়নামিক মেমরি

খ-০৭ · Array বনাম linked list (মাঝারি)

বিষয়ArrayLinked list
মেমরিতে বিন্যাসপাশাপাশি লাগানোছড়ানো, পয়েন্টারে জোড়া
i-তম element পড়াO(1)O(n)
মাঝখানে যোগ/বাদO(n) — বাকিগুলো সরাতে হয়O(1) — আগের node জানা থাকলে
আকারনির্দিষ্ট (বা realloc দিয়ে বাড়ানো)চলার সময় ইচ্ছেমতো
বাড়তি মেমরিনেইপ্রতি node-এ একটি (doubly হলে দুটি) পয়েন্টার
Cacheখুব ভালোখারাপ — প্রায়ই cache miss

ঝালিয়ে নিন: পর্ব ১৬ · অ্যারে, পর্ব ৩৩ · লিংকড লিস্ট

খ-০৮ · Macro বনাম function বনাম inline function (মাঝারি)

বিষয়MacroFunctionInline function
কে প্রক্রিয়া করেPreprocessor — লেখা বদলCompiler — আলাদা কোড, ডাকা হয়Compiler — চাইলে ডাকার জায়গায় কোড বসায়
টাইপ যাচাইনেইআছেআছে
Argument মূল্যায়নযতবার লেখা ততবার (double evaluation)একবারএকবার
ডাকার খরচনেইআছে (সামান্য)সাধারণত নেই — তবে compiler-এর সিদ্ধান্ত
Debugকঠিনসহজসহজ

পরামর্শ: header-এ ছোট সাহায্যকারী ফাংশনের জন্য static inline; macro রাখুন constant, conditional compilation আর __FILE__/__LINE__-এর মতো কাজে। ঝালিয়ে নিন: পর্ব ২৭ · প্রিপ্রসেসর ও ম্যাক্রো

খ-০৯ · Local বনাম global বনাম static ভ্যারিয়েবল (মাঝারি)

বিষয়LocalGlobalStatic local
কোথায় দেখা যায়নিজের blockপুরো ফাইল (আর extern দিয়ে অন্য ফাইল)নিজের block
কতক্ষণ বাঁচেblock চলাকালীনপুরো প্রোগ্রামপুরো প্রোগ্রাম
শুরুর মান না দিলেঅনির্ধারিত00
কোথায় থাকে (সাধারণত)stackdata/bss segmentdata/bss segment
বৈশিষ্ট্যপ্রতিবার নতুন করে তৈরিসবাই বদলাতে পারে — বাগের উৎসডাকের মাঝে মান মনে রাখে

উদাহরণ: void f(void) { static int count = 0; count++; } — তিনবার ডাকলে count হয় 3; static বাদ দিলে প্রতিবার 1. ফাইলের বাইরে static লিখলে global-টি শুধু সেই ফাইলে সীমিত হয়। ঝালিয়ে নিন: পর্ব ১৪ · স্কোপ ও স্টোরেজ ক্লাস

খ-১০ · getchar বনাম scanf বনাম fgets (মাঝারি)

বিষয়getchar()scanf()fgets()
কী পড়েএকটি অক্ষরformat অনুযায়ী মানএকটি পুরো লাইন
ফেরত মানint — অক্ষর বা EOFকয়টি মান সফলভাবে পড়া গেলbuffer-এর ঠিকানা, ব্যর্থ হলে NULL
Spacespace-ও একটি অক্ষর%s space-এ থেমে যায়space সহ পুরো লাইন
নিরাপত্তানিরাপদwidth ছাড়া %s buffer overflow ঘটাতে পারেনিরাপদ — মাপ দিতে হয়
ফাঁদchar-এ রাখলে EOF চেনা যায় না'\n' buffer-এ রয়ে যায়শেষে '\n' রেখে দেয়

gets() কোনো মাপ জানে না বলে C11 থেকে ভাষা থেকে সরিয়ে দেওয়া হয়েছে — খাতায় কখনো লিখবেন না। ঝালিয়ে নিন: পর্ব ১০ · ইনপুট-আউটপুট, পর্ব ১৯ · স্ট্রিং

খ-১১ · Text file বনাম binary file (মাঝারি)

বিষয়Text fileBinary file
ভেতরে কীপড়ার মতো অক্ষর, লাইনে ভাগমেমরির হুবহু বাইট
Mode"r", "w""rb", "wb"
প্রচলিত ফাংশনfprintf, fscanf, fgetsfread, fwrite
লাইন-শেষ অনুবাদWindows-এ "\r\n" ↔ '\n'; Linux-এ কোনো পার্থক্য নেইকোনো অনুবাদ নেই
সুবিধামানুষ পড়তে পারে, যেকোনো মেশিনে খোলেছোট ও দ্রুত
অসুবিধাবড়, পার্স করতে সময় লাগেendianness ও padding-এর কারণে অন্য মেশিনে ভুল পড়তে পারে

ঝালিয়ে নিন: পর্ব ২৪ · ফাইল I/O

খ-১২ · ++i বনাম i++ (সহজ)

বিষয়++i (pre-increment)i++ (post-increment)
Expression-এর মানবাড়ার পরের মানবাড়ার আগের মান
উদাহরণ (i = 5)b = ++i; → b = 6, i = 6b = i++; → b = 5, i = 6
একা লাইনেকোনো পার্থক্য নেই — i++; আর ++i; একই কাজ
সতর্কতাi = i++; বা i++ + ++i — একই ভ্যারিয়েবল ক্রমহীনভাবে দুবার বদলানো undefined behavior; এর কোনো “সঠিক আউটপুট” নেই

ঝালিয়ে নিন: পর্ব ০৭ · অপারেটর

খ-১৩ · = বনাম == (সহজ)

বিষয়===
নামassignment operatorequality (relational) operator
কাজডান পাশের মান বাঁ পাশের ভ্যারিয়েবলে রাখেদুটি মান সমান কি না যাচাই করে
Expression-এর মানযে মান রাখা হলোসমান হলে 1, না হলে 0
ভুলে if-এ লিখলেif (x = 5) — সবসময় সত্য, x বদলে যায়সঠিক তুলনা

-Wall দিলে GCC এই ভুলে সতর্ক করে (suggest parentheses around assignment). ঝালিয়ে নিন: পর্ব ০৭ · অপারেটর, পর্ব ১১ · সিদ্ধান্ত

খ-১৪ · #include <file.h> বনাম #include "file.h" (সহজ)

বিষয়< >" "
কোথায় খোঁজেসিস্টেম ও compiler-এর include folder (আর -I দিয়ে দেওয়া folder)প্রথমে যে ফাইল include করছে তার নিজের folder, না পেলে < >-এর জায়গাগুলোতে
ব্যবহারstandard library — stdio.h, stdlib.hনিজের প্রজেক্টের header — "stack.h"
নিয়মের ভিত্তিখোঁজার সঠিক ক্রম standard-এ implementation-defined; ওপরের বর্ণনা GCC ও Clang-এর প্রচলিত আচরণ

ঝালিয়ে নিন: পর্ব ২৭ · প্রিপ্রসেসর, পর্ব ২৯ · মডুলার প্রোগ্রাম

খ-১৫ · Process বনাম thread (কঠিন)

বিষয়ProcessThread
সংজ্ঞাচলমান একটি প্রোগ্রাম, নিজস্ব মেমরিসহএকটি process-এর ভেতরের একটি কাজের ধারা
মেমরিআলাদা address spaceএকই process-এর code, global ও heap ভাগ করে; নিজের stack ও register আলাদা
তৈরিfork() — তুলনামূলক ভারীpthread_create() — হালকা
যোগাযোগIPC — pipe, socket, shared memoryসরাসরি একই মেমরি — তবে mutex-এর মতো synchronization লাগে
একটি crash করলেসাধারণত অন্য process টিকে থাকেপুরো process বন্ধ হয়ে যায়

বাস্তব উদাহরণ: Chrome-এর মতো ব্রাউজার আলাদা ট্যাব বা সাইটকে আলাদা process-এ রাখে, যাতে একটি ক্র্যাশ অন্যগুলোকে না ফেলে; একটি গেম একই process-এ একাধিক thread-এ গ্রাফিক্স, শব্দ আর নেটওয়ার্ক চালায়। ঝালিয়ে নিন: পর্ব ৪৮ · প্রসেস ও IPC, পর্ব ৪৯ · মাল্টিথ্রেডিং

৭ · অংশ গ — প্রোগ্রাম লিখুন (১৬টি ক্লাসিক প্রোগ্রাম)

প্রতিটি সমস্যার জন্য আগে নিজে খাতায় কোড লিখুন, তারপর মডেল সমাধান দেখুন। চালানো যায় এমন প্রোগ্রামে ইনপুট কোডের ভেতরেই দেওয়া আছে, যাতে প্রতিবার একই আউটপুট আসে; পরীক্ষায় প্রশ্ন চাইলে সেখানে scanf বসাবেন। সব কোড একটি সাধারণ 64-bit Linux GCC ধরে লেখা (int 32-bit, long long 64-bit).

গ-০১ · Factorial — iterative ও recursive (সহজ)

সমস্যা: n! বের করুন, দুই পদ্ধতিতে। পদ্ধতি: লুপে 1 থেকে n পর্যন্ত গুণ; recursive সংস্করণে n! = n × (n−1)!, base case 0! = 1! = 1. মান খুব দ্রুত বড় হয়, তাই unsigned long long — এতে সর্বোচ্চ 20! ধরে।

#include <stdio.h>

unsigned long long fact_iter(int n) {
    unsigned long long f = 1;
    for (int i = 2; i <= n; i++)
        f *= i;
    return f;
}

unsigned long long fact_rec(int n) {
    if (n <= 1) return 1;            /* base case */
    return n * fact_rec(n - 1);      /* recursive case */
}

int main(void) {
    int tests[] = {0, 1, 5, 10, 20};
    for (int k = 0; k < 5; k++) {
        int n = tests[k];
        printf("%2d! = %llu (iter) = %llu (rec)\n", n, fact_iter(n), fact_rec(n));
    }
    return 0;
}

দুই পদ্ধতিই একই ফল দেয়: 5! = 120, 10! = 3628800, 20! = 2432902008176640000. খেয়াল করুন 0! = 1 — এটি গণিতের সংজ্ঞা, আর base case-এ n <= 1 লেখায় এটি আপনাআপনি ঠিক হয়। Recursive সংস্করণে প্রতিটি ডাক call stack-এ একটি frame যোগ করে, তাই খুব বড় n-এ loop বেশি নিরাপদ।

সাধারণ ভুল: int fact নেওয়া — 13! থেকেই 32-bit int overflow করে (signed overflow undefined behavior); fact = 0 দিয়ে শুরু করা; base case বাদ দেওয়া; ঋণাত্মক n-এর কথা না বলা।

গ-০২ · Fibonacci ধারা (সহজ)

সমস্যা: Fibonacci ধারার প্রথম n টি পদ ছাপুন (0, 1, 1, 2, …). পদ্ধতি: শুধু আগের দুটি পদ মনে রাখলেই চলে — next = a + b, তারপর সরিয়ে নেওয়া। এটি O(n); সরল recursive fib(n-1) + fib(n-2) একই কাজ বারবার করে, তাই প্রায় O(2ⁿ).

#include <stdio.h>

int main(void) {
    int n = 10;
    long long a = 0, b = 1;

    printf("First %d terms:", n);
    for (int i = 0; i < n; i++) {
        printf(" %lld", a);
        long long next = a + b;
        a = b;
        b = next;
    }
    printf("\n");
    return 0;
}

আউটপুট: First 10 terms: 0 1 1 2 3 5 8 13 21 34. প্রতিটি ধাপে তিনটি ভ্যারিয়েবলের মান খাতায় একটি dry run টেবিলে দেখালে পরীক্ষক সহজেই যুক্তি অনুসরণ করতে পারেন। ভাইভায় প্রায়ই জিজ্ঞেস করা হয় “recursive সংস্করণ কেন ধীর?” — উত্তর: একই পদ বারবার হিসাব হয়; memoization দিলে O(n) হয়।

সাধারণ ভুল: ধারা 1 থেকে শুরু নাকি 0 থেকে — প্রশ্নে কী চাওয়া হয়েছে না দেখা; n = 1 হলে দুটি পদ ছেপে ফেলা; int-এ রাখা — F(47) = 2971215073 থেকে আর 32-bit int-এ ধরে না।

গ-০৩ · Prime সংখ্যা যাচাই — √n পর্যন্ত (সহজ)

সমস্যা: একটি সংখ্যা prime কি না বলুন। পদ্ধতি: n-এর কোনো ভাজক থাকলে তার অন্তত একটি √n-এর সমান বা ছোট। তাই 2 আলাদা দেখে, তারপর 3 থেকে শুধু বিজোড় সংখ্যা দিয়ে i * i <= n পর্যন্ত ভাগ করলেই যথেষ্ট — O(√n).

#include <stdio.h>
#include <stdbool.h>

bool is_prime(int n) {
    if (n < 2) return false;               /* 0, 1, negative */
    if (n % 2 == 0) return n == 2;         /* even numbers */
    for (int i = 3; (long long)i * i <= n; i += 2)
        if (n % i == 0) return false;
    return true;
}

int main(void) {
    int tests[] = {0, 1, 2, 9, 17, 97, 100, 7919};
    for (int k = 0; k < 8; k++)
        printf("%d -> %s\n", tests[k], is_prime(tests[k]) ? "prime" : "not prime");
    return 0;
}

0 আর 1 prime নয়, 2 একমাত্র জোড় prime, 9 = 3 × 3 তাই নয়, 17, 97 ও 7919 prime, 100 নয়। (long long)i * i লেখা হয়েছে যাতে খুব বড় n-এ i * i নিজেই overflow না করে। পরীক্ষায় √n যুক্তিটি এক লাইনে লিখে দিন — এটিই এই প্রশ্নের মূল নম্বর।

সাধারণ ভুল: 1-কে prime বলা; লুপ i < n পর্যন্ত চালানো (ঠিক, কিন্তু অকারণে ধীর); প্রতিবার sqrt(n) ডাকা আর -lm ভুলে যাওয়া; ভাজক পেয়েও লুপ চালিয়ে যাওয়া।

গ-০৪ · GCD ও LCM — Euclid পদ্ধতি (সহজ)

সমস্যা: দুটি ধনাত্মক সংখ্যার GCD (গসাগু) ও LCM (লসাগু) বের করুন। পদ্ধতি: Euclid-এর নিয়ম — gcd(a, b) = gcd(b, a % b), যতক্ষণ না b = 0. তারপর lcm = a / gcd × b — আগে ভাগ, পরে গুণ, যাতে overflow কম হয়।

#include <stdio.h>

int gcd(int a, int b) {
    while (b != 0) {
        int r = a % b;
        a = b;
        b = r;
    }
    return a;
}

long long lcm(int a, int b) {
    return (long long)(a / gcd(a, b)) * b;   /* divide first, then multiply */
}

int main(void) {
    int pairs[][2] = {{12, 18}, {17, 5}, {100, 75}};
    for (int k = 0; k < 3; k++) {
        int a = pairs[k][0], b = pairs[k][1];
        printf("gcd(%d, %d) = %d, lcm = %lld\n", a, b, gcd(a, b), lcm(a, b));
    }
    return 0;
}

ফল: gcd(12, 18) = 6, lcm = 36; gcd(17, 5) = 1, lcm = 85; gcd(100, 75) = 25, lcm = 300. Euclid পদ্ধতি O(log(min(a, b))) ধাপে শেষ হয় — 1 থেকে ছোট সংখ্যা পর্যন্ত সব ভাজক পরীক্ষার চেয়ে অনেক দ্রুত। অংশ ৩-এর dry run টেবিলটি এই প্রশ্নের উত্তরে জুড়ে দিতে পারেন।

সাধারণ ভুল: a * b / gcd লেখা — গুণফল আগেই overflow করতে পারে; 0 ইনপুটের কথা না ভাবা (gcd(a, 0) = a); সূত্র a × b = gcd × lcm লিখে প্রমাণ ছাড়া ব্যবহার।

গ-০৫ · সংখ্যা উল্টানো, palindrome সংখ্যা ও অঙ্কের যোগফল (সহজ)

সমস্যা: একটি ধনাত্মক পূর্ণসংখ্যা উল্টান, এটি palindrome কি না বলুন, আর অঙ্কগুলোর যোগফল বের করুন। পদ্ধতি: n % 10 দেয় শেষ অঙ্ক, n / 10 শেষ অঙ্কটি ফেলে দেয়। উল্টানোর সময় rev = rev * 10 + শেষ অঙ্ক.

#include <stdio.h>

int reverse_num(int n) {
    int rev = 0;
    while (n > 0) {
        rev = rev * 10 + n % 10;
        n /= 10;
    }
    return rev;
}

int digit_sum(int n) {
    int s = 0;
    while (n > 0) {
        s += n % 10;
        n /= 10;
    }
    return s;
}

int main(void) {
    int tests[] = {1234, 12321, 9050};
    for (int k = 0; k < 3; k++) {
        int n = tests[k];
        printf("%d: reverse=%d, digit sum=%d, %s\n", n, reverse_num(n), digit_sum(n),
               reverse_num(n) == n ? "palindrome" : "not palindrome");
    }
    return 0;
}

1234 উল্টে 4321, যোগফল 10; 12321 উল্টেও 12321 — palindrome, যোগফল 9; আর 9050 উল্টে হয় 509, 0509 নয় — কারণ সংখ্যার শুরুর শূন্য হারিয়ে যায়। এই সূক্ষ্ম বিষয়টি খাতায় উল্লেখ করলে উত্তরটি চোখে পড়ে। মূল সংখ্যা পরে লাগলে তার একটি কপি রাখুন — এখানে ফাংশনে কপি যায় বলে main-এর n অক্ষত থাকে।

সাধারণ ভুল: লুপে মূল n নষ্ট করে ফেলে পরে তুলনা করা; ঋণাত্মক সংখ্যার কথা না ভাবা; বড় সংখ্যা উল্টালে int overflow (যেমন 1999999999 উল্টালে 9999999991 — int-এ ধরে না)।

গ-০৬ · Armstrong সংখ্যা (মাঝারি)

সমস্যা: একটি নির্দিষ্ট সীমা পর্যন্ত সব Armstrong সংখ্যা ছাপুন। পদ্ধতি: আগে অঙ্কসংখ্যা d গুনুন, তারপর প্রতিটি অঙ্কের d-তম ঘাত যোগ করে মূল সংখ্যার সাথে মেলান। ঘাতের জন্য pow() নয়, নিজের পূর্ণসংখ্যার ফাংশন — floating point-এর গোলমাল এড়াতে।

#include <stdio.h>

int count_digits(int n) {
    int c = 0;
    do { c++; n /= 10; } while (n > 0);
    return c;
}

int ipow(int base, int e) {
    int r = 1;
    while (e-- > 0) r *= base;
    return r;
}

int is_armstrong(int n) {
    int d = count_digits(n), sum = 0;
    for (int t = n; t > 0; t /= 10)
        sum += ipow(t % 10, d);
    return sum == n;
}

int main(void) {
    printf("Armstrong numbers up to 10000:");
    for (int n = 1; n <= 10000; n++)
        if (is_armstrong(n)) printf(" %d", n);
    printf("\n");
    return 0;
}

আউটপুট: 1 থেকে 9 (এক অঙ্কের সব সংখ্যা নিজেই নিজের প্রথম ঘাত), তারপর 153, 370, 371, 407, 1634, 8208, 9474. যেমন 153 = 1³ + 5³ + 3³ = 1 + 125 + 27. অনেক বই শুধু তিন অঙ্কের সংজ্ঞা দেয় (ঘাত সবসময় 3) — প্রশ্নে কোনটি চাওয়া হয়েছে দেখে নিন, আর উত্তরে নিজের ধরে নেওয়া সংজ্ঞাটি লিখে দিন। count_digits-এ do-while ইচ্ছে করে, যাতে 0-এর অঙ্কসংখ্যা 1 আসে।

সাধারণ ভুল: pow()-এর double ফল int-এ নেওয়ায় কিছু compiler-এ 124.9999 → 124 হয়ে যাওয়া; লুপে মূল সংখ্যা নষ্ট করা; ঘাত সবসময় 3 ধরে চার অঙ্কের সংখ্যা যাচাই করা।

গ-০৭ · Palindrome string (সহজ)

সমস্যা: একটি শব্দ সামনে ও পেছন থেকে একই কি না বলুন। পদ্ধতি: দুই প্রান্তে দুটি index — i শুরুতে, j শেষে; মিললে দুজনই ভেতরে এক ঘর সরে; অমিল পেলেই উত্তর “না”। O(n), বাড়তি মেমরি লাগে না।

#include <stdio.h>
#include <string.h>

int is_pal(const char *s) {
    size_t len = strlen(s);
    if (len == 0) return 1;            /* empty string */
    size_t i = 0, j = len - 1;
    while (i < j) {
        if (s[i] != s[j]) return 0;
        i++;
        j--;
    }
    return 1;
}

int main(void) {
    const char *words[] = {"madam", "level", "dhaka", "a", ""};
    for (int k = 0; k < 5; k++)
        printf("\"%s\" -> %s\n", words[k], is_pal(words[k]) ? "palindrome" : "not palindrome");
    return 0;
}

"madam", "level", "a" আর খালি string palindrome; "dhaka" নয়। খালি string আলাদা দেখা হয়েছে কারণ size_t unsigned — 0 - 1 হলে বিশাল একটি সংখ্যা হয়ে যেত। বড়-ছোট হাতের অক্ষর (Madam) বা space উপেক্ষা করতে চাইলে তুলনার আগে tolower আর isalnum ব্যবহার করুন — উত্তরে এটুকু উল্লেখ করুন।

সাধারণ ভুল: উল্টো কপি বানাতে strrev ব্যবহার — এটি standard C-তে নেই; j = strlen(s) থেকে শুরু করে '\0'-এর সাথে তুলনা; == দিয়ে দুটি string তুলনা।

গ-০৮ · Vowel ও শব্দ গোনা (সহজ)

সমস্যা: একটি লাইনে কতগুলো vowel আর কতগুলো শব্দ আছে বলুন। পদ্ধতি: প্রতিটি অক্ষর ছোট হাতে বদলে a, e, i, o, u কি না দেখুন। শব্দ গোনার জন্য একটি “শব্দের ভেতরে আছি কি না” চিহ্ন রাখুন — space-এর পর প্রথম অক্ষর পেলেই একটি নতুন শব্দ। এতে একাধিক space-ও ঠিকমতো সামলানো যায়।

#include <stdio.h>
#include <ctype.h>

int main(void) {
    const char *line = "  C is a  great language ";
    int vowels = 0, words = 0, in_word = 0;

    for (const char *p = line; *p != '\0'; p++) {
        int c = tolower((unsigned char)*p);
        if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
            vowels++;

        if (isspace((unsigned char)*p)) {
            in_word = 0;
        } else if (!in_word) {
            in_word = 1;
            words++;
        }
    }
    printf("vowels = %d, words = %d\n", vowels, words);
    return 0;
}

আউটপুট: vowels = 8, words = 5. লাইনের শুরুতে, মাঝে আর শেষে ইচ্ছে করে বাড়তি space রাখা হয়েছে — “space গুনে +1” পদ্ধতি এখানে ভুল উত্তর দিত। (unsigned char) cast দরকার, কারণ ctype.h-এর ফাংশনে ঋণাত্মক char (EOF ছাড়া) পাঠানো undefined behavior.

সাধারণ ভুল: শব্দ = space-এর সংখ্যা + 1 ধরা; বড় হাতের A, E… বাদ পড়া; scanf("%s") দিয়ে পুরো লাইন পড়ার চেষ্টা — প্রথম space-এ থেমে যায়, তাই fgets ব্যবহার করুন।

গ-০৯ · Library ছাড়া string-এর দৈর্ঘ্য ও কপি (মাঝারি)

সমস্যা: strlen আর strcpy নিজে লিখুন। পদ্ধতি: দৈর্ঘ্যের জন্য '\0' না পাওয়া পর্যন্ত গুনুন; কপির জন্য প্রতিটি অক্ষর, শেষের '\0' সহ, গন্তব্যে বসান।

#include <stdio.h>
#include <stddef.h>

size_t my_strlen(const char *s) {
    size_t n = 0;
    while (s[n] != '\0')
        n++;
    return n;
}

void my_strcpy(char *dst, const char *src) {
    while ((*dst++ = *src++) != '\0')
        ;                       /* copies the '\0' too */
}

int main(void) {
    const char *src = "Bangladesh";
    char dst[32];               /* must be large enough */
    my_strcpy(dst, src);
    printf("copy = %s, length = %zu\n", dst, my_strlen(dst));
    printf("empty length = %zu\n", my_strlen(""));
    return 0;
}

আউটপুট: copy = Bangladesh, length = 10 আর empty length = 0. while ((*dst++ = *src++) != '\0') একটি ক্লাসিক এক-লাইনের কপি: প্রথমে অক্ষর বসে, তারপর দুটি পয়েন্টার এগোয়, আর বসানো অক্ষরটি '\0' হলে লুপ থামে — ফলে terminator-ও কপি হয়ে যায়। খাতায় পরিষ্কারতার জন্য index দিয়ে লেখা সংস্করণও সমান গ্রহণযোগ্য।

সাধারণ ভুল: শেষের '\0' কপি করতে ভুলে যাওয়া; গন্তব্য buffer ছোট রাখা (buffer overflow); char *dst; লিখে কোনো মেমরি না দিয়েই কপি করা।

গ-১০ · Matrix যোগ (সহজ)

সমস্যা: দুটি একই মাপের matrix যোগ করুন। পদ্ধতি: দুটি nested লুপে c[i][j] = a[i][j] + b[i][j]. যোগের শর্ত: দুটি matrix-এর সারি ও কলাম সমান হতে হবে — উত্তরে শর্তটি লিখুন।

#define ROWS 2
#define COLS 3

void mat_add(int a[ROWS][COLS], int b[ROWS][COLS], int c[ROWS][COLS]) {
    for (int i = 0; i < ROWS; i++)
        for (int j = 0; j < COLS; j++)
            c[i][j] = a[i][j] + b[i][j];
}
/* a = {{1,2,3},{4,5,6}}, b = {{6,5,4},{3,2,1}}  ->  c = {{7,7,7},{7,7,7}} */

সাধারণ ভুল: লুপের সীমায় সারি-কলাম উল্টে ফেলা; ফলাফল matrix-এ আগের garbage রেখে += করা। ঝালিয়ে নিন: পর্ব ২১ · বহুমাত্রিক অ্যারে

গ-১১ · Matrix গুণ ও transpose (মাঝারি)

সমস্যা: A (2×3) আর B (3×2) গুণ করুন, আর A-র transpose বের করুন। পদ্ধতি: গুণের শর্ত — A-র কলাম = B-র সারি; ফল হবে (A-র সারি × B-র কলাম)। p[i][j] = A-র i-তম সারি আর B-র j-তম কলামের গুণফলের যোগ — তিনটি nested লুপ, O(n³). Transpose-এ t[j][i] = a[i][j].

#include <stdio.h>
#define RA 2   /* rows of A */
#define CA 3   /* cols of A = rows of B */
#define CB 2   /* cols of B */

int main(void) {
    int a[RA][CA] = {{1, 2, 3}, {4, 5, 6}};
    int b[CA][CB] = {{7, 8}, {9, 10}, {11, 12}};
    int p[RA][CB] = {{0}};
    int t[CA][RA];

    for (int i = 0; i < RA; i++)
        for (int j = 0; j < CB; j++)
            for (int k = 0; k < CA; k++)
                p[i][j] += a[i][k] * b[k][j];

    for (int i = 0; i < RA; i++)
        for (int j = 0; j < CA; j++)
            t[j][i] = a[i][j];

    printf("A x B:\n");
    for (int i = 0; i < RA; i++) printf("%4d %4d\n", p[i][0], p[i][1]);
    printf("transpose(A):\n");
    for (int j = 0; j < CA; j++) printf("%4d %4d\n", t[j][0], t[j][1]);
    return 0;
}

A × B = [[58, 64], [139, 154]] — যেমন প্রথম ঘর 1×7 + 2×9 + 3×11 = 58. A-র transpose হলো 3×2: [[1, 4], [2, 5], [3, 6]]. p-কে শুরুতে {{0}} দিয়ে শূন্য করা জরুরি, কারণ ভেতরে += হচ্ছে। খাতায় প্রথম ঘরের হিসাবটি হাতে দেখিয়ে দিন — পরীক্ষক বুঝবেন সূত্রটি আপনার পরিষ্কার।

সাধারণ ভুল: গুণের শর্ত যাচাই না করা; p শূন্য না করা; ঘরে ঘরে গুণ (a[i][j] * b[i][j]) করে একে matrix গুণ বলা; transpose-এ একই matrix-এ in-place অদলবদল করতে গিয়ে প্রতিটি জোড়া দুবার অদলবদল করে ফেলা।

গ-১২ · Bubble sort ও binary search (মাঝারি)

সমস্যা: একটি array সাজান, তারপর binary search দিয়ে একটি মান খুঁজুন। পদ্ধতি: Bubble sort-এ পাশাপাশি দুটি উল্টো ক্রমে থাকলে অদলবদল; প্রতিটি পাসে সবচেয়ে বড়টি শেষে চলে যায়। কোনো পাসে অদলবদল না হলে আগেই থামা যায়। Binary search শুধু সাজানো array-তে চলে — প্রতিবার মাঝের মান দেখে অর্ধেক বাদ।

#include <stdio.h>

void bubble_sort(int a[], int n) {
    for (int pass = 0; pass < n - 1; pass++) {
        int swapped = 0;
        for (int j = 0; j < n - 1 - pass; j++)
            if (a[j] > a[j + 1]) {
                int tmp = a[j]; a[j] = a[j + 1]; a[j + 1] = tmp;
                swapped = 1;
            }
        if (!swapped) break;              /* already sorted */
    }
}

int binary_search(const int a[], int n, int key) {
    int lo = 0, hi = n - 1;
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;     /* avoids overflow of lo + hi */
        if (a[mid] == key) return mid;
        if (a[mid] < key) lo = mid + 1; else hi = mid - 1;
    }
    return -1;
}
int main(void) {
    int a[] = {29, 10, 14, 37, 13, 5};
    int n = (int)(sizeof a / sizeof a[0]); bubble_sort(a, n);
    for (int i = 0; i < n; i++) printf("%d ", a[i]);
    printf("\nfind 14 -> %d, find 99 -> %d\n", binary_search(a, n, 14), binary_search(a, n, 99));
    return 0;
}

সাজানোর পর: 5 10 13 14 29 37; 14 আছে index 3-এ, আর 99 নেই বলে -1. Bubble sort worst ও average O(n²), কিন্তু swapped চিহ্নের কারণে আগেই সাজানো array-তে best case O(n). Binary search O(log n) — ১০ লাখ উপাদানে বড়জোর ২০টির মতো তুলনা। mid = lo + (hi - lo) / 2 লেখাটি ভাইভায় প্রায়ই জিজ্ঞেস করা হয়: (lo + hi) / 2 খুব বড় index-এ overflow করতে পারে।

সাধারণ ভুল: ভেতরের লুপে j < n লেখা — a[j + 1] array-র বাইরে যায়; অসাজানো array-তে binary search; while (lo < hi) লিখে এক উপাদানের ক্ষেত্র বাদ পড়া।

গ-১৩ · Linear search (সহজ)

সমস্যা: একটি array-তে একটি মান কোথায় আছে বলুন। পদ্ধতি: শুরু থেকে একে একে মেলান; পেলে index, না পেলে -1. Linear search অসাজানো ডেটাতেও চলে, worst case O(n).

int linear_search(const int a[], int n, int key) {
    for (int i = 0; i < n; i++)
        if (a[i] == key)
            return i;        /* first match */
    return -1;               /* not found */
}

সাধারণ ভুল: লুপের ভেতরে else return -1; লেখা — প্রথম উপাদান না মিললেই ফাংশন শেষ। ঝালিয়ে নিন: পর্ব ৪১ · সার্চিং, পর্ব ৪০ · সর্টিং

গ-১৪ · পয়েন্টার দিয়ে দুটি সংখ্যা অদলবদল (সহজ)

সমস্যা: একটি ফাংশন লিখুন, যা main-এর দুটি ভ্যারিয়েবলের মান অদলবদল করে। পদ্ধতি: মান পাঠালে শুধু কপি বদলায়, তাই ঠিকানা পাঠান এবং * দিয়ে মূল ঘরে লিখুন।

void swap(int *x, int *y) {
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
/* main-এ:  int a = 3, b = 7;  swap(&a, &b);  ->  a = 7, b = 3 */

সাধারণ ভুল: void swap(int x, int y) — কপি বদলায়, main-এ কিছুই হয় না; ডাকার সময় & ভুলে যাওয়া; int *tmp নিয়ে ঠিকানা অদলবদল করা। XOR দিয়ে tmp ছাড়া অদলবদল দেখাতে চাইলে সতর্ক থাকুন — একই ঠিকানা দুবার পাঠালে (swap(&a, &a)) মানটি শূন্য হয়ে যায়।

গ-১৫ · struct দিয়ে ছাত্রের রেকর্ড ও গড় (মাঝারি)

সমস্যা: কয়েকজন ছাত্রের roll, নাম ও নম্বর রাখুন; গড় নম্বর, সর্বোচ্চ নম্বরধারী আর গড়ের ওপরের ছাত্রদের নাম ছাপুন। পদ্ধতি: একটি struct student-এর array; একবার লুপে যোগফল ও সর্বোচ্চের index, দ্বিতীয় লুপে গড়ের সাথে তুলনা।

#include <stdio.h>

struct student {
    int roll;
    char name[20];
    double marks;
};

int main(void) {
    struct student s[] = {
        {1, "Rahim", 78.5},
        {2, "Karima", 91.0},
        {3, "Tanvir", 64.5},
    };
    int n = (int)(sizeof s / sizeof s[0]);
    double sum = 0.0;
    int top = 0;

    for (int i = 0; i < n; i++) {
        sum += s[i].marks;
        if (s[i].marks > s[top].marks) top = i;
    }
    double avg = sum / n;
    printf("Average = %.2f\n", avg);
    printf("Top: roll %d, %s (%.1f)\n", s[top].roll, s[top].name, s[top].marks);
    for (int i = 0; i < n; i++)
        if (s[i].marks > avg) printf("Above average: %s\n", s[i].name);
    return 0;
}

মোট 234.0, গড় 78.00; সর্বোচ্চ roll 2, Karima (91.0); গড়ের ওপরে Rahim (78.5) ও Karima. গড়ের জন্য sum রাখা হয়েছে double-এ, তাই sum / n দশমিকসহ ভাগ হয় — দুটোই int হলে দশমিক অংশ হারিয়ে যেত। ছাত্র সংখ্যা 0 হলে শূন্য দিয়ে ভাগ এড়াতে একটি যাচাই রাখার কথা উত্তরে লিখুন।

সাধারণ ভুল: s[i].name = "Rahim"; লেখা — array-তে assignment চলে না, strcpy বা শুরুর মান লাগে; int ভাগে গড় হারানো; scanf("%s", &s[i].name) — name নিজেই ঠিকানা, & লাগে না। ঝালিয়ে নিন: পর্ব ২২ · struct ও union

গ-১৬ · ফাইল কপি ও লাইন গোনা (মাঝারি)

সমস্যা: input.txt-এর সব লেখা copy.txt-এ কপি করুন এবং লাইনের সংখ্যা বলুন। পদ্ধতি: দুটি ফাইল খুলুন, NULL যাচাই করুন, fgetc দিয়ে অক্ষর ধরে পড়ে fputc-এ লিখুন, '\n' পেলে গুনুন, শেষে দুটিই বন্ধ করুন। এটি এই পাতায় চালানো যায় না, কারণ ফাইলটি আপনার কম্পিউটারে থাকতে হবে।

#include <stdio.h>

int main(void) {
    FILE *in = fopen("input.txt", "r");
    if (in == NULL) { perror("input.txt"); return 1; }
    FILE *out = fopen("copy.txt", "w");
    if (out == NULL) { perror("copy.txt"); fclose(in); return 1; }

    int ch, lines = 0, last = '\n';     /* int, not char: must hold EOF */
    while ((ch = fgetc(in)) != EOF) {
        fputc(ch, out);
        if (ch == '\n') lines++;
        last = ch;
    }
    if (last != '\n') lines++;          /* last line without newline */

    fclose(in);
    fclose(out);
    printf("copied, lines = %d\n", lines);
    return 0;
}

সাধারণ ভুল: char ch নেওয়া — EOF (সাধারণত -1) আর একটি বৈধ বাইটকে আলাদা করা যায় না; fopen-এর NULL যাচাই না করা; while (!feof(in)) লুপ — ফাইল শেষ হওয়ার পরও লুপ একবার বাড়তি চলে, তাই EOF-কেও একটি অক্ষর হিসেবে লেখা হয়; শেষ লাইনে '\n' না থাকলে সেটি গোনা বাদ পড়া; fclose ভুলে যাওয়া। ঝালিয়ে নিন: পর্ব ২৪ · ফাইল I/O, পর্ব ৩০ · এরর হ্যান্ডলিং

৮ · অংশ ঘ — ভাইভা কুইকফায়ার (৩২টি প্রশ্ন)

ভাইভায় প্রশ্ন আসে দ্রুত, উত্তরও হতে হয় এক–দুই বাক্যে। এখানে পুরো কোর্স থেকে প্রশ্ন আছে, শেষের দিকে আধুনিক বিষয়ও — sanitizer, testing, modern C, thread, embedded — কারণ ইন্টারভিউতে এগুলো এখন প্রায়ই আসে। জোরে জোরে উত্তর বলুন, তারপর মেলান। সময় ধরুন: প্রতিটিতে ২০ সেকেন্ড।

ভিত্তি ও ভাষার মূল যন্ত্রপাতি (পর্ব ০১–১৫)

ঘ-০১main() কী ফেরত দেয়, আর কেন? (সহজ)

int — প্রোগ্রামের exit status, যা OS বা shell পায়। 0 মানে সফল, অন্য মান মানে কোনো সমস্যা। C99 থেকে main-এর শেষে return না লিখলে 0 ধরে নেওয়া হয়। পর্ব ০৪

ঘ-০২sizeof কি একটি ফাংশন? (সহজ)

না, এটি একটি operator. সাধারণত compile-এর সময়ই মান ঠিক হয় (ব্যতিক্রম VLA), ফল size_t টাইপের; ছাপতে %zu. ভেরিয়েবলের ক্ষেত্রে বন্ধনী ছাড়াও লেখা যায়: sizeof x. পর্ব ০৬

ঘ-০৩int কত বাইট? char signed নাকি unsigned? (মাঝারি)

Standard শুধু বলে int অন্তত 16 bit; আজকের সাধারণ 32/64-bit Linux ও Windows-এ 4 বাইট। char signed না unsigned — implementation-defined: x86 Linux-এর GCC-তে signed, ARM Linux-এ সাধারণত unsigned. তাই ভাইভায় বলুন “প্ল্যাটফর্মের ওপর নির্ভর করে”। পর্ব ০৫

ঘ-০৪5 / 2 আর 5 / 2.0-এর মান কত? (সহজ)

5 / 2 = 2 — দুটোই int, তাই integer division, দশমিক অংশ কেটে যায়। 5 / 2.0 = 2.5 — একটি double থাকায় অন্যটিও double-এ রূপান্তরিত হয় (usual arithmetic conversions). পর্ব ০৮

ঘ-০৫n & 1 দিয়ে কী বোঝা যায়? (সহজ)

n-এর সবচেয়ে ডানের bit. ফল 1 হলে n বিজোড়, 0 হলে জোড় (অঋণাত্মক সংখ্যার জন্য নিশ্চিন্তে; আধুনিক two's complement সিস্টেমে ঋণাত্মকেও ঠিক কাজ করে)। পর্ব ০৯

ঘ-০৬switch-এ break না দিলে কী হয়? (সহজ)

Fall-through — মিলে যাওয়া case-এর পরের সব case-এর কোডও চলতে থাকে, যতক্ষণ না break বা switch-এর শেষ আসে। কখনো ইচ্ছে করে করা হয়; তখন মন্তব্যে লিখে দেওয়া ভালো। পর্ব ১১

ঘ-০৭Global আর local ভ্যারিয়েবলের শুরুর মান না দিলে কী থাকে? (সহজ)

Global ও static ভ্যারিয়েবল আপনাআপনি 0 হয়। Local (automatic) ভ্যারিয়েবলের মান অনির্ধারিত — garbage; শুরুর মান না দিয়ে পড়লে ফল অনির্ভরযোগ্য। পর্ব ১৪

ঘ-০৮Recursion নাকি loop — কোনটি ভালো? (মাঝারি)

সমস্যার ওপর নির্ভর করে। Tree, divide and conquer, backtracking-এ recursion স্বাভাবিক ও পরিষ্কার; সরল পুনরাবৃত্তিতে loop দ্রুত ও stack-নিরাপদ। খুব গভীর recursion-এ stack overflow-এর ঝুঁকি। পর্ব ১৫

মেমরি ও পয়েন্টার (পর্ব ১৬–২৩)

ঘ-০৯void * কী? (মাঝারি)

Generic pointer — যেকোনো object-এর ঠিকানা রাখতে পারে, কিন্তু টাইপ জানে না বলে সরাসরি dereference বা pointer arithmetic করা যায় না (standard C-তে)। malloc এটিই ফেরত দেয়; C-তে int *p = malloc(...) লিখতে cast লাগে না। পর্ব ১৭

ঘ-১০int *p-তে 1 যোগ করলে ঠিকানা কত বাড়ে? (সহজ)

sizeof(int) বাইট — সাধারণত 4. Pointer arithmetic সবসময় নির্দেশিত টাইপের মাপের এককে চলে; double * হলে 8 বাইট, char * হলে 1. পর্ব ১৮

ঘ-১১char *s = "hi"; s[0] = 'H'; — কী হবে? (মাঝারি)

Undefined behavior — string literal বদলানো নিষেধ। Linux-এ literal সাধারণত read-only মেমরিতে থাকে, তাই প্রায়ই segmentation fault. বদলাতে চাইলে char s[] = "hi"; — এটি একটি নিজস্ব array কপি। পর্ব ১৯

ঘ-১২free করার পর পয়েন্টার NULL করা কেন? free(NULL) কি নিরাপদ? (সহজ)

যাতে ভুলে আবার ব্যবহার (use-after-free) বা দ্বিতীয়বার free (double free) না হয় — NULL হলে ভুলটা সাথে সাথে ধরা পড়ে। হ্যাঁ, free(NULL) standard অনুযায়ী কিছুই করে না। পর্ব ২০

ঘ-১৩2D array মেমরিতে কীভাবে থাকে? (মাঝারি)

C-তে row-major — প্রথম সারির সব উপাদান, তারপর দ্বিতীয় সারি, এভাবে এক টানা। int a[R][C]-এ a[i][j]-এর অবস্থান শুরু থেকে (i * C + j) উপাদান দূরে। তাই সারি ধরে লুপ চালানো cache-এ দ্রুত। পর্ব ২১

ঘ-১৪typedef আর #define দিয়ে টাইপের নতুন নাম দেওয়ার পার্থক্য? (কঠিন)

typedef compiler-এর কাছে আসল টাইপের নাম; #define শুধু লেখা বদল। #define PTR int * দিয়ে PTR a, b; হয় int *a, b; — b সাধারণ int! typedef int *PTR; হলে দুটিই পয়েন্টার। পর্ব ২২

বড় প্রোগ্রাম সাজানো (পর্ব ২৪–৩২)

ঘ-১৫argc আর argv কী? (সহজ)

int main(int argc, char *argv[]) — argc হলো কমান্ড-লাইনের শব্দের সংখ্যা, argv সেগুলোর string-এর array. argv[0] সাধারণত প্রোগ্রামের নাম, আর argv[argc] সবসময় NULL. পর্ব ২৫

ঘ-১৬Function pointer-এর একটি বাস্তব ব্যবহার বলুন। (মাঝারি)

qsort-কে তুলনার নিয়ম জানানো — comparator ফাংশনের ঠিকানা পাঠানো হয়। এছাড়া callback, মেনুর জন্য dispatch table, আর event handler. পর্ব ২৮

ঘ-১৭errno আর perror কী কাজে লাগে? (মাঝারি)

অনেক library ফাংশন ব্যর্থ হলে errno-তে কারণের কোড রাখে; perror("msg") সেটি পড়ে মানুষের বোধগম্য বার্তা ছাপে, যেমন input.txt: No such file or directory. errno দেখতে হয় ব্যর্থতা নিশ্চিত হওয়ার পরই। পর্ব ৩০

ঘ-১৮প্রোগ্রাম segmentation fault দিচ্ছে — প্রথমে কী করবেন? (মাঝারি)

-g দিয়ে compile করে gdb-তে চালান; crash-এর পর bt (backtrace) দেখায় কোন ফাংশনের কোন লাইনে থেমেছে। সাথে -fsanitize=address দিয়ে চালালে ভুল পড়া-লেখার সঠিক জায়গা আর কারণ জানা যায়। পর্ব ৩১

ঘ-১৯AddressSanitizer আর Valgrind-এর পার্থক্য কী? (কঠিন)

দুটিই মেমরির ভুল ধরে। AddressSanitizer compile-এর সময় কোডে পাহারা বসায় (-fsanitize=address), তাই নতুন করে build লাগে, কিন্তু তুলনামূলক দ্রুত (মোটামুটি ২ গুণের মতো ধীর)। Valgrind আগে থেকে তৈরি প্রোগ্রামকে নিজের ভেতরে চালায় — build বদলাতে হয় না, কিন্তু অনেক গুণ ধীর। পর্ব ৩১

ঘ-২০Unit test কী? C-তে সবচেয়ে সহজ উপায়? (সহজ)

Unit test হলো একটি ছোট অংশ — সাধারণত একটি ফাংশন — জানা ইনপুটে প্রত্যাশিত আউটপুট দেয় কি না স্বয়ংক্রিয়ভাবে যাচাই। সহজতম রূপ assert(gcd(12, 18) == 6);; edge case (0, ঋণাত্মক, খালি) অবশ্যই রাখুন। বড় প্রজেক্টে Unity বা CMocka-র মতো framework. পর্ব ৩২

ডেটা স্ট্রাকচার ও অ্যালগরিদম (পর্ব ৩৩–৪৪)

ঘ-২১BST-তে search-এর complexity কত? (মাঝারি)

ভারসাম্যপূর্ণ হলে O(log n). কিন্তু সাজানো ক্রমে ঢোকালে tree একটি লম্বা শিকল হয়ে যায় — worst case O(n). AVL বা red-black tree-র মতো self-balancing tree উচ্চতা O(log n)-এ রাখে। পর্ব ৩৫

ঘ-২২Priority queue সাধারণত কী দিয়ে বানানো হয়? (মাঝারি)

Binary heap দিয়ে, যা একটি array-তে রাখা যায়: index i-এর সন্তান 2i+1 ও 2i+2. সবচেয়ে বড় (বা ছোট) দেখা O(1), ঢোকানো ও বের করা O(log n). পর্ব ৩৬

ঘ-২৩Hash table-এ খোঁজার complexity কত? (মাঝারি)

গড়ে O(1), কিন্তু অনেক collision হলে worst case O(n). ভালো hash function আর load factor বাড়লে rehash করা — এই দুটিই গড় দ্রুত রাখে। পর্ব ৩৭

ঘ-২৪BFS আর DFS-এর মূল পার্থক্য? (মাঝারি)

BFS queue দিয়ে স্তর ধরে এগোয় — unweighted graph-এ সবচেয়ে কম ধাপের পথ দেয়। DFS stack বা recursion দিয়ে এক পথে যতদূর যাওয়া যায় গিয়ে ফেরে — cycle খোঁজা, topological sort-এ কাজে লাগে। দুটিই O(V + E). পর্ব ৩৮

ঘ-২৫Quicksort-এর worst case কখন হয়? Stable sort মানে কী? (কঠিন)

Pivot বারবার সবচেয়ে ছোট বা বড় উপাদান হলে — যেমন সাজানো array-তে সবসময় প্রথম উপাদানকে pivot নিলে — O(n²). Stable sort সমান মানের উপাদানের আগের আপেক্ষিক ক্রম রাখে; merge sort stable, সাধারণ quicksort নয়। পর্ব ৪০

ঘ-২৬Dynamic programming কখন কাজে লাগে? (কঠিন)

যখন সমস্যায় একই ছোট সমস্যা বারবার আসে (overlapping subproblems) আর ছোট সমস্যার সেরা উত্তর থেকে বড়টির সেরা উত্তর বানানো যায় (optimal substructure). একবার হিসাব করে মনে রাখা হয় — memoization বা tabulation. পর্ব ৪২

সিস্টেম প্রোগ্রামিং ও আধুনিক বিষয় (পর্ব ৪৫–৫২)

ঘ-২৭volatile কেন লাগে? এটি কি thread-এর জন্য যথেষ্ট? (কঠিন)

volatile compiler-কে বলে মানটি প্রোগ্রামের বাইরে থেকে বদলাতে পারে — hardware register, interrupt handler — তাই প্রতিবার মেমরি থেকে নতুন করে পড়তে হবে, optimize করে বাদ দেওয়া যাবে না। Thread synchronization-এর জন্য যথেষ্ট নয়; সেখানে mutex বা _Atomic লাগে। পর্ব ৪৫, পর্ব ৫১

ঘ-২৮C99, C11 আর C23-এ একটি করে নতুন জিনিস বলুন। (মাঝারি)

C99: // মন্তব্য, for (int i = 0; …), stdint.h, stdbool.h. C11: _Generic, _Static_assert, threads.h (ঐচ্ছিক)। C23: bool/true/false keyword, nullptr, typeof, binary literal 0b1010. পর্ব ৪৭

ঘ-২৯fork() কী ফেরত দেয়? (মাঝারি)

একবার ডাকলে দুবার ফেরে: child process-এ 0, parent-এ child-এর PID, আর ব্যর্থ হলে parent-এ -1 (কোনো child তৈরি হয় না)। এই মান দেখেই কোড ঠিক করে সে child না parent. পর্ব ৪৮

ঘ-৩০Data race কী? কীভাবে ঠেকাবেন ও ধরবেন? (কঠিন)

দুটি thread একই মেমরিতে একসাথে কাজ করছে, অন্তত একটি লিখছে, আর কোনো synchronization নেই — C-তে এটি undefined behavior. ঠেকাতে mutex বা atomic operation; ধরতে -fsanitize=thread (ThreadSanitizer). পর্ব ৪৯

ঘ-৩১TCP আর UDP এক লাইনে? (সহজ)

TCP connection বানিয়ে নির্ভরযোগ্য, ক্রম ঠিক রাখা byte stream দেয় (ওয়েব, ইমেইল); UDP connection ছাড়া আলাদা আলাদা বার্তা পাঠায়, পৌঁছানোর নিশ্চয়তা নেই কিন্তু দ্রুত (ভিডিও কল, গেম, DNS). পর্ব ৫০

ঘ-৩২এমবেডেড সিস্টেমে malloc কেন প্রায়ই এড়ানো হয়? (কঠিন)

RAM খুব সীমিত, দীর্ঘদিন চললে fragmentation-এ মেমরি টুকরো হয়ে যায়, আর allocation-এ কত সময় লাগবে আগে থেকে বলা যায় না — real-time কাজে যা বিপজ্জনক। তাই static বা আগে থেকে ঠিক করা buffer ব্যবহার হয়; MISRA C-এর মতো নিরাপত্তা-নির্দেশিকাও dynamic memory সীমিত করে। পর্ব ৫১

৯ · AI-কে ভাইভা পরীক্ষক বানান — আর তাকে যাচাই করুন

ভাইভার অনুশীলনে সবচেয়ে বড় সমস্যা হলো প্রশ্ন করার মানুষ না থাকা। AI এখানে খুব কাজের সঙ্গী — ক্লান্ত হয় না, যেকোনো সময় প্রস্তুত। কিন্তু AI নিজে থেকে প্রায়ই খুব নরম পরীক্ষক হয় — সব উত্তরেই “চমৎকার!” বলে। তাই তাকে স্পষ্ট নিয়ম দিতে হয়।

নিচের prompt-টি কপি করে যেকোনো AI চ্যাটে দিন। বিষয়ের জায়গায় যে পর্ব ঝালাতে চান সেটি লিখুন:

তুমি একজন কড়া C প্রোগ্রামিং ভাইভা পরীক্ষক। বিষয়: [পয়েন্টার ও dynamic memory]. নিয়ম: ১. একবারে একটিই প্রশ্ন করো, তারপর আমার উত্তরের জন্য অপেক্ষা করো। ২. আমার উত্তর শুনে সেখান থেকেই আরও গভীর একটি follow-up প্রশ্ন করো — যেমন সত্যিকারের ভাইভা বোর্ড করে। ৩. প্রতিটি উত্তরকে ১০-এর মধ্যে নম্বর দাও, আর ঠিক কোন তথ্য ভুল বা বাদ পড়েছে বলো। প্রশংসা নয়, সঠিকতা। ৪. আমি ভুল বললে সাথে সাথে সঠিক উত্তর দিও না — আগে একটা ইঙ্গিত দাও, আমাকে আরেকবার চেষ্টা করতে দাও। ৫. উত্তর প্ল্যাটফর্মের ওপর নির্ভর করলে (যেমন sizeof) আমি অনুমানটা বলছি কি না দেখো। ৬. ৮টি প্রশ্নের পর আমার দুর্বল জায়গাগুলোর তালিকা দাও, আর কোন ধারণা আবার পড়া উচিত বলো। ৭. যে তথ্য নিয়ে তুমি নিশ্চিত নও, সেটা স্পষ্ট করে বলো — অনুমানকে তথ্য হিসেবে দিও না।

লিখিত উত্তরের জন্য AI-কে আপনার খাতার উত্তর টাইপ করে দিন এবং বলুন: “এটি ১০ নম্বরের প্রশ্ন। পরীক্ষক হিসেবে নম্বর দাও, আর পূর্ণ নম্বর পেতে কোন অংশ যোগ করতে হবে বলো — কিন্তু পুরো উত্তর নতুন করে লিখে দিও না।” শেষ শর্তটি জরুরি — নইলে আপনি শিখবেন না, শুধু আরেকটি উত্তর মুখস্থ করবেন।

AI-এর মডেল উত্তরে যেসব ভুল প্রায়ই থাকে

AI যা বলে বা লেখেআসল সত্য
“C-তে call by reference আছে”C-তে শুধু pass by value; পয়েন্টারের মানও কপি হয় (খ-০৩)
“int সবসময় 4 বাইট”Standard শুধু ন্যূনতম 16 bit বলে; 4 বাইট সাধারণ, নিশ্চিত নয়
i++ + ++i-এর একটি নির্দিষ্ট আউটপুট দেয়Undefined behavior — কোনো সঠিক আউটপুট নেই
void main(), conio.h, getch()Standard C নয়; int main(void) লিখুন
gets() বা fflush(stdin) ব্যবহারgets C11-এ বাদ; fflush(stdin)-এর আচরণ standard-এ undefined
“array আর পয়েন্টার একই জিনিস”নামটি decay হয়, কিন্তু sizeof ও &-এ পার্থক্য স্পষ্ট (ক-০৯)

AI-এর উত্তর যাচাইয়ের চেকলিস্ট

নিজেকে যাচাই করুন — উত্তর লেখার দক্ষতা

এবারের প্রশ্নগুলো C-এর তথ্য নয়, উত্তর লেখা ও ভাইভা সামলানোর কৌশল নিয়ে। ভুল হলে কারণসহ ব্যাখ্যা পাবেন।

ক্রম“ব্যাখ্যা করুন” প্রশ্নের একটি ভালো লিখিত উত্তরের অংশগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. 📌 এক বাক্যের নির্ভুল সংজ্ঞা
  2. 🧩 ব্যাখ্যা — কীভাবে কাজ করে
  3. 🗺️ চিত্র বা মেমরি ডায়াগ্রাম
  4. 💻 ছোট কোড ও তার আউটপুট
  5. ⚠️ ব্যবহার, সতর্কতা ও complexity

“পয়েন্টার কী? উদাহরণসহ ব্যাখ্যা করুন” — ১০ নম্বরের প্রশ্নে কোন উত্তরটি পূর্ণ নম্বরের সবচেয়ে কাছে?

  • এক লাইনের সঠিক সংজ্ঞা
  • দুই পাতা — পয়েন্টারের ইতিহাস, কোন ভাষায় আছে, কোনটায় নেই
  • সংজ্ঞা, & ও *-এর ব্যাখ্যা, মেমরি ডায়াগ্রাম, ৪ লাইনের কোড ও আউটপুট, আর NULL/dangling-এর সতর্কতা
  • শুধু একটি ৩০ লাইনের linked list প্রোগ্রাম

ভাইভায় আপনাকে এমন প্রশ্ন করা হলো, যার উত্তর আপনি পুরোপুরি জানেন না। সবচেয়ে ভালো পদক্ষেপ কোনটি?

  • আত্মবিশ্বাস দেখাতে আন্দাজে একটি উত্তর জোর দিয়ে বলা
  • “পুরো নিশ্চিত নই, তবে এভাবে ভাবছি…” বলে যা জানেন তা থেকে যুক্তি দিয়ে এগোনো
  • চুপ করে থাকা, যতক্ষণ না পরের প্রশ্ন আসে
  • প্রশ্ন এড়িয়ে নিজের জানা অন্য বিষয়ে কথা বলা শুরু করা

“i++ + ++i-এর আউটপুট কী?” — ইন্টারভিউতে এই প্রশ্নের সেরা উত্তর কোনটি?

  • “i = 5 হলে উত্তর 12”
  • “এটি compile-ই হবে না”
  • “ডান থেকে বাঁয়ে হিসাব হয়, তাই আগে ++i”
  • “একই ভ্যারিয়েবল ক্রমহীনভাবে দুবার বদলানো হচ্ছে — এটি undefined behavior; আলাদা লাইনে লিখলে ফল নিশ্চিত হয়”

খাতায় prime যাচাইয়ের প্রোগ্রাম লিখছেন। কোন সংযোজন পরীক্ষকের কাছে সবচেয়ে বেশি মূল্যবান?

  • n < 2 আলাদা দেখা, আর “√n পর্যন্ত দেখাই যথেষ্ট” — এক লাইনের মন্তব্য
  • চার রঙের কলমে কোড লেখা
  • #include <conio.h> আর শেষে getch();
  • একই প্রোগ্রামে বাড়তি একটি মেনু, যেখানে ১০টি গাণিতিক কাজ বেছে নেওয়া যায়

১৮০ মিনিটের পরীক্ষায় মোট ৬০ নম্বর। একটি ১০ নম্বরের প্রশ্নে আপনি সর্বোচ্চ কত সময় দেবেন?

  • ১০ মিনিট
  • ৬০ মিনিট — যতক্ষণ না নিখুঁত হয়
  • মোটামুটি ৩০ মিনিট
  • সময়ের হিসাব না করে যতটা লাগে

“struct ও union-এর পার্থক্য লিখুন” — কোন উপস্থাপনা সবচেয়ে কার্যকর?

  • একটি লম্বা অনুচ্ছেদ
  • মেমরি, মাপ, একসাথে কয়টি মান, ব্যবহার — এমন সারির একটি টেবিল, নিচে একটি sizeof উদাহরণ
  • শুধু দুটি কোড উদাহরণ, কোনো লেখা ছাড়া
  • এক লাইন: “union মেমরি বাঁচায়”

ভাইভায় প্রশ্ন এলো: “sizeof একটি পয়েন্টারের কত?” — সবচেয়ে পরিণত উত্তর কোনটি?

  • “সবসময় 8 বাইট”
  • “যে টাইপের পয়েন্টার, তার মাপের সমান — int * হলে 4”
  • “এটি বলা সম্ভব নয়”
  • “প্ল্যাটফর্মের ওপর নির্ভর করে — সাধারণ 64-bit সিস্টেমে 8 বাইট, 32-bit-এ 4; এটি ঠিকানার মাপ, নির্দেশিত টাইপের নয়”

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বে ব্যবহৃত সব শব্দ এখানে একসাথে:

মনে রাখুন

  1. “ব্যাখ্যা করুন” প্রশ্নে পাঁচ অংশ: সংজ্ঞা → ব্যাখ্যা → চিত্র → কোড ও আউটপুট → সতর্কতা.
  2. “পার্থক্য লিখুন” প্রশ্নে টেবিল — একই মাপকাঠিতে সারি, নিচে একটি ছোট উদাহরণ।
  3. হাতে কোডে পূর্ণ প্রোগ্রাম, শুরুর মান, edge case আর নমুনা আউটপুট; সাথে একটি dry run টেবিল।
  4. ভাইভায় ছোট করে শুরু, অনুমান স্পষ্ট, না জানলে সৎভাবে ভেবে বলা — UB-কে UB বলাও একটি সঠিক উত্তর।
  5. AI দিয়ে অনুশীলন করুন, কিন্তু তার প্রতিটি কোড চালিয়ে আর প্রতিটি দাবি মিলিয়ে নিন।

পরের পর্বে: লিখিত আর মৌখিক প্রশ্নের পর শেষ ধাপ — কোডিং ইন্টারভিউ। একটি অচেনা সমস্যা থেকে কীভাবে প্রশ্ন করে বুঝবেন, brute force থেকে ভালো সমাধানে যাবেন, আর ইন্টারভিউয়ারের সামনে জোরে ভেবে কোড লিখবেন।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান