Learn C Programming

lesson ১ / ৮ · Function, scope আর storage class

Module ৭ · Function, scope আর storage class

Function: এক টুকরো কাজকে একটা নাম দেওয়া

Freeপড়া

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

  • main-এর উপরে একটা function define করতে পারবে, আর main থেকে ওটার নাম লিখে call করতে পারবে।
  • বলতে পারবে একটা program-কে কেন function-এ ভাগ করা হয়: একটা কাজ, একটা নাম, একবার লেখা।
  • একটা call trace করতে পারবে: control লাফ দিয়ে function-এ ঢোকে, body চালায়, তারপর পরের লাইনে ফিরে আসে।

Module 3-এর Maria-র till-টার কথা মনে আছে? VAT-এর হার লেখা ছিল এগারো জায়গায়, আর একটা কপি তিন সপ্তাহ ধরে ভুল ছিল।

ওর receipt-এর program এখন তিন রকম কাগজ ছাপে: বিক্রির slip, টাকা ফেরতের slip, আর দিনের শেষের report। প্রতিটার মাথায় একই দুই লাইনের header, তিন জায়গায় কপি করা।

VAT-এর হার 15 শতাংশ হওয়ার পর Bob যতগুলো কপি খুঁজে পেয়েছে, সব বদলে দিয়েছে। খুঁজে পেয়েছে দুইটা। Code কপি করতে এক সেকেন্ড লাগে; পরে প্রতিটা কপি খুঁজে বের করতেই যত ঝামেলা।

এই lesson ওই header-কে একটা নাম দেবে, আর রাখবে একটাই জায়গায়।

এক টুকরো কাজ, একটা নাম

এটাই সেই program যেটা Bob বদলেছিল, শুধু তিনটা slip রেখে ছেঁটে আনা। আসল till-এ কপিগুলো ছিল শত শত লাইন দূরে দূরে।

#include <stdio.h>

int main(void)
{
    printf("Maria General Store\n");
    printf("VAT 15 percent included\n");
    printf("sale: 40\n");
    printf("\n");

    printf("Maria General Store\n");
    printf("VAT 15 percent included\n");
    printf("refund: 50\n");
    printf("\n");

    printf("Maria General Store\n");
    printf("VAT 7.5 percent included\n");
    printf("day total: 1250\n");

    return 0;
}
Maria General Store
VAT 15 percent included
sale: 40

Maria General Store
VAT 15 percent included
refund: 50

Maria General Store
VAT 7.5 percent included
day total: 1250

দিনের শেষের report এখনো 7.5 শতাংশই বলছে। কোনো compiler এটা ধরতে পারবে না, কারণ প্রতিটা লাইনই বৈধ C।

Maria যথারীতি জিজ্ঞেস করে: "কিন্তু কেন? একটু সাবধানে তিনটাই বদলে দিলেই তো হতো।"

কারণ এরপর যে till-এর code-এ হাত দেবে, সে জানবেই না যে কপি আছে তিনটা। কপিগুলো চোখেই না পড়লে সাবধান থেকে লাভ নেই।

সমাধান হলো header-টা একবার লেখা, ওটাকে একটা নাম দেওয়া, আর তিন জায়গায় শুধু নামটা বসানো। নাম দেওয়া এমন এক টুকরো কাজকে বলে function। নামটা লিখে ওটাকে চালানোকে বলে function-টাকে call করা।

#include <stdio.h>

void print_header(void)
{
    printf("Maria General Store\n");
    printf("VAT 15 percent included\n");
}

int main(void)
{
    print_header();
    printf("sale: 40\n");
    printf("\n");

    print_header();
    printf("refund: 50\n");
    printf("\n");

    print_header();
    printf("day total: 1250\n");

    return 0;
}
Maria General Store
VAT 15 percent included
sale: 40

Maria General Store
VAT 15 percent included
refund: 50

Maria General Store
VAT 15 percent included
day total: 1250

চেহারা একই, আর তিনটা header-ই ঠিক। VAT-এর লাইন এখন থাকে একটাই জায়গায়, তাই পরের বদলটা প্রতিটা slip-এ নিজে থেকেই পৌঁছে যায়।

