Lesson পড়তে ৬ মিনিট লাগবে

সি (C) ফাংশন (C Functions)

একটি ভেন্ডিং মেশিন (vending machine) — যার ভেতরে কিছু একটি দিন, এবং তার বদলে অন্য আরেকটি জিনিস পেয়ে যান

ভেন্ডিং মেশিন (The Vending Machine)

যেকোনো ফাংশনকে (function) একটি ভেন্ডিং মেশিনের (vending machine) মতো কল্পনা করে নিতে পারেন। আপনি এর ভেতরে কয়েকটি কয়েন বা মুদ্রা দেবেন (আর্গুমেন্ট বা arguments), একটি বোতাম চাপবেন (ফাংশন কল বা function call), এবং এর ভেতর থেকে একটি স্ন্যাকস বা খাবার বেরিয়ে আসবে (রিটার্ন ভ্যালু বা return value)। এখানকার এই মেশিনের ভেতরে থাকা গিয়ার (gears) বা মোটরগুলো (motors) ঠিক কীভাবে ঘোরে বা কাজ করে তা আপনার জানার কোনো প্রয়োজন নেই — আপনার মাথাব্যথা শুধু এর ভেতরে কী যাচ্ছে এবং এর ভেতর থেকে কী বেরিয়ে আসছে তা নিয়েই।

যেকোনো সি (C) প্রোগ্রামে মূলত একটিমাত্র ফাংশন (function) দিয়েই এর সমস্ত কাজ শুরু হয়: সেটি হলো main()। এরপর আপনি মূলত এই main() থেকে শুরু করে আপনার পুরো কোডটিকে (code) ছোট ছোট অনেকগুলো ফাংশনে (functions) ভাগ করে নিতে পারেন — যার প্রত্যেকটিই মূলত এদের নিজস্ব কাজগুলোকে (job) খুব ভালোভাবে সম্পন্ন করতে পারবে।

ফাংশনের গঠন বা অ্যানাটমি (Anatomy of a Function)

আর যেকোনো সি (C) ফাংশনের (function) মূলত এই ৪টি অংশ থাকে:

  • রিটার্ন টাইপ (Return type) — এর ভেতর থেকে মূলত কী ধরনের জিনিস বা স্ন্যাকস বেরিয়ে আসবে (int, float, char, বা কিছুই না এলে void)
  • নাম (Name) — মেশিনের বোতামের ওপরের লেবেলটি (label)
  • প্যারামিটার্স (Parameters) — কয়েন দেওয়ার স্লট বা জায়গাগুলো (যা মূলত এর ভেতরে যায়)
  • বডি (Body) — এই { }-এর ভেতরের মূল মেশিনারি (machinery) বা যন্ত্রপাতিগুলো

একটি সাধারণ বা সিম্পল ম্যাথ ফাংশন (A Simple Math Function)

#include <stdio.h>
// ফাংশন (Function): দুটি int ইনপুট হিসেবে নেয়, এবং তাদের যোগফল বা sum রিটার্ন (returns) করে
int add(int a, int b) {
return a + b;
}
// ফাংশন (Function): একটি int ইনপুট হিসেবে নেয়, এবং এর স্কয়ার বা বর্গ (square) রিটার্ন (returns) করে
int square(int n) {
return n * n;
}
int main() {
int sum = add(3, 7);
printf("3 + 7 = %d\n", sum);
int sq = square(5);
printf("5 squared = %d\n", sq);
// আপনি চাইলে একে নেস্ট (nest) করেও কল (calls) করতে পারেন
printf("sum of squares = %d\n", add(square(3), square(4)));
return 0;
}
Output
3 + 7 = 10
5 squared = 25
sum of squares = 25

ভয়েড ফাংশন (Void Functions) — এর ভেতর থেকে কোনো স্ন্যাকস বেরিয়ে আসে না (No Snack Comes Out)

