Module ১ · C-র সঙ্গে পরিচয়: প্রথম program, চিহ্ন ধরে ধরে
Comment, whitespace আর মানুষের পড়ার মতো code
এই lesson-এ যা শিখবে
- দুই রকম comment লিখতে পারবে, আর কোনটা কোথায় খাটে বলতে পারবে।
- Comment কী কাজে, আর কোন কাজে ওটা কখনোই না, সেটা বোঝাতে পারবে।
- একটাই indentation style আর অচেনা পাঠকের পড়ার মতো নাম দিয়ে program সাজাতে পারবে।
Amara আর Kenji একই tile হিসাবের program জমা দিল। দুইটাই warning ছাড়া compile হয়। দুইটাই ঠিক উত্তর ছাপে।
Amara-রটা পড়তে দশ সেকেন্ড লাগে। Kenji-রটা পড়তে দশ মিনিট, আর সে ওটা লিখেছে গতকাল। এই lesson program কী করে সেটা বদলায় না; পুরোটাই বদলায় পরের মানুষটার বুঝতে কত সময় লাগবে সেটা।
Compiler তোমার জীবনের একমাত্র পাঠক, যে তোমার style নিয়ে কখনো নালিশ করে না। বাকি প্রতিটা পাঠক করে, আর তাদের একজন হলো ছয় মাস পরের তুমি।
দুই রকম comment, আর কোনটা কোথায় খাটে
Comment হলো মানুষের জন্য একটা টুকে রাখা কথা। Compiler তোমার code দেখার আগেই preprocessor প্রতিটা comment মুছে দেয়, তাই একটা comment কখনো program-র কাজ বদলাতে পারে না।
C তোমাকে দুইটা রূপ দেয়।
// চলে লাইনের শেষ পর্যন্ত। এটা এসেছে C99-এ, আর একটা লাইনের পাশে বা কয়েকটা লাইনের উপরে ছোট একটা কথা লিখতে তুমি এটাই চাও।
/* ... */ যত লাইন চাও তত লাইন জুড়ে থাকতে পারে, আর শেষ হয় কেবল তুমি বন্ধ করলে। একটা file বা একটা function-র মাথায় একটা অনুচ্ছেদ লিখতে এটা ব্যবহার করো।
দ্বিতীয় রূপটার সঙ্গে দুইটা নিয়ম আসে। এর ভেতরে এটা বসে না, তাই প্রথম */-ই ওটা বন্ধ করে দেয়। আর */ একেবারে ভুলে গেলে GCC বলে error: unterminated comment, আর তোমার /*-র পরের সবটা চুপচাপ comment হয়ে গেছে।
তাই // এক বাক্যের জন্য, /* */ এক অনুচ্ছেদের জন্য, আর বন্ধ না করা একটা /* গোটা একটা function গিলে ফেলতে পারে।
Comment বলে কেন, কী করছে সেটা না
কাজের comment আর বাজে comment-র তফাত এই একটা নিয়মেই। Code নিজেই বলে দিচ্ছে সে কী করছে। Comment তার জায়গাটা অর্জন করে এমন কিছু বলে, যেটা code বলতে পারে না।
বাজে, আর শুরুতে সবাই এটাই লেখে:
total = total + marks; /* add marks to total */
কাজের, কারণ এটা একটা সিদ্ধান্ত বোঝাচ্ছে:
total = total + marks; /* the retake mark replaces nothing, it stacks */
চার রকম comment প্রায় সব সময়ই লেখার মতো। কেন এই পথ, আর সোজা পথটা কেন না। একটা সংখ্যা কোথা থেকে এল। একটা লাইন তার input নিয়ে কী ধরে নিচ্ছে। আর পরের মানুষটাকে কোনো ফাঁদ নিয়ে সতর্ক করা।
এক রকম comment সব সময়ই মুছে দেওয়ার মতো: যেটা তার পাশের লাইনটাই আবার বলে। ওটা পড়ার কাজ দ্বিগুণ করে, আর লাইনটা বদলানোর মুহূর্তেই বাসি হয়ে যায়।
তাই একটা comment যদি code ঢাকা রেখে পড়লেও টিকে যায়, তাহলে ওটা সম্ভবত কাজের কিছু বলছে।
নাম: সবচেয়ে সস্তা documentation
নাম হলো এমন একটা comment, যেটা compiler তোমার হয়ে মিলিয়ে দেখে, কারণ ভুল নামটাও সব জায়গায় একই বানানে থাকে।
C নামে অক্ষর, অঙ্ক আর underscore চলতে দেয়, আর নাম অঙ্ক দিয়ে শুরু হতে পারে না। ফলে style বাছার সুযোগ তোমার থাকে, আর এই track ব্যবহার করে snake_case: ছোট হাতের অক্ষর, শব্দের মাঝে underscore, যেমন total_marks আর boxes_needed।
একটা নামের তিনটা পরীক্ষা:
- অচেনা কেউ কি আন্দাজ করতে পারবে ওতে কী আছে?
total_scoreপারবে,tsপারবে না। - এটা কি একক বা ধরনটা বলে?
delay_secondsdelay-কে হারায়, আরis_validহারায়flag-কে। - এটা কি সৎ?
areaনামের একটা variable যদি পরিসীমা ধরে রাখে, সেটাxনামেরটার চেয়েও খারাপ, কারণ ওটা পাঠককে মিথ্যা বলছে।
ছোট আয়ুর জিনিসের ছোট নামই ঠিক। i নামের একটা loop counter একটা রেওয়াজ, ব্যর্থতা না, কারণ ওটা বাঁচে তিন লাইন। যে variable পঞ্চাশ লাইন বাঁচে, সে পঞ্চাশটা character-র ভাবনা পাওয়ার যোগ্য।
তাই ভালো একটা নাম comment-র দরকারটাই মিটিয়ে দেয়, আর খারাপ নামকে comment দিয়ে বাঁচানো যায় না।
Indentation আর brace: এই track যে একটাই style মানে
Compiler তোমার সাজানো একেবারেই গোনে না। নিচের প্রতিটা লাইন তার কাছে একই program, আর এর একটামাত্র রূপ তুমি উত্তরাধিকারে পেতে চাইবে।
এই track একটাই style মানে, একবার বলে দেওয়া, যাতে তোমার পড়া প্রতিটা example একই ভাবে সাজানো থাকে:
- প্রতি স্তরে চারটা space, কখনো tab না, আর দুইটা মিলিয়ে তো নাই।
- Function-র শুরুর brace নিজের লাইনে, আর শেষের brace প্রথম ঘরে।
- প্রতি লাইনে একটা statement। এক লাইনে দুইটা statement কিছুই বাঁচায় না, একটাকে লুকিয়ে ফেলে।
- সম্পর্কিত লাইনের দলগুলোর মাঝে একটা ফাঁকা লাইন, বইয়ে অনুচ্ছেদ যেভাবে কাজ করে।
- প্রতিটা binary operator-র দুই পাশে একটা space:
a * b,a*bনা।
অন্য project অন্যভাবে বাছে, আর তারা ভুল না। Linux kernel আট চওড়া tab ব্যবহার করে আর brace একই লাইনে রাখে। আসল কথা হলো, একটা file, আর আদর্শভাবে একটা project, একটা বেছে নিয়ে আর কখনো তর্ক করে না।
তাই কোন style নেবে সেটা একবারের সিদ্ধান্ত, আর সব সময় ভুল একটাই জিনিস: এক file-এ দুইটা style।
Compiler যে whitespace গোনে না, পাঠক গোনে
Whitespace মানে space, tab আর ফাঁকা লাইন। Token-র মাঝে C যত whitespace-ই থাকুক একটা বিভাজক ধরে নেয়, তাই int a = 5; আর int a=5; compiler-র কাছে অবিকল এক।
দুই জায়গায় এটা বিনা পয়সার না। Keyword বা নাম ভাঙা যায় না, তাই in t a; একটা error। আর একটা string literal-র ভেতরে প্রতিটা space সত্যিকারের একটা character, যেটা ছাপা হয়।
কাজের ফলটা এই: একটা লম্বা statement-কে কয়েক লাইনে ছড়িয়ে তার টুকরোগুলো সাজিয়ে রাখতে পারো। Compiler-র কিছুই মনে হবে না।
তাই সাজানোটা পাঠকের জন্য, আর ঠিক দুইটা জায়গায় compiler তাকিয়ে আছে।
এই track নিয়ে একটা সৎ কথা
এই track-র প্রতিটা program প্রকাশের আগে compile করে চালানো হয়েছে, আর output-র ঘরগুলো সত্যিকারের output, অনুমান না। যে tool এটা করে তার নাম verify-code.mjs, আর সে দুই ভাষার প্রতিটা code block compile করে।
এখানকার কোনো program তোমার কাছে গোলমাল করলে আগে টাইপের ভুল খোঁজো, তারপর আমাদের জানাও। ছাপা বইটাও এই একই কথা দেয়। সেজন্যই একটা output-র ঘরকে তুমি এতটা বিশ্বাস করতে পারো যে নিজেরটা তার সঙ্গে মিলিয়ে দেখো।
Comment-র দুইটা রূপ
// A single-line comment. Runs to the end of this line.
/* A block comment.
It can span as many lines as you like,
and it ends only when you close it. */
int marks = 90; // a comment may sit after code too
//-র কোনো বন্ধ করার চিহ্ন লাগে না; লাইনের শেষটাই ওটা বন্ধ করে।/*-কে*/দিয়ে বন্ধ করতে হয়, আর block comment-র ভেতরে block comment বসে না।- দুইটাই preprocessor মুছে দেয়, তাই চলার সময় কোনোটারই কোনো খরচ নেই।
- String-র ভেতরে
//আর/*সাদামাটা character, আর সেজন্যই"https://progsity.io"টিকে যায়।
এটা Kenji-র রূপ। কোনো warning ছাড়া compile হয়, আর 5 বাই 3 ঘরের জন্য tile-র বাক্সের ঠিক সংখ্যাটাই ছাপে।
#include <stdio.h>
int main(void){int l=5;int w=3;int a=l*w;printf("Order %d boxes\n",a);return 0;}
Order 15 boxes
এটা নিয়ে নিজেকে তিনটা প্রশ্ন করো। a জিনিসটা কী? l মানে length না list? আর দ্বিতীয় একটা ঘর তুমি কোথায় যোগ করবে?
এখানে space, লাইন ভাঙা আর ভালো নাম ছাড়া কিছুই যোগ হয়নি। Output অবিকল একই, byte ধরে ধরে।
#include <stdio.h>
int main(void)
{
int room_length = 5;
int room_width = 3;
int boxes_needed = room_length * room_width;
printf("Order %d boxes\n", boxes_needed);
return 0;
}
Order 15 boxes
Example 1-র তিনটা প্রশ্নের উত্তর এখন নিজেরাই এসে যায়, আর দ্বিতীয় ঘরটা কোথায় বসবে সেই ফাঁকা লাইনটাও চোখে পড়ে। কাজের জায়গায় "পড়ার মতো" মানে এটাই: পরের বদলটা কোথায় হবে সেটা দেখা যায়।
Compiler-এ চালাওএবার comment-গুলো। খেয়াল করো, এদের একটাও বলছে না লাইনটা কী করে; প্রতিটা বলছে এমন কিছু, যেটা code বলতে পারে না।
#include <stdio.h>
int main(void)
{
/* Room measurements in metres, from the site survey of 12 March.
Both are whole numbers because the supplier only cuts to the metre. */
int room_length = 5;
int room_width = 3;
/* Tiles ship in boxes of one square metre, so the area is the box count. */
int boxes_needed = room_length * room_width;
printf("Order %d boxes\n", boxes_needed); // the shop wants boxes, not area
return 0;
}
Order 15 boxes
তিনটা comment, তিনটা তথ্য, যেগুলো code বইতে পারে না: সংখ্যাগুলো কোথা থেকে এল, পূর্ণসংখ্যাই কেন যথেষ্ট, আর ছাপা শব্দটা "boxes" কেন। এর যেকোনোটা মুছে দাও, আর পাঠককে কাউকে জিজ্ঞেস করতে হবে।
Compiler-এ চালাওএটা কোথায় কাজে লাগছে
- Linux kernel-র coding style. একটা document,
Documentation/process/coding-style.rst, তিন কোটি লাইন C-র জন্য ঠিক করে দেয় আট চওড়া tab, প্রতি লাইনে একটা statement আর ছোট function। তার comment-র অংশটা সোজা বলে, comment-র বলা উচিত code কী করে, কীভাবে করে সেটা না, কারণ কীভাবে তো ওখানেই আছে। - Git. Git-র source-র
Documentation/CodingGuidelinesহাজার হাজার মানুষের patch পাঠানো একটা project-এ ঠিক এই কাজটাই করে। এটা না মানা একটা patch যুক্তি পড়ার আগেই ফিরে আসে। - CI-র formatting job. অনেক project প্রতিটা বদলের উপর
clang-formatচালায় আর তফাত পেলে build ফেল করায়। Style নিয়ে তর্কটা একবার হয়, একটা config file-এ, আর review-তে আর কখনো না। - SQLite. এর source-এ অসম্ভব বেশি comment, আর C শেখার জন্য মানুষ ওটা পড়ে তার বড় কারণও সেটাই। ওখানকার comment file-র গড়ন আর সিদ্ধান্ত বোঝায়, syntax না।
যে ভুলগুলো সবাই করে
১. যে comment পাশের লাইনটাই আবার বলে।
i = i + 1; /* add one to i */
কোনো message নেই, কারণ ভুল কিছু নেই। তবু এটা একটা ভুল: পড়ার কাজ দ্বিগুণ করে আর কিছুই যোগ করে না। /* skip the header row */ হলে জায়গাটার দাম উঠত।
২. Block comment বন্ধ করতে ভুলে যাওয়া।
/* fix this later
printf("Order %d boxes\n", boxes_needed);
return 0;
}
GCC বলে error: unterminated comment, তারপর error: expected declaration or statement at end of input। /*-র পরের সবটা comment হয়ে গেছে, শেষের brace-টা সহ, তাই দ্বিতীয় message এমন একটা file নিয়ে, যার এখন কোনো শেষ নেই।
৩. বাসি হয়ে যাওয়া comment।
/* Boxes of half a square metre each. */
int boxes_needed = room_length * room_width;
কোনো message নেই, কখনোই না। লাইনটা বদলানো হয়েছে, comment-টা না, তাই file-এ এখন আত্মবিশ্বাসী একটা মিথ্যা বসে আছে। বাসি comment কোনো comment না থাকার চেয়েও খারাপ, আর নিচের মাথা খাটানোর প্রশ্নটা ওটাই।
৪. Tab আর space মিলিয়ে ফেলা।
int main(void)
{
int a = 1;
int b = 2;
int c = 3;
}
কোনো message নেই। ওই তিনটা লাইনের indentation চারটা space, একটা tab আর আটটা space, আর ওরা এক editor-এ মেলে আর আরেকটায় মেলে না। Space বেছে নাও, Tab চাপলে editor যাতে space বসায় সেটা ঠিক করে দাও, আর সমস্যাটা উবে যায়।
এই program-টা হাতে নতুন করে সাজাও, এই track যে style মানে সেভাবে। এটা কী করে, তার কিছু বদলাবে না।
#include <stdio.h>
int main(void){int p=37;int c=41;int t=p+c;printf("Total %d\n",t);printf("Subjects 2\n");return 0;}
পাঁচটা যাচাই। প্রতি স্তরে চারটা space। main-র brace দুইটা নিজের লাইনে। প্রতি লাইনে একটা statement। অচেনা কেউ পড়তে পারে এমন নাম। Declaration আর ছাপার মাঝে একটা ফাঁকা লাইন।
নিজে যাচাই করো। তোমার রূপটা চালিয়ে output-টা আসলটার সঙ্গে লাইন ধরে ধরে মেলাও। যে সাজানোয় output বদলে যায়, সেটা সাজানো না; সেটা তোমার সদ্য লেখা একটা bug।
Compiler-এ চালাওএই program-এ তিনটা লাইন আছে, যেগুলো একজন পাঠককে অবাক করবে। ঠিক তিনটা comment যোগ করো, প্রতিটার জন্য একটা, আর প্রতিটাকে বলতে হবে কেন।
#include <stdio.h>
int main(void)
{
int reading = 512;
int calibrated = reading - 12;
int percent = calibrated / 5;
printf("Tank at %d percent\n", percent);
return 0;
}
তিনটা অবাক করা জিনিস। 12 কেন বিয়োগ করা হচ্ছে। ভাগটা 5 দিয়ে কেন। আর ভাগে ভগ্নাংশ হারিয়ে যাওয়ার পরেও ট্যাংকের পাঠ পূর্ণসংখ্যায় কেন ছাপা হচ্ছে।
নিয়ম। কারণগুলো তুমি বানিয়ে নাও; sensor-টা তুমিই বসিয়েছ এমন engineer তুমি। কোনো comment কোনো operator-র নাম বলবে না, আর হিসাবটা আবার বলবে না। প্রতিটা বারো শব্দ বা তার কম।
নিজে যাচাই করো। Code ঢেকে রেখে কেবল তোমার তিনটা comment পড়ো। ওগুলো যদি একটা sensor নিয়ে ছোট একটা গল্প বলে, তাহলে তুমি ঠিক ধরনের comment লিখেছ।
Compiler-এ চালাওযে প্রশ্নগুলো সবার মনে আসে
একটা program-এ কতটা comment থাকা উচিত?
কোনো সংখ্যা নেই। একটা ভালো পরীক্ষা: এক সপ্তাহ দূরে থেকে ফিরে এসে তুমি কি এই file নিরাপদে বদলাতে পারবে? কোনো লাইনে থেমে যেতে হলে ওই লাইনটার একটা comment পাওয়া দরকার।
লম্বা নাম program ধীর করে দেয়?
না। নাম থাকে কেবল তোমার source-এ; compiler ওগুলোকে address বানিয়ে দেয়।
boxes_neededআরaএকই machine code বানায়।Tab না space?
যেকোনোটা, তবে এক নিয়মে। এই track চারটা space ব্যবহার করে, Linux kernel আট চওড়া tab, আর দুইটাই ঠিক। এক file-এ দুইটা মেলানোটাই একমাত্র আসল ভুল।
পুরনো code মুছে না দিয়ে comment করে রাখা যায়?
না। Git প্রতিটা রূপ মনে রাখে, তাই comment করে রাখা code পাঠকের ডিঙাতে হওয়া বাড়তি বোঝা। মুছে দাও আর history-কে বিশ্বাস করো।
Comment কি মেশিন যে program চালায় তার ভেতরে থাকে?
না। Preprocessor ধাপ 1-এই ওগুলো ছেঁটে দেয়, compiler তোমার file পড়ার আগেই, তাই চলার সময় ওদের কোনো খরচ নেই।
মূল কথা
//শেষ হয় লাইনে;/* */শেষ হয় তুমি যেখানে বন্ধ করো, আর ওটার ভেতরে ওটা বসে না।- Comment তার জায়গা অর্জন করে "কেন" বলে, কারণ "কী" তো code-ই বলে দিচ্ছে।
- ভালো নাম comment-র দরকার মিটিয়ে দেয়; বাসি comment না থাকার চেয়েও খারাপ।
- এই track চারটা space, নিজের লাইনে brace আর প্রতি লাইনে একটা statement ব্যবহার করে।
- নামের ভেতরে আর string-র ভেতরে ছাড়া compiler whitespace গোনে না।
- এই track-র প্রতিটা program compile করে চালানো হয়েছে, আর output-র ঘরগুলো সত্যিকারের।
পরের lesson-এ তুমি নিজে একটা program compile করে চালাবে, Playground-এ, আর চাইলে নিজের মেশিনে gcc দিয়ে।
lesson ৫ শেষ
শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।
পরেরটা: নিজে compile করে চালাও: Playground আর নিজের মেশিনের toolchain