এবার শুধু main-টা পড়ো: header, বিক্রি; header, ফেরত; header, দিনের মোট। Program-এর গল্পটা এখানেই বলা আছে, খুঁটিনাটিগুলো সরানো আছে অন্য জায়গায়।

তার মানে function এক টুকরো কাজকে একটা নাম দেয়। তুমি ওটা একবার লেখো, আর যেখানে যেখানে ওই কাজটা লাগে, সেখানে call করো।

একটা definition-এর গঠন

যে block একটা function তৈরি করে, সেটাকে বলে ওর definition। উপরেরটাকেই এবার অংশ ধরে ধরে দেখো।

Function definition আর একটা call

void print_header(void)
{
    statements
}

print_header();
  • নামের আগের void হলো return type: কাজ শেষে function কী ফেরত দেয়। void মানে কিছুই না।
  • print_header হলো নাম। Variable-এর নামের যে নিয়ম, এর নামেরও সেই নিয়ম।
  • (void) হলো parameter list: যে call করছে, তার কাছ থেকে function-এর কী লাগবে। এখানে void মানে কিছুই নেয় না।
  • Brace দুইটার ভিতরে থাকে body, মানে সেই statement-গুলো যেগুলো প্রতিবার call হলে চলে।
  • print_header(); হলো একটা call: নাম, তারপর (), তারপর একটা semicolon।

শুধু definition লিখে রাখলে কিছুই ঘটে না: function call হলে তবেই body চলে, প্রতিটা call-এ একবার করে।

তার মানে void print_header(void) পড়তে হয় এভাবে: print_header নামের একটা function, যে কিছু নেয় না, কিছু ফেরতও দেয় না। দুই জায়গাতেই আসল type বসবে lesson 2-এ।

Call মানে ভিতরে লাফ, তারপর ফিরে আসা

main একটা call-এ পৌঁছালে কী ঘটে? দুইটা call আছে, এমন একটা ছোট program দেখো।

#include <stdio.h>

void print_header(void)
{
    printf("Maria General Store\n");
    printf("VAT 15 percent included\n");
}

int main(void)
{
    printf("till on\n");
    print_header();
    printf("sale: 40\n");
    print_header();
    printf("till off\n");
    return 0;
}
till on
Maria General Store
VAT 15 percent included
sale: 40
Maria General Store
VAT 15 percent included
till off

চলন্ত program যেকোনো মুহূর্তে ঠিক একটা লাইনে থাকে। Programmer-রা বলে, control আছে ওই লাইনে। Program শুরু হয় main থেকে, যদিও print_header লেখা আছে আগে।

main-এর ভিতরে control এক লাইন এক লাইন করে নিচে নামে। একটা call ওকে পাঠিয়ে দেয় function-এর body-র প্রথম লাইনে। Body-র শেষ brace-এ পৌঁছে control ফিরে আসে call-এর ঠিক পরের লাইনে।

Control লাফ দিয়ে print_header-এ ঢোকে আর ফিরে আসে, দুইবার একটা function, দুইটা call: নম্বর ধরে এগোও void print_header(void) { printf("Maria General Store\n"); printf("VAT 15 percent included\n"); } ② ⑤ int main(void) { printf("till on\n"); print_header(); printf("sale: 40\n"); print_header(); printf("till off\n"); return 0; } ① ④ ③ ⑥ ① ④ call লাফ দিয়ে উপরে print_header-এ ঢোকে ② ⑤ ওর body উপর থেকে নিচে চলে ③ ⑥ শেষ brace control-কে call-এর পরের লাইনে ফেরত পাঠায়

একই run এবার table-এ, প্রতিটা লাইনের জন্য একটা সারি। গোল নম্বরগুলো ছবির নম্বরের সাথে মেলে।