তবে কিছু কিছু ক্ষেত্রে ভেন্ডিং মেশিন (vending machine) মূলত আপনাকে কোনো ধরনের স্ন্যাকস বা ফলাফল দেয় না — এটি হয়তো শুধুমাত্র ভেতরের কোনো একটি কাজ সম্পন্ন করে (যেমন কোনো গান বা music বাজানো)। একটি void ফাংশন মূলত কোনো প্রকার রিটার্ন ভ্যালু (returning a value) ছাড়াই তার সমস্ত অ্যাকশনগুলোকে (action) সরাসরি সম্পন্ন করে থাকে। তবে আপনি চাইলে এর ভেতর থেকে যেকোনো সময় তাড়াতাড়ি বের হয়ে আসতে (exit early) এখানকার এই return; অপশনটিকে ব্যবহার করতে পারেন, তবে সেক্ষেত্রে আপনি এর ভেতর থেকে কোনো ধরনের ডেটা রিটার্ন (return any data) করতে পারবেন না।

ভয়েড ফাংশনসমূহ (Void Functions)

#include <stdio.h>
void greet(char name[]) {
printf("Hello, %s! Welcome aboard.\n", name);
}
void printLine(int length) {
for (int i = 0; i < length; i++) {
printf("-");
}
printf("\n");
}
int main() {
printLine(30);
greet("Alice");
greet("Bob");
printLine(30);
return 0;
}
Output
------------------------------
Hello, Alice! Welcome aboard.
Hello, Bob! Welcome aboard.
------------------------------

ফাংশন প্রোটোটাইপ (Function Prototypes) — ব্যবহারের আগে ডিক্লেয়ার বা ঘোষণা করা (Declare Before You Use)

