সি প্রি-প্রসেসর এবং ম্যাক্রোস - C Preprocessor and Macros
এই অধ্যায়ে আপনি সি প্রি-প্রসেসর (preprocessor) এবং ম্যাক্রো(macro) এর সাথে পরিচিত হবেন। এছাড়া কন্ডিশনাল(conditional), প্র্যাগমা(pragma), লাইন কন্ট্রোল(line control) ইত্যাদি প্রি-প্রসেসর কে আপনার প্রোগ্রামে ব্যবহার করা শিখবেন।
সি প্রি-প্রসেসর এবং ম্যাক্রো
সি প্রি-প্রসেসর হলো ম্যাক্রো প্রি-প্রসেসর যা আপনার প্রোগ্রামে প্রি-প্রসেসর ডিফাইন্ড করার সম্মতি দেয় এবং যা কম্পাইলের পূর্বে আপনার প্রোগ্রামকে ট্রান্সফর্ম(transform) করে। এই ট্রান্সফরমেশনে হেডার ফাইল, ম্যাক্রো এক্সটেশন ইত্যাদি সংযুক্ত হতে পারে।
সকল প্রিপ্রোসেসর ডিরেক্টিভ(directive) #
প্রতীকের মাধ্যমে শুরু হয়।
#define PI 3.14
হেডার ফাইল সংযুক্ত করা
সি প্রোগ্রামে হেডার ফাইল সংযুক্তকরনের জন্য #include
প্রি-প্রসেসর যোগ করা হয়। উদাহরণস্বরূপঃ
#include <stdio.h>
এখানে "stdio.h"
হলো হেডার ফাইল। যা ফাংশন এবং ম্যাক্রো ডেফিনিশন নিয়ে গঠিত। #include
প্রি-প্রসেসর ডিরেক্টিভ(directive) উপরের লাইনকে stdio.h
হেডার ফাইলের কন্টেন্ট দ্বারা প্রতিস্থাপন করে।
scanf()
এবং printf()
ফাংশন ব্যবহারের পূর্বে #include <stdio.h>
প্রি-প্রসেসর ব্যবহার করার এটাই মূল কারণ।
আপনার প্রয়োজনে আপনিও নিজের মত করে বিভিন্ন ফাংশন ডেফিনিশন বিশিষ্ট হেডার ফাইল তৈরি করতে পারেন এবং প্রি-প্রসেসর ডিরেক্টিভ ব্যবহার করে আপনার প্রোগ্রামে সংযুক্ত করতে পারেন।
সি #if, #elif, #else, #endif প্রি-প্রসেসর
এই প্রি-প্রসেসর ডিরেক্টিভ-সমূহ সোর্স কোড কম্পাইল করার জন্য কন্ডিশনাল প্যারামিটার তৈরি করে যা সোর্স কোডের কম্পাইলকে নিয়ন্ত্রণ করে। এগুলো অবশ্যই ভিন্ন ভিন্ন লাইনে শুরু হতে হবে।
সিনট্যাক্স
#if constant_expression
#else constant_expression
#endif
অথবা
#if constant_expression
#elif constant_expression
#endif
- যদি এবং কেবল যদি #if expression এর ভ্যালু true(non-zero) হয় তাহলে কম্পাইলার পরবর্তী কোড-সমূহকে কম্পাইল করে।
- কিন্তু যদি #if expression এর ভ্যালু false(0) হয় তাহলে কম্পাইলার পরবর্তী #else, #elif অথবা #endif পর্যন্ত লাইন-সমূহকে এড়িয়ে(skip করে) যায়।
- যদি #if expression এর ভ্যালু false(0) হয় এবং যদি কোনো #else এর ভ্যালু true(non-zero) হয় তাহলে #else এবং #endif এর মধ্যবর্তী লাইন-সমূহ কম্পাইল হয়।
- যদি #if expression এর ভ্যালু false(0) হয় এবং যদি কোনো #elif এর constant_expression এর ভ্যালু true(non-zero) হয় তাহলে #elif এবং #endif এর মধ্যবর্তী লাইন-সমূহ কম্পাইল হয়।
একই ভাবে
উদাহরন১:
int main(void)
{
#if 1
printf("Hello SATT!\n");
#else
printf("Hello Aziz!\n");
#endif
return 0;
}
আউটপুট
"Hello SATT!"
উপরের উদাহরনে শুধুমাত্র "Hello SATT!" প্রিন্ট হয়েছে। কারণ #if এর ভ্যালু 1 হওয়ায় কম্পাইলার পরবর্তী কোড-সমূহকে এড়িয়ে গেছে।
উদাহরন২:
int main(void)
{
#if 1
printf("Mango\n");
#elif 1
printf("Orange\n");
#endif
return 0;
}
আউটপুট
"Mango"
এখানেও শুধুমাত্র "Mango" প্রিন্ট হয়েছে। কারণ কেবল যদি প্রথম লাইন #if 0 হয় তাহলে "Orange" প্রিন্ট হবে।
উদাহরন৩:
#if OS==1
printf("Version 1.0");
#elif OS==2
printf("Version 2.0");
#else
printf("Version unknown");
#endif
OS এর সেটিং এর উপর ভিত্তিকরে প্রিন্ট হবে এবং যা #define প্রি-প্রসেসর দ্বারা ডিফাইন করা আছে।
সি #define, #undef, #ifdef, #ifndef প্রি-প্রসেসর
#define এবং #undef প্রি-প্রসেসর ডিরেক্টিভ আইডেন্টিফায়ার(identifier) ডিফাইনে সম্মতি দিয়ে থাকে এবং যা নির্দিষ্ট ভ্যালু বহন করে। এসব আইডেন্টিফায়ার সাধারণত কন্সট্যান্ট অথবা ম্যাক্রো(macro) ফাংশন হতে পারে।
আইডেন্টিফায়ার ডিফাইন করা হয়েছে কিনা এই কন্ডিশন(condition) এর উপর ভিত্তিকরে #ifdef এবং #ifndef ডিরেক্টিভ(directive) নির্দিষ্ট সংখ্যক লাইনের কোডকে কম্পাইলের সম্মতি দিয়ে থাকে।
সিনট্যাক্স
#define identifier replacement-code
#undef identifier
#ifdef identifier
#else or #elif
#endif
#ifndef identifier
#else or #elif
#endif
- #ifdef identifier এবং #if defined( identifier) একই অর্থে ব্যবহৃত হয়।
- #ifndef identifier এবং #if !defined(identifier) একই অর্থে ব্যবহৃত হয়।
- #define দ্বারা কোনো আইডেন্টিফায়ার ডিফাইন্ড করা হলে ইহা #undef এ না পৌঁছা পর্যন্ত সোর্স কোডের যেকোনো জায়গায় বিদ্যমান থাকে।
#define দ্বারা নিম্নোক্ত উপায়ে ম্যাক্রো(macro) ফাংশন ডিফাইন্ড করা করা যেতে পারেঃ
#define identifier(parameter-list) (replacement-text)
parameter-list এর ভ্যালু replacement-text এর ভ্যালু দ্বারা প্রতিস্থাপিত হয়।
উদাহরণ১:
#define PI 3.141
printf("%f",PI);
#define DEBUG
#ifdef DEBUG
printf("This is a debug message.");
#endif
#define QUICK(x) printf("%s\n",x);
QUICK("Hi!")
#define ADD(x, y) x + y
z=3 * ADD(4,5)
উপরের প্রোগ্রামের আউটপুট হবে 17। কারণ যোগের চেয়ে গুণের কাজ আগে হয়।
উদাহরণ২:
#define ADD(x,y) (x + y)
z=3 * ADD(4,5)
উপরের প্রোগ্রামের আউটপুট হবে 17। কারণ যোগফলকে বন্ধনীর মধ্যে রাখা হয়ছে এবং গুণের চেয়ে বন্ধনীর কাজ আগে হয়।
সি #include ডিরেক্টিভ
#include ডিরেক্টিভ(directive) এর মাধ্যমে এক্সটারনাল হেডার ফাইলকে কম্পাইলার দ্বারা প্রসেস করা যায়।।
সিনট্যাক্স
#include <header-file>
অথবা
#include "source-file"
যখন কোনো ফাইলকে < এবং > এর মধ্যে রাখা হয় তখন কম্পাইলার(implementation) উক্ত ফাইলের জন্য পরিচিত হেডার ডিরেক্টরিতে( যা কম্পাইল কর্তৃক সংজ্ঞায়িত) অনুসন্ধান করে এবং ইহাকে কম্পাইল করে।
যখন ফাইলকে ডবল উদ্ধৃতি চিহ্নের মধ্যে রাখা হয় তখন সোর্স-ফাইলের সম্পূর্ণ বিষয়বস্তু(content) প্রতিস্থাপিত হয়। ফাইলের জন্য এই অনুসন্ধান পদ্ধতি বাস্তবায়ন-নির্দিষ্ট(implementation-specific)।
উদাহরণঃ
#include <stdio.h>
#include "my_header.h"
সি #line প্রি-প্রসেসর
#line ডিরেক্টিভ(directive) বর্তমান লাইন নম্বর এবং বর্তমান সোর্স কোড ফাইলের নাম পরিবর্তনে সম্মতি দিয়ে থাকে।
সিনট্যাক্স
#line line-number filename
উল্লেখ্য, যদি ফাইলের নাম না দেওয়া হয় তাহলে ইহা একই রকম থাকে। চলমান লাইন এর লাইন নম্বরটি নতুন লাইনের থেকে এক বশী হয়। সুতরাং প্রথম লাইন নম্বর 1
উদাহরণঃ
#line 50 user.c
#line 23
সি #error প্রি-প্রসেসর
#error ডিরেক্টিভ(directive) কম্পাইল থামিয়ে দিয়ে নির্ধারিত error message রিটার্ন করে।
সিনট্যাক্স
#error message
উদাহরণঃ
#ifndef VERSION
#error Version number not specified.
#endif
সি #pragma প্রি-প্রসেসর
#pragma ডিরেক্টিভ ডিরেক্টিভকে ডিফাইন করার সম্মতি দেয়। ইহা কম্পাইলারের ক্রিয়াকলাপ নিয়ন্ত্রণ করে। যদি pragma সাপোর্ট না করে তাহলে ইহাকে এড়িয়ে যায়।
সিনট্যাক্স
#pragma directive
সি ম্যাক্রো
ম্যাক্রো(macro) হলো এক খণ্ড কোড(a fragment of code) যার একটি নির্দিষ্ট নাম দেওয়া হয়। নাম ব্যবহার করেই আপনি এই কোড খণ্ডকে আপনার প্রোগ্রামে ব্যবহার করতে পারেন।
পূর্বনির্ধারিত ম্যাক্রো
নিম্নোক্ত ম্যাক্র-সমূহ ইতিমধ্যেই কম্পাইলারে ডিফাইন্ড করা আছে এবং এগুলোকে পরিবর্তন করা যায় না।
পূর্বনির্ধারিত ম্যাক্রো | বর্ণনা |
---|---|
__LINE__ | চলমান লাইন নম্বরকে নির্দেশের জন্য ইন্টেজার(integer) কন্সট্যান্ট |
__FILE__ | চলমান সোর্স কোড ফাইলের নাম নির্দেশের জন্য স্ট্রিং |
__DATE__ | চলমান সোর্স ফাইলের কম্পাইল শুরু হওয়ার তারিখ নির্দেশের জন্য স্ট্রিং। asctime ফাংশন যে ফরম্যাটে তারিখ প্রদান করে ইহার ও ঠিক একই ফরম্যাট। যেমন- "mmm dd yyyy"। |
__TIME__ | চলমান সোর্স ফাইলের কম্পাইল শুরু হওয়ার সময় নির্দেশ এর জন্য স্ট্রিং। asctime ফাংশন যে ফরম্যাটে তারিখ প্রদান করে ইহার ও ঠিক একই ফরম্যাট। যেমন- "hh:mm:ss"। |
__STDC__ | যদি ANSI স্টান্ডার্ড অনুসরণ করে তাহলে ভ্যালু ১(non-zero) হবে। |
উদাহরনঃ
পূর্বনির্ধারিত ম্যাক্রো ব্যবহার করে চলমান সময় নির্ণয়ের জন্য সি প্রোগ্রাম
#include <stdio.h>
int main()
{
printf("Current time: %s",__TIME__); // চলমান সময় নির্ণয়
}
আউটপুট
Current time: 19:54:39