ধাপযে লাইন চলছেযা ছাপা হয়
1main-এর printf("till on\n");till on
2প্রথম print_header();, লাফ ①এখনো কিছু না
3body-র প্রথম printf ②Maria General Store
4body-র দ্বিতীয় printf ②VAT 15 percent included
5body-র শেষ brace, লাফ ③কিছু না
6main-এর printf("sale: 40\n");sale: 40
7দ্বিতীয় print_header();, লাফ ④এখনো কিছু না
8আবার body-র প্রথম printf ⑤Maria General Store
9আবার body-র দ্বিতীয় printf ⑤VAT 15 percent included
10body-র শেষ brace, লাফ ⑥কিছু না
11main-এর printf("till off\n");till off
12main-এর return 0;কিছু না: program শেষ

Body-র লাইন দুইটা চলেছে দুইবার, অথচ লেখা হয়েছে একবার। main কখনো নিজের জায়গা হারায়নি: প্রতিবার call-এর পর থেকেই আবার চলেছে।

তার মানে call হলো ফেরার টিকিটসহ একটা লাফ। Control ভিতরে ঢোকে, পুরো body চালায়, তারপর পরের লাইনে ফিরে আসে।

main-ও একটা function

int main(void)-ও একটা function definition, আকারে ঠিক print_header-এর মতো।

int বলছে ও একটা পূর্ণসংখ্যা ফেরত দেয়। কিন্তু main-কে call করে কে? তোমার file-এর কোথাও কেউ করে না। Program চালু হওয়ার সময় ওকে call করে operating system (Linux, Windows, মানে যে software বাকি সব program চালায়)।

ওর return value-ই হয়ে যায় program-এর exit code, Module 1 যাকে exit status বলেছিল। 0 মানে "ঠিকঠাক শেষ হয়েছে", আর অন্য যেকোনো সংখ্যা মানে কোথাও গোলমাল হয়েছে।

#include <stdio.h>

int main(void)
{
    printf("receipt printed\n");
    return 3;
}
receipt printed

GCC 12-এ চালালে program নিজের লাইনটা ছাপে, তারপর exit code 3 দিয়ে শেষ হয়। Playground-এর output panel-এ দেখায় Exit: 3, আর badge-এ লেখা আসে Runtime error। ছাপার কাজ ঠিকঠাকই হয়েছিল, কিন্তু 3 জানিয়ে দিয়েছে যে কিছু একটা ব্যর্থ হয়েছে।

Judge-এর কাছে 0 মানে program ঠিকঠাক শেষ হয়েছে, তাই এই track-এর প্রতিটা program শেষ হয় return 0; দিয়ে। Module 1 দেখিয়েছিল, main চাইলে ওই লাইনটা বাদ দিতে পারে। শুধু main-ই কেন পারে, সেই প্রশ্ন রাখা আছে মাথা খাটাও অংশে।

তার মানে main একটা সাধারণ function-ই, শুধু ওকে যে call করে সে একটু বিশেষ।

একটা function আরেকটা function-কে call করতে পারে

main-এ যা লেখা যায়, function-এর body-তেও তা-ই লেখা যায়, call-সহ। এখানে print_receipt ছোট দুইটা function জুড়ে পুরো একটা receipt বানায়।

#include <stdio.h>

void print_header(void)
{
    printf("Maria General Store\n");
    printf("VAT 15 percent included\n");
}

void print_footer(void)
{
    printf("Thank you, come again\n");
}

void print_receipt(void)
{
    print_header();
    printf("tea: 40\n");
    print_footer();
}

int main(void)
{
    print_receipt();
    printf("till off\n");
    return 0;
}
Maria General Store
VAT 15 percent included
tea: 40
Thank you, come again
till off

ছবিতে যেভাবে গুনেছিলে, সেভাবেই লাফগুলো গুনে গুনে দেখো control কোথায় কোথায় যায়।

  1. main call করে print_receipt-কে: লাফ 1।
  2. print_receipt call করে print_header-কে: লাফ 2। Header ছাপা হয়।
  3. print_header শেষ হয়: লাফ 3, ফিরে যায় print_receipt-এ, যেটা ছাপে tea: 40।
  4. print_receipt call করে print_footer-কে: লাফ 4। ধন্যবাদের লাইনটা ছাপা হয়।
  5. print_footer শেষ হয়: লাফ 5, ফিরে যায় print_receipt-এ।
  6. print_receipt শেষ হয়: লাফ 6, ফিরে যায় main-এ, যেটা ছাপে till off।

