একটি ব্লক হলো একগুচ্ছ স্টেটমেন্ট ও ডিক্লারেশন, যা একটি সিনট্যাকটিক একক হিসেবে গ্রুপ করা হয়। ব্লক সাধারণত দ্বিতীয় বন্ধনীর ভেতরে ডিফাইন করা হয়, তবে সবসময় নয়। ব্লক ফাংশনের ভেতরে এবং অন্য ব্লকের ভেতরে নেস্টেড হতে পারে।
নিচের উদাহরণে number ডিফাইন করা হয়েছে main ফাংশনের ভেতরে। flag ডিফাইন করা হয়েছে if ব্লকে, যা main ফাংশনের ভেতরে নেস্টেড। কোনো ব্লকের ভেতরে ডিফাইন করা ভ্যারিয়েবল শুধু সেই ব্লকের ভেতরেই বৈধ। তাই কোডটি কম্পাইল করার চেষ্টা করলে একটি কম্পাইলেশন এরর দেখা যায়।
#include <stdio.h>
int main() {
int number = 1;
if (number == 1) {
int flag = 1;
printf("%d", flag);
}
// this line generates a compilation error
// error: 'flag' undeclared (first use in this function)
printf("%d", flag);
}
এরর মেসেজটি বিভ্রান্তিকর লাগতে পারে, কারণ flag ফাংশনের আগের অংশে ব্যবহার করা হয়েছে। কিন্তু flag ডিফাইন করা হয়েছে ফাংশনের ভেতরের একটি ব্লকে। ভেতরের ব্লকটি তার বাইরের ফাংশনের number ভ্যারিয়েবলটি "দেখতে" পারে, কিন্তু বাইরের ফাংশনটি তার ভেতরের ব্লকে ডিফাইন করা flag ভ্যারিয়েবলটি "দেখতে" পারে না। একবার এক্সিকিউশন ব্লকটি ছেড়ে গেলে, flag ভ্যারিয়েবলটি স্বয়ংক্রিয়ভাবে ধ্বংস হয়ে যায় এবং আর প্রোগ্রামের জন্য উপলব্ধ থাকে না। main ফাংশন নিজে শেষ printf স্টেটমেন্টে গিয়ে প্রথমবারের মতো flag আইডেন্টিফায়ারটি দেখে। শেষ printf স্টেটমেন্টটি সরিয়ে দিলে, প্রোগ্রামটি কম্পাইল হবে এবং রান করলে 1 প্রিন্ট করবে। এর কারণ, প্রথম printf স্টেটমেন্টটি যে ব্লকে আছে সেই ব্লকে flag ভ্যারিয়েবলটি বৈধ।
C99 থেকে শুরু করে, এক লাইনের if, for, while ও do স্টেটমেন্টকেও ব্লক হিসেবে গণ্য করা হয়। নিচের উদাহরণে, i দ্বিতীয় বন্ধনীর ভেতরে ডিফাইন করা না হলেও এটি কেবল for স্টেটমেন্ট দ্বারা ডিফাইন করা ব্লকটিতেই বিদ্যমান থাকে, আর বাইরের main ফাংশনের কাছে এটি দৃশ্যমান নয়।
#include <stdio.h>
int main() {
for (int i = 0, j = 11; i < j; i+=2)
printf("%d", i);
// this line generates a compilation error
// error: 'i' undeclared (first use in this function)
printf("%d", i);
}
শেষ printf স্টেটমেন্টটি সরিয়ে দিলে, প্রোগ্রামটি কম্পাইল হবে এবং রান করলে 0 2 4 6 8 10 প্রিন্ট করবে। কিছু ভাষা, যেমন Go ও Rust, স্পষ্টভাবে বলে যে এক লাইনের শর্তভিত্তিক ও লুপ স্টেটমেন্টও একটি ব্লক গঠন করে। তারা দাবি করে যে এমন এক লাইনের স্টেটমেন্টও দ্বিতীয় বন্ধনীর ভেতরে রাখতে হবে।
একটি ফাংশন অনেকটা একটি ব্লকের মতো কাজ করে। একটি ফাংশনে ডিফাইন করা ভ্যারিয়েবলের আইডেন্টিফায়ার ফাংশনের বাইরে থেকে দেখা যায় না, ঠিক যেমন একটি ব্লকে ডিফাইন করা ভ্যারিয়েবলের আইডেন্টিফায়ার ব্লকের বাইরে থেকে দেখা যায় না। দুটি ফাংশনের দুটি ভ্যারিয়েবলের একই নাম থাকতে পারে, কারণ দুটি ফাংশন একটির ভেতরে আরেকটি দেখতে পায় না, ঠিক যেমন দুটি ব্লকের দুটি ভ্যারিয়েবলের একই নাম থাকতে পারে, কারণ দুটি ব্লক একটির ভেতরে আরেকটি দেখতে পায় না। কিন্তু ফাংশন ও ব্লক এক নয়। একটি ফাংশনের ভেতরে আরেকটি ফাংশন ডিফাইন করা যায় না, কিন্তু একটি ব্লক আরেকটি ব্লকের ভেতরে নেস্টেড করা যায়। আর একটি ব্লকের কিছু আইডেন্টিফায়ার ব্লকের বাইরে থেকেও দেখা যায়। নিচের উদাহরণে, কোডটি কম্পাইল হবে না
#include <stdio.h>
int main() {
{
int i = 0;
begin:
printf("i == %d\n", i);
if (i == 0) {
i = 1;
goto begin;
}
}
{
int i = 1;
begin:
printf("i == %d\n", i);
if (i == 1) {
i = 0;
goto begin;
}
}
}
উপরের কোডটি নিচের এররটি দিয়ে কম্পাইল করতে ব্যর্থ হয়
/tmp/gH2TVYnTSJ.c: In function 'main':
/tmp/gH2TVYnTSJ.c:17:5: error: duplicate label 'begin'
17 | begin:
| ^~~~~
/tmp/gH2TVYnTSJ.c:7:5: note: previous definition of 'begin' was here
7 | begin:
| ^~~~~
যদিও i প্রতিটি ব্লকে ডিফাইন করা যায়, label নামগুলো একটি ফাংশনে অনন্য হতে হবে। এর কারণ, labels-এর স্কোপ ফাংশন-ব্যাপী, অর্থাৎ ফাংশনের যেখানেই একটি লেবেল ডিফাইন করা হোক না কেন, ফাংশনটি তা দেখতে পায়। তাই ভ্যারিয়েবলের মতো নয়, একটি ফাংশনে label যেখানেই ডিফাইন করা হোক, সেই একই ফাংশনের ভেতরে তার নাম অনন্য হতে হবে। কোডটি কাজ করাতে হলে labels-এর একটির নাম বদলাতে হবে যাতে দুটোই অনন্য হয়, যেমনটি নিচের উদাহরণে দেখানো হয়েছে
#include <stdio.h>
int main() {
{
int i = 0;
begin:
// prints
// i == 0
// i == 1
printf("i == %d\n", i);
if (i == 0) {
i = 1;
goto begin;
}
}
{
int i = 1;
start:
// prints
// i == 1
// i == 0
printf("i == %d\n", i);
if (i == 1) {
i = 0;
goto start;
}
}
}
একলাইনের স্টেটমেন্ট "স্তূপ" করে রাখা যায় এবং তবুও দ্বিতীয় বন্ধনী ব্যবহার না করেই একটি ব্লকের ভেতরে থাকতে পারে। নিচের উদাহরণে, for স্টেটমেন্টে একটি if স্টেটমেন্ট আছে, যার ভেতরে আবার একটি printf স্টেটমেন্ট আছে। for ব্লকে মোট দুটি স্টেটমেন্ট থাকলেও, for লুপের বডি হিসেবে বিবেচিত হয় কেবল তার একলাইনের if স্টেটমেন্টটি। if স্টেটমেন্টের একলাইনের printf স্টেটমেন্টটি আবার তার বডি হিসেবে বিবেচিত হয়। নেস্টেড ব্লকগুলো যদি প্রত্যেকটি কেবল এক লাইনের হয়, তবে কোনো ব্লকেরই দ্বিতীয় বন্ধনীর দরকার নেই। লক্ষ্য করুন, মন্তব্যটি এবং if ব্লকের ভেতরের printf স্টেটমেন্টটিকে ঘিরে রাখতে দ্বিতীয় বন্ধনীর প্রয়োজন হয় না, কারণ মন্তব্যটি কোনো স্টেটমেন্ট নয়। ইনডেন্টেশন দেখে বোঝা যায় কী ঘটছে, তবে যেকোনো ধরনের অস্পষ্টতা দূর করতে এমন পরিস্থিতিতে দ্বিতীয় বন্ধনী বসানো নিয়ে একটি ডেভেলপমেন্ট টিমের নিজস্ব কোডিং গাইডলাইন থাকতে পারে।
#include <stdio.h>
int main() {
for (int i = 0, j = 11; i < j; i++)
if (i & 1 == 1)
// prints 1 3 5 7 9
printf("%d ", i);
}
আমরা দেখেছি, একটি ব্লকের ভেতরে ডিফাইন করা মান ব্লকের বাইরে থেকে অ্যাক্সেস করা যায় না। আর আমরা দেখেছি, একটি ব্লকের বাইরে ডিফাইন করা মান সাধারণত ব্লকের ভেতর থেকে অ্যাক্সেস করা যায়। কোনো ব্লকের বাইরের একটি ভ্যারিয়েবল ব্লকের ভেতরে লুকিয়ে যেতে পারে, যদি ওই ভ্যারিয়েবলের আইডেন্টিফায়ারটি একটি ব্লক-ভ্যারিয়েবল ডিফাইন করতে ব্যবহার করা হয়। নিচের উদাহরণে, number আইডেন্টিফায়ারটি ফাংশন-স্তরের ভ্যারিয়েবল ও ব্লক-স্তরের ভ্যারিয়েবল দুটোর জন্যই ব্যবহার করা হয়েছে। আমরা বলি, ব্লক-স্তরের ভ্যারিয়েবলটি ফাংশন-স্তরের ভ্যারিয়েবলটিকে লুকিয়ে দেয় বা শ্যাডো করে।
#include <stdio.h>
int main() {
int number = 1;
// prints number in function is 1
printf("number in function is %d\n", number);
if (number == 1) {
// the definition of a block-level variable using the same name
// shadows the function-level variable
int number = 2;
// prints number in function is 2
printf("number in if block is %d\n", number);
}
// prints number in function is 1
printf("number in function is %d\n", number);
}
কোনো ফাংশন প্যারামিটারকে শ্যাডো করার চেষ্টা করলে কম্পাইলার একটি এরর দিতে পারে। নিচের উদাহরণটি
#include <stdio.h>
void print_number(int number) {
// will fail to compile
int number = 2;
printf("number in function is %d\n", number);
}
int main() {
int number = 1;
printf("number in main is %d\n", number);
print_number(number);
printf("number in main is %d\n", number);
}
দুটি প্ল্যাটফর্মে নিচের এররগুলো দেয়
/tmp/jIvhD5UOop.c: In function 'print_number':
/tmp/jIvhD5UOop.c:5:9: error: 'number' redeclared as different kind of symbol
5 | int number = 2;
| ^~~~~~
/tmp/jIvhD5UOop.c:3:23: note: previous definition of 'number' was here
3 | void print_number(int number) {
| ~~~~^~~~~~
আর
redefinition of formal parameter 'number'