Learn C Programming

lesson ১ / ৮ · C-র সঙ্গে পরিচয়: প্রথম program, চিহ্ন ধরে ধরে

Module ১ · C-র সঙ্গে পরিচয়: প্রথম program, চিহ্ন ধরে ধরে

তোমার প্রথম C program, লাইন ধরে ধরে

Freeপড়া

এই lesson-এ যা শিখবে

  • Playground-এ নিজের প্রথম C program চালাতে পারবে আর তার output পড়তে পারবে।
  • Skeleton-র প্রতিটা অংশের নাম বলতে পারবে: directive, function header, body, statement, return।
  • দুইটা printf একটা newline ভাগ করে নিলে কী ছাপবে, সেটা আগেই বলতে পারবে।

Alice পাঁচ লাইন লিখল, Run চাপল, আর পর্দায় এল Hello, C. চার সেকেন্ড সে খুশি থাকল। তারপর return 0; লাইনটা মুছে আবার Run চাপল, আর তাতেও এল Hello, C.

তাহলে ওই লাইনটা কী কাজে ছিল? যে program চলে, আর যে program তুমি বোঝো, দুইটা এক জিনিস না। এই lesson সেই পাঁচ লাইনকে এক শব্দ ধরে ধরে খুলবে, আর Alice-র মুছে ফেলা লাইনটা কারণসহ ফিরিয়ে দেবে।

C-তে keyword আছে 32টা। তোমার প্রথম program তার দুইটা ব্যবহার করে, বাকি 30টা অপেক্ষা করতে পারে।

পাঁচটা লাইন, একবার দেখে নাও

পুরো program-টা নিচে। এখনই বোঝার চেষ্টা করো না। আগে শুধু এর চেহারাটা চোখে বসতে দাও।

#include <stdio.h>

int main(void)
{
    printf("Hello, C.\n");
    return 0;
}
Hello, C.

এখানে অর্থ বহন করছে চারটা জিনিস: # দিয়ে শুরু একটা লাইন, একটা function header, একজোড়া brace, আর তার ভেতরে দুইটা statement। এরপর সারা জীবন যত C program লিখবে, সবই এই চেহারাটার একটু লম্বা রূপ।

লাইন 1: include directive

শুরুর # চিহ্নটা একটা সংকেত। এটা বলে: এই লাইন compiler-র জন্য না। এটা preprocessor-র জন্য, যে tool সবার আগে চলে আর তোমার file গুছিয়ে দেয় (Module 0, lesson 4)।

include তার নির্দেশ, আর নির্দেশটা সোজাসাপটা: ওই file-র পুরোটা এখানে, এই মুহূর্তে, compile শুরুর আগেই বসিয়ে দাও।

stdio.h হলো standard input output header-র সংক্ষেপ। এটা সাদামাটা একটা text file, তোমার compiler-র সঙ্গেই এসেছে। এর ভেতরে printf, scanf আর আরও প্রায় চল্লিশটা function-র declaration আছে।

Declaration মানে প্রতিশ্রুতি, code না। Menu-র কথা ভাবো: কোন খাবার আছে আর কোনটার কী লাগে, সেটাই ওতে লেখা। রান্নাঘর, মানে printf-র আসল machine code, থাকে C standard library-তে, আর জোড়া লাগে linker-এ।

কোণাকৃতি bracket < > preprocessor-কে system-র নিজের folder-এ পাঠায়। Double quote, যেমন #include "myfile.h", তাকে আগে তোমার project folder-এ পাঠায়।

তাই directive কোনো statement না, আর ওতে semicolon বসে না। ওখানে একটা বসালে তুমি program-র ভেতরে একটা এলোমেলো ; বসিয়ে দিলে।

লাইন 3: function-র header

এখান থেকেই তোমার program চলতে শুরু করে। Operating system program চালু করার সময় main-কে call করে। File-র প্রথম লাইনকে না। তুমি প্রথমে যে function লিখেছ তাকেও না। সব সময় main।

বাম থেকে ডানে তিনটা টুকরো:

  • int হলো return type। এটা কথা দেয়, main শেষ হলে সে operating system-কে একটা পূর্ণসংখ্যা ফেরত দেবে।
  • main হলো নাম, আর নামটা C standard বেঁধে দিয়েছে। Main, MAIN আর main2 তিনটা আলাদা নাম, আর এর একটাও system যাকে call করে সেটা না।
  • (void) হলো parameter-র তালিকা। Parameter মানে function যে মানগুলো নেয়, আর এখানে void বলছে "এটা কিছুই নেয় না"।