ভিতরে ঢোকার প্রতিটা লাফের সাথে ফেরার একটা লাফ থাকে। যে function সবার শেষে call হয়, সে-ই সবার আগে শেষ হয়; Module 8 এটাকে একটা stack হিসেবে আঁকবে।

খেয়াল করো, print_header আর print_footer বসে আছে print_receipt-এর উপরে, যে ওদের call করে। কেন, সেটা পরের অংশে।

তার মানে function দিয়েই function বানানো যায়, আর control যে পথে যায়, সেই পথেই ফিরে আসে।

Call করার আগে define করো

Compiler তোমার file একবারই পড়ে, উপর থেকে নিচে। কোনো call-এ পৌঁছানোর আগেই function-টা ওর চেনা থাকা চাই। Playground একটাই file compile করে, তাই সবকিছু থাকে ওই file-এ, ক্রম মেনে।

Bob main-কে উপরে রাখতে ভালোবাসে, কারণ program তো ওখান থেকেই শুরু হয়। তাই ও print_header-কে নিচে নামিয়ে দেয়।

#include <stdio.h>

int main(void)
{
    print_header();
    printf("sale: 40\n");
    return 0;
}

void print_header(void)
{
    printf("Maria General Store\n");
    printf("VAT 15 percent included\n");
}

Playground দুইটা warning ছাপে, তারপরও program চালিয়ে দেয়:

  • warning: implicit declaration of function 'print_header' [-Wimplicit-function-declaration]
  • warning: conflicting types for 'print_header'; have 'void(void)'

Declaration প্রথম ব্যবহারের আগেই compiler-কে একটা function-এর নাম আর আকার জানিয়ে রাখে। Bob-এর call এসেছে আগে, তাই GCC 12 আন্দাজ করে নিয়েছে, 1989 সালের C-এর একটা নিয়ম ধরে: অচেনা function একটা int ফেরত দেয়। এই আন্দাজটাকে বলে implicit declaration।

তারপর compiler আসল definition-টা পেয়েছে, যেটা ফেরত দেয় void, আর দ্বিতীয় warning ওই অমিলটাই জানায়। Program-টা এখানে চলছে কপালের জোরে। GCC 14 আর আন্দাজই করে না: প্রথম warning-টা হয়ে যায় error: implicit declaration of function 'print_header' [-Wimplicit-function-declaration], আর build ব্যর্থ হয়।

তাই আপাতত নিয়ম একটাই: যে লাইন একটা function-কে প্রথম call করে, function-টা define করো তার উপরে। Lesson 2-এ আসবে prototype, এক লাইনের declaration, যেটা থাকলে main আবার সবার উপরে বসতে পারে।

Parameter-এর সাথে প্রথম দেখা

এতক্ষণ একটা function প্রতিটা call-এ হুবহু একই কাজ করেছে। Kenji একটা function দিয়েই নানা দৈর্ঘ্যের লাইন আঁকতে চায়।

#include <stdio.h>

void draw_line(int width)
{
    for (int i = 0; i < width; i++) {
        printf("-");
    }
    printf("\n");
}

int main(void)
{
    draw_line(10);
    printf("SCORES\n");
    draw_line(6);
    return 0;
}
----------
SCORES
------

int width হলো একটা parameter: function-এর নিজের একটা variable, যার মান আসে call থেকে। Call-এ যে মানটা লেখা থাকে, সেটাকে বলে argument। draw_line(10) body চালায় width 10 ধরে, আর draw_line(6) আবার চালায় 6 ধরে।

একটা function কমা দিয়ে আলাদা করা একাধিক parameter নিতে পারে, যেমন void print_row(int i, int n)। Call থেকে মানটা ঠিক কীভাবে function-এর ভিতরে যায়, সেটা বুঝিয়ে বলবে lesson 2। অনুশীলন 2 আর 3-এর জন্য এটুকু জানলেই চলবে।