সি (C) মূলত আপনার ফাইলটিকে (file) উপর থেকে নিচের দিকে (top to bottom) পড়তে থাকে। তাই যদি main() এমন কোনো ফাংশনকে কল (calls) করে যাকে এর নিচে কোথাও সংজ্ঞায়িত বা ডিফাইন (defined) করে রাখা হয়েছে, তবে এখানকার ওই কম্পাইলারটি (compiler) তা আর খুঁজে পাবে না (hasn't seen it yet) এবং আপনাকে এররার (error) বা কমপ্লেন (complain) দিতে শুরু করবে। এর সমাধানটি (fix) কী? এর শুরুতে বা একেবারে ওপরে (top) একটি প্রোটোটাইপ (prototype) (বা ফরওয়ার্ড ডিক্লেয়ারেশন বা forward declaration) বসিয়ে দেওয়া। এটি মূলত একটি সাধারণ সেমিকোলন (semicolon) দিয়ে শেষ হওয়া ফাংশন সিগনেচার (function signature) ছাড়া আর কিছুই না — যা সি (C)-কে এটুকু ভরসা (promise) দেয় যে, এর সম্পূর্ণ ডেফিনিশনটি (definition) বা সংজ্ঞাটি সামনে বা পরে কোথাও না কোথাও অবশ্যই দেওয়া আছে।

সি (C)-তে মূলত এই পদ্ধতিটিকেই সবচেয়ে বেশি ফল (standard practice) করা হয়: এর শুরুতে প্রোটোটাইপগুলো (prototypes) রাখা হয় (অথবা যেকোনো হেডার বা header ফাইলের ভেতরে), এবং main()-এর নিচে এর প্রমাণ বা ডেফিনিশনগুলোকে (definitions) রাখা হয়।

ফাংশন প্রোটোটাইপসমূহ (Function Prototypes)

#include <stdio.h>
// প্রোটোটাইপগুলো (Prototypes) — যা কম্পাইলারকে (compiler) দেওয়া এক ধরনের ভরসা (promises) বা প্রতিশ্রুতি
int max(int a, int b);
int clamp(int value, int low, int high);
int main() {
printf("Max of 10, 20: %d\n", max(10, 20));
printf("Clamp 150 to [0,100]: %d\n",
clamp(150, 0, 100));
printf("Clamp 50 to [0,100]: %d\n",
clamp(50, 0, 100));
return 0;
}
// ডেফিনিশনগুলো (Definitions) — এর আসল বা অ্যাকচুয়াল মেশিনারিগুলো (machinery)
int max(int a, int b) {
return (a > b) ? a : b;
}
int clamp(int value, int low, int high) {
if (value < low) return low;
if (value > high) return high;
return value;
}
Output
Max of 10, 20: 20
Clamp 150 to [0,100]: 100
Clamp 50 to [0,100]: 50
Note: সি (C) হলো মূলত ডিফল্টভাবে (default) একটি পাস-বাই-ভ্যালু (pass-by-value) ভাষা। তাই যখন আপনি কোনো ভ্যারিয়েবলকে (variable) একটি ফাংশনে পাস (pass) করেন, তখন এখানকার ওই ফাংশনটি মূলত শুধু এর একটি কপি (copy) বা অনুলিপিই রিসিভ (receives) করে থাকে। তাই এই ফাংশনের ভেতরে বসে এর ওই কপিটিকে (copy) পরিবর্তন বা মডিফাই (Modifying) করলে তা এর আসল বা অরিজিনাল (original) ভ্যারিয়েবলটির ওপর 0% বা জিরো (zero effect) প্রভাব ফেলে থাকে। আপনি যদি চান যে এখানকার ওই ফাংশনটি কলকারী (caller) বা কলারের ওই প্রধান ভ্যারিয়েবলটিকে সরাসরি পরিবর্তন (change) করুক, তবে আপনাকে এর বদলে একটি পয়েন্টারকে (pointer) পাস (pass) করতে হবে — যেটি নিয়ে মূলত আমরা পয়েন্টার (pointers) লেসনে বা অধ্যায়ে বিস্তারিত আলোচনা করব।

পাস-বাই-ভ্যালু-এর ব্যবহার (Pass-by-Value in Action)

#include <stdio.h>
void tryToChange(int x) {
x = 999; // এটি শুধুমাত্র এখানকার লোকাল কপিটিকে বা local copy-টিকেই পরিবর্তন (changes) করে!
printf("Inside function: x = %d\n", x);
}
int main() {
int num = 42;
printf("Before call: num = %d\n", num);
tryToChange(num);
printf("After call: num = %d\n", num);
// এখানেও num-এর মানটি এখনো (STILL) 42-ই আছে!
return 0;
}
Output
Before call: num = 42
Inside function: x = 999
After call:  num = 42

কোডকে (Code) ফাংশনে (Functions) ভাগ করা হয় কেন (Why Break Code Into Functions?)

ফাংশনগুলো (Functions) মূলত শুধু বারবার একই কোড (repeated code) লেখাকেই প্রতিরোধ (avoiding) করে না (যদিও এটি একটি বড় জয় বা big win)। বরং এটি মূলত আপনাকে আরও যে সমস্ত কাজগুলো করার সুযোগ দেয়, তা নিচে দেওয়া হলো:

  • এর অনেকগুলো ছোট ছোট লজিকের (logic) চাঙ্ককে বা টুকরোকে একটি নাম দেওয়া (Name) — ১৫ লাইনের ম্যাথ (15 lines of math) বা জটিল হিসাব-নিকাশের বদলে শুধুমাত্র এই calculateTax(income) লেখাটুকু দেখতে বা বুঝতে অনেক বেশি পরিষ্কার বা ক্লিয়ার (clearer) মনে হয়
  • যেকোনো ছোট ছোট অংশগুলোকে স্বাধীনভাবে বা ইন্ডিপেন্ডেন্টলি (independently) টেস্ট করা (Test) — সম্পূর্ণ প্রোগ্রামটি রান না করিয়েও (without running the whole program) আপনি খুব সহজেই চেক (verify) করে দেখতে পারেন যে এখানকার এই max()-টি ঠিকমতো কাজ করছে কি না
  • কাজের ভাগ (Divide work) — যেকোনো দলের বা টিমের আলাদা আলাদা সদস্যরা (different team members) চাইলে একই সাথে বিভিন্ন বা ডিফারেন্ট (different) ফাংশনগুলোর নিয়ে কাজ করতে পারে
  • স্কোপ লিমিট (Limit scope) — একটি নির্দিষ্ট ফাংশনের ভেতরের ভ্যারিয়েবলগুলো (variables) কখনোই এর বাইরে গিয়ে কোনো প্রকার কনফিউশন (confusion) বা সমস্যার সৃষ্টি করতে পারে না

যেকোনো ভালো মানের সি (C) কোড বা Good C code মূলত এমন কিছু ছোট ছোট, এবং সুনির্দিষ্ট (focused) ফাংশনের একটি চমৎকার সংগ্রহ বা কালেকশন (collection) ছাড়া আর কিছুই না。

চ্যালেঞ্জ

ছোট কুইজ

একটি void রিটার্ন টাইপ (return type) বলতে মূলত কী বোঝায়?
Strings & CharactersPointers