int main(int argc, char *argv[])-ও দেখবে, যেভাবে একটা program তার নামের পরে লেখা শব্দগুলো পড়ে। সেটা Module 15। তার আগে (void) লিখলেই তুমি যা বলতে চাও তা বলা হয়।

তাই int main(void) হলো একটা প্রতিশ্রুতি আর একটা নাম, আর মেশিন নিজের ভাগটা রাখে ওটাকে সবার আগে call করে।

লাইন 4 আর 7: দুইটা brace

Curly brace function-র body চিহ্নিত করে: কোথায় শুরু আর কোথায় শেষ। এই দুইটার মাঝের সবকিছুই main-র কাজ।

Brace সব সময় জোড়ায় আসে। সঙ্গীহীন একটা brace C-তে beginner-দের সবচেয়ে সাধারণ ভুল। এর message-টা অদ্ভুত লাগে, কারণ compiler ব্যাপারটা টের পায় file-র একদম শেষে।

তাই কোনো error যদি তোমার না ছোঁয়া শেষ লাইনটার নাম বলে, brace গুনে দেখো।

লাইন 5: statement, টুকরো ধরে ধরে

এই লাইনটা একটা function call। তুমি printf-কে তার কাজটা করতে বলছ, আর হাতে একটা জিনিস ধরিয়ে দিচ্ছ।

  • Function-র হাতে যে মান তুমি ধরিয়ে দাও, তার নাম argument। এখানে argument হলো "Hello, C.\n" লেখাটা।
  • Double quote বানায় একটা string literal, মানে নির্দিষ্ট একটুকরো লেখা।
  • \n একটা escape sequence। এর মানে newline: পরের লাইনের শুরুতে যাও।
  • Semicolon statement-টা শেষ করে। C-তে statement শেষ হয় তুমি যেখানে শেষ বলো, লাইন যেখানে শেষ সেখানে না।

\n-টা আরেকবার দেখো, কারণ এটা সবাইকে অবাক করে। তুমি লিখেছ দুইটা character, একটা backslash আর একটা n। Output-এ থাকে একটা character, লাইন শেষ করা ওই অদৃশ্যটা। Backslash ছাপা হয় না; ওটা compiler-কে তার পরের character নিয়ে দেওয়া নির্দেশ।

তাই printf("A\nB\n") একটা call থেকেই দুই লাইন ছাপে, আর ওই string-র দৈর্ঘ্য 4টা character, 6টা না।

লাইন 6: return 0, আর Alice যেটা মুছেছিল

main একটা int ফেরত দেওয়ার কথা দিয়েছিল, তাই সে দেয়। প্রচলিত নিয়মে 0 মানে "শেষ, কোনো সমস্যা হয়নি", আর অন্য যেকোনো সংখ্যা মানে কিছু একটা গড়বড় হয়েছে।

ওই সংখ্যাটা program-র exit status, আর এটা সাজসজ্জা না। Operating system ওটা মনে রাখে। Linux বা macOS-এ একটা program চালিয়ে তারপর echo $? লিখলে ফেরত দেওয়া সংখ্যাটা দেখতে পাবে।

$ ./hello
Hello, C.
$ echo $?
0

এভাবেই একটা script ঠিক করে পরের ধাপ কী। CI pipeline-র প্রতিটা "build failed, stopping here" আসলে শূন্য ছাড়া অন্য কিছু ফেরত দেওয়া একটা program।

এবার Alice-র পরীক্ষাটা। C99 standard থেকে, main-র শেষ brace পর্যন্ত পৌঁছালে ফলটা return 0;-র সমান। ওর program ঠিক ছিল, কারণ standard লাইনটা ওর হয়ে বসিয়ে দিয়েছে।

তবুও লাইনটা লেখো। এক লাইনই তো, আর এটা তুমি যা বলতে চাও সেটাই বলে, আর এই ছাড়টা C-তে কেবল main পায়। মান ফেরত দেয় এমন অন্য কোনো function-এ এটা বাদ দিলে সেটা সত্যিকারের একটা bug।

তাই main-এ return 0; বাদ পড়লে কিছুই বদলায় না, আর আর কোথাও return বাদ পড়লে সবই বদলে যায়।

Skeleton, প্রতিটা অংশের নাম দিয়ে

#include <header>        directive: for the preprocessor, no semicolon