তার মানে parameter থাকলে একটাই function প্রতিটা call-এ আলাদা মান নিয়ে নিজের কাজটা করতে পারে।

Function-এর নাম দেওয়া

Function-এর নামই পাঠককে বলে দেয়, ও কী করে। নামটা হবে একটা কাজের কথা, ইংরেজি verb দিয়ে শুরু, ছোট হাতের অক্ষরে, শব্দগুলো underscore দিয়ে জোড়া: print_header, draw_line, read_score। এই ধরনটাকে বলে snake_case।

যে function হ্যাঁ-না প্রশ্নের উত্তর দেয়, তার নাম শুরু হয় is_ দিয়ে, যেমন lesson 2-এর is_leap। f1 বা stuff-এর মতো নাম দেখলে কিছু বুঝতে হলে পাঠককে body খুলে পড়তেই হয়। ভালো নাম থাকলে body না পড়লেও চলে।

printf আর scanf-ও function, এসেছে C-এর standard library থেকে, যেটা প্রতিটা C installation-এই থাকে। তোমার প্রথম call-এর আগেই #include <stdio.h> compiler-এর সাথে ওদের পরিচয় করিয়ে দেয়।

তাই নামটা এমন দাও, যেন নামেই বোঝা যায় function কী করে।

Example 1: সবচেয়ে ছোট function

কাজের সবচেয়ে ছোট function: একটা লাইন, main থেকে দুইবার call করা।

#include <stdio.h>

void say_hello(void)
{
    printf("Hello from a function\n");
}

int main(void)
{
    say_hello();
    say_hello();
    return 0;
}
Hello from a function
Hello from a function

দশবার call করলে দশটা লাইন ছাপবে, আর function তখনো লেখা থাকবে একবারই।

Run in Compiler
Example 2: Maria-র receipt, header আর footer

তিনটা receipt, প্রতিটা একই header আর footer দিয়ে বানানো। বদলায় শুধু জিনিসের লাইনটা।

#include <stdio.h>

void print_header(void)
{
    printf("Maria General Store\n");
    printf("VAT 15 percent included\n");
}

void print_footer(void)
{
    printf("Thank you, come again\n");
    printf("---------------------\n");
}

int main(void)
{
    print_header();
    printf("tea x 2: 40\n");
    print_footer();

    print_header();
    printf("cake x 1: 50\n");
    print_footer();

    print_header();
    printf("rice x 3: 360\n");
    print_footer();

    return 0;
}
Maria General Store
VAT 15 percent included
tea x 2: 40
Thank you, come again
---------------------
Maria General Store
VAT 15 percent included
cake x 1: 50
Thank you, come again
---------------------
Maria General Store
VAT 15 percent included
rice x 3: 360
Thank you, come again
---------------------

দোকানের নাম বদলাতে এখন print_header-এর একটা লাইন বদলালেই হয়। main-এ থাকে শুধু জিনিসের লাইনগুলো।

Run in Compiler
Example 3: নতুন শিখছে এমন কেউ আসলে যে scoreboard লেখে

Kenji-র game ছাপে একটা title, প্রতি round-এ একটা লাইন, আর শেষে মোট। Score পড়ে input শেষ হওয়া পর্যন্ত, Module 6-এর সেই চেনা নিয়মে।

#include <stdio.h>

void draw_line(int width)
{
    for (int i = 0; i < width; i++) {
        printf("=");
    }
    printf("\n");
}

int main(void)
{
    int score = 0;
    int round = 0;
    int total = 0;

    draw_line(20);
    printf("KENJI'S SCOREBOARD\n");
    draw_line(20);

    while (scanf("%d", &score) == 1) {
        round++;
        total += score;
        printf("round %d: %d\n", round, score);
    }

    draw_line(20);
    printf("total: %d\n", total);
    return 0;
}
====================
KENJI'S SCOREBOARD
====================
round 1: 120
round 2: 85
round 3: 240
====================
total: 445

ওই output-টা 120 85 240 input-এর জন্য। Input ফাঁকা হলেও title আর total: 0 ঠিকই ছাপা হয়। draw_line(20) call হয়েছে তিনবার, তাই লাইনের নকশা বদলাতে হাত দিতে হয় একটাই জায়গায়।

Run in Compiler

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

  • C standard library। printf আর scanf তোমার function-গুলোর মতোই function, library-র লেখকেরা একবার লিখে রেখেছেন। কিছু ছাপে এমন প্রতিটা C program ওদের call করে।
  • Linux kernel। এটা হাজার হাজার ছোট ছোট, নাম দেওয়া function দিয়ে গড়া। Kernel printf ব্যবহার করতে পারে না, তাই ওর নিজের একটা ছাপার function আছে, printk, যেটা kernel-এর log-এ লেখে।
  • SQLite। কোনো program এই database ব্যবহার করে sqlite3_open আর sqlite3_exec-এর মতো C function call করে।
  • একটা সাধারণ game loop। প্রতিটা frame-এর জন্য একবার, মানে সেকেন্ডে অনেকবার, ওটা একটা আঁকার function call করে, যার নাম প্রায়ই draw_frame-এর মতো কিছু।

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

১. যে function নিচে define করা, সেটাকে call করা।

int main(void)
{
    print_header();
    printf("tea: 40\n");
    print_footer();
    return 0;
}

void print_footer(void)
{
    printf("Thank you, come again\n");
}

Playground ছাপে warning: implicit declaration of function 'print_footer'; did you mean 'print_header'? [-Wimplicit-function-declaration], তারপরও program চালায়। এর পরেই আসে conflicting types warning, আর GCC 14 build-ই করে না। Definition-টা main-এর উপরে তুলে দাও। এই ভুলটা হবেই, কারণ নতুন function-এর জায়গা মনে হয় file-এর একদম নিচে।

২. Parenthesis ছাড়া call।

print_header;
printf("tea: 40\n");

Playground-এ নীরব, আর header কখনো ছাপাই হয় না। নিজের মেশিনে GCC 12-এর gcc -Wall বলে warning: statement with no effect [-Wunused-value]। লেখো print_header();। শুধু নাম লিখলে function-টার কথা বলা হয় মাত্র; call করে parenthesis। খালি নামটা পড়তে হুকুমের মতো শোনায়, আর Playground-ও কোনো আপত্তি তোলে না।

৩. Definition-এর প্রথম লাইনের পরে semicolon।

void print_header(void);
{
    printf("Maria General Store\n");
}

প্রতিটা command line-এ error, Playground-সহ: error: expected identifier or '(' before '{' token। (void)-এর পরের semicolon-টা মুছে দাও। প্রতিটা statement semicolon দিয়ে শেষ হয়, তাই মনে হয় এই লাইনেরও একটা লাগবে। লাগে না: body আসে ঠিক এর পরেই।

৪. main-এর ভিতরে define করা function।

int main(void)
{
    void print_header(void)
    {
        printf("Maria General Store\n");
    }

    print_header();
    printf("tea: 40\n");
    return 0;
}

কোনো command line-এই বার্তা নেই, -Wall -Wextra-তেও না, আর program চলেও। GCC এটাকে একটা extension হিসেবে মেনে নেয়, মানে এমন একটা সুবিধা যেটা standard C-তে নেই। -pedantic দিলে GCC 12 বলে warning: ISO C forbids nested functions [-Wpedantic]। আরেকটা compiler, Clang 18, সোজা থেমে যায় error: function definition is not allowed here বলে। Function-টা main-এর উপরে সরিয়ে নাও। ভুলটা হয় কারণ আজ পর্যন্ত তুমি যা লিখেছ, সবই ছিল main-এর ভিতরে।

মাথা খাটাও

Zara যত ছোট করে পারা যায়, একটা receipt program লেখে। ওতে কোনো return statement-ই নেই।

#include <stdio.h>

int main(void)
{
    printf("receipt printed\n");
}

Judge কোন exit code দেখবে? কঠিন অর্ধেকটা: int ফেরত দেয় এমন অন্য যেকোনো function-এ ঠিক এই লাইনটা না থাকা bug কেন?