int main(void)            return type, name, parameter list
{                         body starts
    statement;            one instruction, ended by a semicolon
    return 0;             the exit status handed to the system
}                         body ends
  • #include compile শুরুর আগে declaration-ভরা একটা file বসিয়ে দেয়।
  • int হলো main যা ফেরত দেয়; (void) হলো সে যা নেয়, মানে কিছুই না।
  • Brace দুইটা body ধরে রাখে; compiler ওদের জোড়ায় জোড়ায় গোনে।
  • ভেতরের প্রতিটা statement ; দিয়ে শেষ হয়, যত লাইন জুড়েই লেখা হোক।
একটা C program-র শরীর, লাইন ধরে ধরে #include <stdio.h> int main(void) { printf("Hello, C.\n"); return 0; } directive preprocessor পড়ে, semicolon নেই function header return type, নাম, parameter-র তালিকা body brace সব সময় জোড়ায় আসে একটা statement: একটা call, একটা argument, একটা semicolon return 0 হলো system যে exit status পড়ে
ছবি 1। সেই একই পাঁচ লাইন, প্রতিটা অংশের নাম দিয়ে। নামগুলো এখনই শিখে নাও; প্রতিটা error message এগুলোই ব্যবহার করে।
Example 1: কিছু একটা করে এমন সবচেয়ে ছোট program

হাতে লিখো। paste করো না। লিখতে গিয়ে যে ভুলগুলো করবে, ওগুলোই শেখার জায়গা।

#include <stdio.h>

int main(void)
{
    printf("Hello, C.\n");
    return 0;
}
Hello, C.

লেখাটা বদলে আবার Run চাপো, আর দেখো output তোমার পিছু নিচ্ছে। এই চক্রটাই, লেখা আর চালানো, পুরো কাজ।

Compiler-এ চালাও
Example 2: তিনটা call থেকে তিন লাইন

Statement উপর থেকে নিচে, ক্রম ধরে চলে। মাঝখানে চালাকির কিছু ঘটে না।

#include <stdio.h>

int main(void)
{
    printf("Welcome to Progsity\n");
    printf("Track: C, from zero\n");
    printf("Part Zero: before you write a single line\n");
    return 0;
}
Welcome to Progsity
Track: C, from zero
Part Zero: before you write a single line

এবার একটা \n মুছে আবার চালাও। দুই লাইন জুড়ে এক লাইন হয়ে যায়, আর তাতেই প্রমাণ হয় ভাঙাটা আসে escape sequence থেকে, আলাদা call থেকে না।

Compiler-এ চালাও
Example 3: beginner আসলে যেটা লেখে

চারটা printf হাতে পেলে বেশিরভাগ মানুষ সঙ্গে সঙ্গে একটা বাক্স আঁকে। দাঁড়ি দুইটার ভেতরের character গুনে দেখো; না মিললে বাক্সটা কাত হয়ে যায়।

#include <stdio.h>

int main(void)
{
    printf("+----------------+\n");
    printf("|   PROGSITY C   |\n");
    printf("|   Part Zero    |\n");
    printf("+----------------+\n");
    return 0;
}
+----------------+
|   PROGSITY C   |
|   Part Zero    |
+----------------+

এখানে প্রতিটা লাইন 18 character চওড়া। Judge তোমার output-কে প্রত্যাশিত output-র সঙ্গে মেলানোর সময় space-ও গোনে, তাই একটা বাড়তি space মানেই ভুল উত্তর। নিচের problem-গুলোতে ঠিক সেটার সামনেই পড়বে।

Compiler-এ চালাও

এটা কোথায় কাজে লাগছে

  • Git. Git-র source-র git.c file-এ আছে int main(int argc, const char **argv), parameter-র তালিকা সহ সেই একই header, আর git push-র পর তোমার shell যে status পড়ে সেটা ওটাই ফেরত দেয়।
  • SQLite. sqlite3 command line tool-টা একটাই C file, shell.c, যার main তোমার query পড়ে আর .quit লিখলে 0 ফেরত দেয়।
  • GNU coreutils. ls.c শেষ হয় একটা return দিয়ে, যেটা exit status বহন করে, আর সেজন্যই ls missing-file; echo $? শূন্য ছাড়া অন্য একটা সংখ্যা ছাপে।
  • Nginx. nginx.c-তে একটাই main, কয়েকশো লাইন লম্বা, যেটা configuration পড়ে আর worker process চালু করে। একই skeleton, শুধু আসবাব বেশি।

যে ভুলগুলো সবাই করে

১. Include লাইনটা ভুলে যাওয়া, আর তাতেও পার পেয়ে যাওয়া।

int main(void)
{
    printf("Hello, C.\n");
    return 0;
}