এর একটা অংশের উত্তর Module 1-এ আছে। কঠিন অর্ধেকের জন্য ভাবো, এমন একটা function-কে যে call করে, সে হাতে কী পায়।

অনুশীলন ১সহজ

Maria-র চায়ের দোকানেও একই ঝামেলা হয়েছিল: receipt-এর banner-এর তিনটা কপি, আর তার একটা পুরোনো থেকে গিয়েছিল। এখন একটাই function banner ছাপে।

Input. বিক্রির টাকার অঙ্ক, পূর্ণসংখ্যা, input শেষ হওয়া পর্যন্ত। একটাও না থাকতে পারে।

Output. প্রতিটা অঙ্কের জন্য banner-এর দুই লাইন *** MARIA'S TEA HOUSE *** আর VAT included, তারপর total: <amount>। শেষ receipt-এর পরে receipts: <k>, যেখানে k হলো অঙ্কের সংখ্যা (ফাঁকা input-এ 0)। Judge শুধু output দেখে। Banner-টা লেখো void print_banner(void) হিসেবে।

Constraints. সর্বোচ্চ 1000-টা অঙ্ক, প্রতিটা 1 থেকে 1000000-এর মধ্যে।

Sample. Input 120 45 দিলে এই সাতটা লাইন আসে।

*** MARIA'S TEA HOUSE ***
VAT included
total: 120
*** MARIA'S TEA HOUSE ***
VAT included
total: 45
receipts: 2
#include <stdio.h>

void print_banner(void)
{
    /* Print the two banner lines. */
}

int main(void)
{
    int amount = 0;

    while (scanf("%d", &amount) == 1) {
        /* Call print_banner, then print the total line. */
    }

    /* Print "receipts: k", where k counts the amounts read. */

    return 0;
}

receipt-banner নামে গ্রেড হয়। Hidden test-গুলোয় আছে ফাঁকা input আর 1000-টা receipt।

Run in Compiler
অনুশীলন ২মাঝারি

Zara একটা গণিত club-এর জন্য নামতার কার্ড বানায়। একটা function একটা সারি ছাপে, আর main প্রতিটা সারির জন্য ওটাকে একবার করে call করে।

Input. একটা পূর্ণসংখ্যা n।

Output. n-টা লাইন। i নম্বর লাইনে থাকে i x 1 থেকে i x n পর্যন্ত, পাশাপাশি দুইটা সংখ্যার মাঝে একটা space। একটা লাইন ছাপার জন্য লেখো void print_row(int i, int n)।

Constraints. 1 <= n <= 12।

Sample. Input 3 দিলে এই তিনটা লাইন আসে।

1 2 3
2 4 6
3 6 9
#include <stdio.h>

void print_row(int i, int n)
{
    /* Print i*1, i*2, and so on up to i*n, one space between two numbers. */
}

int main(void)
{
    int n = 0;

    scanf("%d", &n);

    /* Call print_row once for each i from 1 to n. */

    return 0;
}

আলাদা করে গ্রেড হয় না। Table-টা Module 6-এর aligned-table আগেই গ্রেড করে, আর call-গুলো গ্রেড করে receipt-banner আর star-box।

Run in Compiler
অনুশীলন ৩কঠিন

Kenji-র game প্রতিটা score panel-এর চারপাশে তারা দিয়ে একটা ফ্রেম আঁকে। একটা function পুরো একটা বাক্স আঁকে।

Input. Input শেষ হওয়া পর্যন্ত width height জোড়া, অন্তত একটা জোড়া থাকবেই।

Output. প্রতিটা জোড়ার জন্য একটা বাক্স, যার ভিতরটা ফাঁকা। প্রথম আর শেষ সারিতে width-টা তারা। মাঝের প্রতিটা সারি হলো একটা তারা, width - 2-টা space, তারপর আরেকটা তারা। 1 চওড়া বাক্স মানে প্রতি সারিতে একটাই তারা, আর 1 উঁচু বাক্স মানে একটাই সারি। দুইটা বাক্সের মাঝে একটা ফাঁকা লাইন ছাপো, প্রথম বাক্সের আগে কিছু না।

Judge শুধু output দেখে। লেখো void draw_box(int width, int height)। চাইলে void draw_full_row(int width) আর void draw_hollow_row(int width)-এর মতো helper function-ও লিখতে পারো।

Constraints. 1 <= width, height <= 50। সর্বোচ্চ 20-টা জোড়া।

Sample. Input 4 3, তারপর 1 2 দিলে এই ছয়টা লাইন আসে।

****
*  *
****

*
*
#include <stdio.h>

void draw_box(int width, int height)
{
    /* Full rows at the top and the bottom, a star at each end
       of every row in between. What if width or height is 1? */
}

int main(void)
{
    int width = 0;
    int height = 0;

    while (scanf("%d %d", &width, &height) == 2) {
        /* Print one empty line before every box except the first. */
        draw_box(width, height);
    }
    return 0;
}

star-box নামে গ্রেড হয়। Hidden test-গুলোয় আছে 1 চওড়া আর 1 উঁচু বাক্স, আর সবচেয়ে বড় বাক্স।

Run in Compiler

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

  • Function কি program-কে ধীর করে দেয়?

    প্রশ্নটা সবার আগে করে Kenji। টের পাওয়ার মতো একদমই না। Playground-এর setting -O2-তে GCC 12 খুব ছোট একটা function-এর body সরাসরি যে call করে তার ভিতরে বসিয়ে দেয়, একে বলে inlining। Example 1-এর main শেষমেশ লাইন দুইটা নিজেই ছাপে, say_hello-কে একবারও call করে না। একটু বড় print_header-এর call-গুলো থেকে যায়, আর প্রতিটা লাফে খরচ মাত্র কয়েকটা machine instruction।

  • আমার code কি main-কে call করতে পারে?

    C-তে এটা বৈধ। তবু কেউ করে না, কারণ তাতে program-এর গল্প আবার গোড়া থেকে শুরু হয়। main-কে call করার কাজটা operating system-এর জন্যই রেখে দাও।

  • void দুইবার লেখা কেন?

    দুইটা দুই প্রশ্নের উত্তর দেয়। সামনেরটা বলে কী ফেরত আসে: কিছুই না। ভিতরেরটা বলে কী ঢোকে: কিছুই না। খালি () লিখলেও compile হয়, তবু এই track কেন দ্বিতীয়টা সব সময় লেখে, সেটা দেখাবে lesson 2।

  • দুইটা function-এর কি একই নাম হতে পারে?

    এক program-এ না। দ্বিতীয় একটা print_header লিখলে প্রতিটা command line-এ আসে error: redefinition of 'print_header'। এমন নাম বাছো যা দুইটার তফাতটা বলে দেয়, যেমন print_shop_header আর print_report_header।

মূল কথা

  • Function হলো নাম দেওয়া এক টুকরো কাজ: একবার লেখো, যেখানে দরকার সেখানে call করো।
  • একটা definition-এ থাকে return type, নাম, parameter list আর body। সামনের void মানে কিছু ফেরত দেয় না; (void) মানে কিছু নেয় না।
  • Call লাফ দিয়ে function-এ ঢোকে, পুরো body চালায়, তারপর call-এর পরের লাইনে ফিরে আসে।
  • main হলো সেই function যাকে operating system call করে, আর ওর return value-ই সেই exit code, যেটা judge পড়ে।
  • যে লাইন একটা function-কে প্রথম call করে, function-টা define করো তার উপরে। উল্টো ক্রমে GCC 12 শুধু warning দেয়, আর GCC 14 মানেই না।
  • Function-এর নাম দাও snake_case-এ, একটা কাজের কথা দিয়ে, যেমন print_header বা draw_line।

এরপর function মান নেবে আর একটা মান ফেরত দেবে, আর prototype থাকলে main আবার সবার উপরে বসতে পারবে।

lesson ১ শেষ

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

পরেরটা: Parameter, return value আর prototype