Playground-র GCC 12-তে এটা warning: implicit declaration of function 'printf', আর program build হয়ে ঠিকঠাক ছাপে। Compiler printf-র নামও শোনেনি, তার চেহারাটা অনুমান করেছে, আর এইবার অনুমান মিলে গেছে। GCC 14 একই লাইনকে error বানায়। Header-টা দাও; যে warning তুমি বোঝো, সেটাও সারানোর মতো warning।

২. Printf-র P বড় হাতের হয়ে যাওয়া।

#include <stdio.h>

int main(void)
{
    Printf("Hello, C.\n");
    return 0;
}

GCC 12 বলে warning: implicit declaration of function 'Printf', আর তারপর linker বলে undefined reference to 'Printf'। C ছোট আর বড় হাতের অক্ষর আলাদা গোনে, তাই Printf আলাদা একটা নাম, আর ওটা কোথাও লেখাই নেই। দুইটা message একসঙ্গে পড়লেই সূত্রটা পাবে: compiler তোমাকে বিশ্বাস করেছে, আর linker খুঁজতে গিয়েছে।

৩. Directive-র শেষে semicolon দেওয়া।

#include <stdio.h>;

int main(void)
{
    printf("Hello, C.\n");
    return 0;
}

GCC 12 বলে warning: extra tokens at end of #include directive, তারপর program build করে চালিয়েও দেয়। Preprocessor header-র নামটা পড়েছে, তারপর এমন একটা semicolon পেয়েছে যার তার কোনো দরকার নেই, আর নালিশ করে ওটা ফেলে দিয়েছে। Directive statement না, ওতে semicolon বসে না, আজ ভুলটা তোমার কিছু না কাড়লেও।

৪. Semicolon বাদ পড়ার error-এ লাইন নম্বরটাকে বিশ্বাস করা।

printf("Hello, C.\n")
return 0;

GCC বলে error: expected ';' before 'return', আর return লাইনটার নাম বলে। ওই লাইনে কোনো সমস্যাই নেই। Compiler পড়তে পড়তে এমন একটা শব্দে এসে থেমেছে, যেটা statement-টা আর চালিয়ে নিতে পারে না, আর নালিশ করেছে ওখানেই। যে লাইনের নাম বলছে, সব সময় তার উপরের লাইনটা দেখো।

মাথা খাটাও

Zara এটা লিখে বলছে দুই লাইন ছাপবে। Bob বলছে এক লাইন। হুবহু output-টা character ধরে ধরে বলো, তারপর বলো program থেকে কয়টা newline character বেরোয়।

#include <stdio.h>

int main(void)
{
    printf("Chittagong");
    printf("Sylhet\n");
    return 0;
}

তারপর কঠিন অর্ধেকটা: একটা, আর কেবল একটা \n কোথায় বসালে output দুই লাইন হবে, আর দ্বিতীয় লাইনের পরে কিছু ঝুলে থাকবে না?

লাইন ভাঙাটা string-র ভেতরে, call-র ভেতরে না। Source-এ \n character কয়টা আছে গোনো। তোমার output-র একই লাইনে shell-র prompt বসে থাকাটা একটা সূত্র, bug না।

অনুশীলন ১সহজ

Alice স্কুলের মেলায় বই বেচে, আর তার stall-র জন্য একটা সাইন লাগবে। সাইনটা দুই লাইনের: একটা বাঁধা banner, তারপর Stall শব্দটা আর তার stall নম্বর।

Input. এক লাইনে একটা integer n, stall নম্বর।

Output. ঠিক দুই লাইন:

=== PROGSITY ===
Stall n

Constraints. 1 <= n <= 999।

Sample. Input 7 দিলে আসবে === PROGSITY ===, তারপর Stall 7। Banner-র দুই পাশে ঠিক তিনটা সমান চিহ্ন, আর প্রতি জোড়ার ভেতরে একটা space।

#include <stdio.h>

int main(void)
{
    int n;
    scanf("%d", &n);

    /* Your two printf calls go here. */

    return 0;
}

Input পড়ার লাইনটা তোমার জন্য লেখা আছে; scanf ঠিকভাবে শেখানো হয় Module 3-এ। এটার hidden test সহ grading হয় এই module-র Problems lesson-এ, banner-two-lines নামে।

Compiler-এ চালাও
অনুশীলন ২সহজ

Maria চায় তার তিন বিষয়ের নম্বর ছোট একটা card-র মতো ছাপা হোক, প্রতি লাইনে এক বিষয়, তিনটা আলাদা printf call দিয়ে।

Input. এক লাইনে তিনটা integer a b c, মানে Maths, Physics আর Chemistry-র নম্বর।

Output. এই ক্রমে তিন লাইন:

Maths: a
Physics: b
Chemistry: c

Constraints. 0 <= a, b, c <= 100।

Sample. Input 90 85 77 দিলে আসবে Maths: 90, Physics: 85, Chemistry: 77, প্রতিটা নিজের লাইনে। প্রতিটা colon-র পরে একটা space।

#include <stdio.h>

int main(void)
{
    int a, b, c;
    scanf("%d %d %d", &a, &b, &c);

    /* Three printf calls, one per subject. */

    return 0;
}

Grading হয় three-printf-lines নামে। একটা না, তিনটা call ব্যবহার করো; আসল কথাটা হলো statement ক্রম ধরে চলে।

Compiler-এ চালাও
অনুশীলন ৩মাঝারি

Bob-র report লাইনটায় একটা tab আর একজোড়া উদ্ধৃতি চিহ্ন থাকতে হবে। String-র ভেতরে quote ছাপতে escape লাগে, কারণ খালি একটা quote string-টা আগেই শেষ করে দিত।

Input. এক লাইনে একটা integer s, Bob-র score।

Output. ঠিক দুই লাইন। Tab মানে একটা tab character, space না:

Bob wrote:	"Hello, C."
Score:	s

Constraints. 0 <= s <= 100।

Sample. Input 95 দিলে আসে Bob wrote:, একটা tab, তারপর "Hello, C."; দ্বিতীয় লাইনটা Score:, একটা tab, তারপর 95।

#include <stdio.h>

int main(void)
{
    int s;
    scanf("%d", &s);

    /* Two printf calls. You need \t and \" here. */

    return 0;
}

Grading হয় tab-and-quote নামে। Escape-র table আছে এই module-র lesson 3-এ, চেষ্টা করার আগে দেখে নিতে চাইলে।

Compiler-এ চালাও

যে প্রশ্নগুলো সবার মনে আসে

  • int main(void)-র বদলে int main() লেখা যায়?

    Compile হয়, আর C17-এ দুইটার মানে একটু আলাদা: () parameter নিয়ে কিছুই বলে না, (void) বলে একটাও নেই। (void) লিখলে code review-তে এটা নিয়ে কেউ প্রশ্ন করবে না।

  • \n একটা character কেন, দুইটা না কেন?

    কারণ backslash ছাপা হয় না। ওটা compiler-কে বলে, লাইন শেষ করা ওই একটামাত্র অদৃশ্য character রাখো। তোমার file-এ দুইটা character, program-র output-এ একটা।

  • Indentation-এ কিছু আসে যায়?

    Compiler-র কিছু আসে যায় না, সে token-র মাঝের space, tab আর ফাঁকা লাইন ধরেই না। পরের পাঠকের অনেক কিছু আসে যায়, আর এই module-র lesson 5 ঠিক সেটা নিয়েই।

  • 0-র বদলে 1 ফেরত দিলে কী হয়?

    Program একই জিনিস ছাপে, আর operating system-কে বলে সে ব্যর্থ হয়েছে। Status দেখে এমন একটা shell script ওখানেই থেমে যেত। "কিছু গড়বড় হয়েছে" বোঝাতে না চাইলে 0 ব্যবহার করো।

  • এগুলো চালাতে কিছু বসাতে হবে?

    না। প্রতিটা example-র Run link Playground খুলে দেয়, program আগেই ভেতরে বসানো থাকে। নিজের মেশিনে compiler বসানো এই module-র lesson 6, আর ওটা ইচ্ছাধীন।

মূল কথা

  • একটা C program মানে একটা directive, একটা function header, একজোড়া brace, আর ভেতরে statement।
  • #include <stdio.h> printf-র প্রতিশ্রুতিগুলো বসায়; আসল code জোড়া লাগে linker-এ।
  • main হলো বাঁধা নামটা, যাকে operating system call করে, আর int হলো সে যে status ফেরত দেয়।
  • Statement শেষ হয় তার semicolon-এ, লাইনের শেষে না।
  • \n একটা character, লেখা হয় দুইটা দিয়ে, আর নতুন লাইনটা ওটাই বানায়।
  • C99 থেকে main-র শেষে পৌঁছালে নিজেই 0 ফেরত যায়, তবুও লাইনটা তুমি লেখো।

পরের lesson-এ ভুল হওয়ার দুই রকমের সঙ্গে পরিচয় হবে। একটা compiler নিতেই রাজি হয় না, আরেকটা সে হাসিমুখে build করে ভুল উত্তর দেয়।

lesson ১ শেষ

শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।

পরেরটা: Syntax আর semantics: ভুল হওয়ার দুই রকম