Module ৩ · কনস্ট্যান্ট, qualifier, ইনপুট আর আউটপুট
কনস্ট্যান্ট আর literal: নাম পাওয়া মান
এই lesson-এ যা শিখবে
- Integer, floating, character আর string literal লিখতে পারবে, ঠিক যে type-টা তুমি চেয়েছিলে সেটা দিয়ে।
#defineবাconstদিয়ে একটা মানকে নাম দিতে পারবে, আর কোনটা আগে ধরতে হয় সেটা বলতে পারবে।- Magic number চিনতে পারবে, আর
010-কে যে আট সেটাই পড়তে পারবে।
Maria-র দোকানের till 7.5 শতাংশ VAT নেয়। 7.5 সংখ্যাটা ওর program-এ লেখা আছে এগারো জায়গায়। সরকার হার বাড়িয়ে 15 করল, ও এগারোটার মধ্যে দশটা বদলাল, আর তিন সপ্তাহ ধরে একরকম receipt চুপচাপ ভুল ছাপা হলো।
এই গল্পে কঠিন কোনো bug নেই। এটা গোছানোর সমস্যা, আর C-তে এর জন্য দুইটা জিনিস আছে। এই lesson সেইসব মান নিয়ে, যেগুলো তুমি সোজা code-এ লিখে দাও। আর জরুরি মানগুলোকে নাম দেওয়া নিয়ে, যাতে ওটা কখনো একটার বেশি না হয়।
Literal মানে নাম ছাড়া একটা মান
Program-এ তুমি যত খালি মান লিখো, প্রতিটাই একটা literal। int marks = 42;-র 42 একটা integer literal। কোনো printf-র "Progsity" একটা string literal।
Literal-র একটা type থাকে, যদিও তুমি কোথাও type লেখোনি। Literal-টা কীভাবে লেখা, তা দেখে compiler type ঠিক করে, আর সেই বাছাই সবসময় তোমার ভাবনার সাথে মেলে না।
Literal-র পরিবারগুলো, আর প্রতিটা যে type পায়
42 int
42U unsigned int U বা u
42L long L বা l
42LL long long LL বা ll
42ULL unsigned long long দুইটা suffix একসাথে
3.14 double দশমিকের জন্য এটাই default
3.14f float f বা F
3.14L long double L বা l
1.5e3 double, মান 1500.0 e মানে দশের ঘাত দিয়ে গুণ
'A' int, ভিতরে 65 একক উদ্ধৃতিতে একটা অক্ষর
'\n' int, ভিতরে 10 escape sequence একটাই অক্ষর
"Progsity" char-র array জোড়া উদ্ধৃতি, পুরো আলাদা জিনিস
0x1F int, মান 31 base 16, 0x prefix
037 int, মান 31 base 8, শুরুতে একটা শূন্য
0b11111 int, মান 31 base 2, C23 বা compiler-র extension
- Suffix-টা literal-র অংশ, যে variable-এ ওটা যাচ্ছে তার অংশ না।
3.14একটাdouble। ওটাfloatvariable-এ লিখলে সরু হয়ে যায়, সেটা চলে আর কিছু অঙ্ক হারায়।- C-তে character constant একটা
int,charনা। Module 2-র lesson 4 বলেছিল ওটা সংখ্যা কেন।
তাই সবচেয়ে কাজের নিয়মটা হলো: পূর্ণসংখ্যা মানে int, দশমিক মানে double, আর অন্য কিছু বলতে চাইলে suffix।
#include <stdio.h>
int main(void)
{
printf("int literal : %d\n", 42);
printf("long long : %lld\n", 42LL);
printf("unsigned : %u\n", 42U);
printf("double literal : %.2f\n", 3.14);
printf("float literal : %.2f\n", 3.14f);
printf("char literal : %c and %d\n", 'A', 'A');
printf("string literal : %s\n", "Progsity");
return 0;
}
int literal : 42
long long : 42
unsigned : 42
double literal : 3.14
float literal : 3.14
char literal : A and 65
string literal : Progsity
সাতটা literal, সাতটা specifier, একটা program। ছয় নম্বর লাইনটা খেয়াল করো: একটাই literal, দুইবার ছাপা, একবার চেহারা হিসেবে আর একবার ভিতরের সংখ্যা হিসেবে।
শুরুতে শূন্য মানে base আট
একই সংখ্যা C-তে চার ভাবে লেখা যায়। Base 10 সাধারণটা। Base 16 শুরু হয় 0x দিয়ে, base 8 শুরু হয় একটা 0 দিয়ে, আর base 2 শুরু হয় 0b দিয়ে।
এর তিনটা নিরীহ। চার নম্বরটা ভাষার সবচেয়ে পুরনো ফাঁদ, কারণ শুরুতে শূন্য বসানো মানুষ সৌন্দর্যের জন্যই করে।
#include <stdio.h>
int main(void)
{
int plain = 31;
int hex = 0x1F;
int octal = 037;
int looks_like_ten = 010;
printf("31 is %d\n", plain);
printf("0x1F is %d\n", hex);
printf("037 is %d\n", octal);
printf("010 is %d\n", looks_like_ten);
return 0;
}
31 is 31
0x1F is 31
037 is 31
010 is 8
প্রথম তিন লাইন একই মান, তিন ভাবে লেখা। চার নম্বরটা এমন একটা সংখ্যা যেটা কেউ ইচ্ছে করে লেখেনি।
কেউ যদি product code-র তালিকা 001, 007, 010 লেখে, ও পায় 1, 7 আর 8। Compiler দুই command line-র কোনোটাতেই কিছু বলে না, কারণ তিনটাই বৈধ octal।
Base 2 তুলনায় নতুন। 0b11111 বহু বছর ধরে GCC-র extension, আর standard C-তে ঢুকেছে C23-তে, তাই bit-র নকশা জরুরি হলে এই track hex লেখে।
তাই নিয়মটা হলো: C-তে শুরুর শূন্য কখনো সাজসজ্জা না, আর সারি মেলাতে চাইলে printf-এ width ব্যবহার করো।
#define মানে compiler দেখার আগেই খুঁজে বদলে দেওয়া
একটা #define হলো preprocessor-র নির্দেশ। Preprocessor compiler-র আগে চলে, তোমার file-টা লেখা হিসেবে পড়ে, আর নামটা যেখানে যেখানে আছে সব জায়গায় বদলি বসিয়ে দেয়।
এর type নেই, semicolon লাগে না, আর compiler শুরু হওয়ার আগেই এটা হাওয়া। এটাই এর জোর, আর এর প্রতিটা সমস্যাও এখান থেকেই।
#include <stdio.h>
#define VAT_PERCENT 15
#define SHOP_NAME "Maria General Store"
int main(void)
{
long long bill_paisa = 36000;
printf("%s\n", SHOP_NAME);
printf("VAT is %d percent\n", VAT_PERCENT);
printf("VAT on 360.00 is %lld paisa\n", bill_paisa * VAT_PERCENT / 100);
return 0;
}
Maria General Store
VAT is 15 percent
VAT on 360.00 is 5400 paisa
Compiler যখন ওই program পড়ে, VAT_PERCENT শব্দটার আর অস্তিত্বই নেই। ও দুই জায়গাতেই 15 অঙ্কগুলো দেখে, ঠিক যেন Maria নিজে লিখে দিয়েছে।
বদলটা যেহেতু লেখার স্তরে, একটা macro-র কোনো scope-ও নেই। File-র মাথায় বসানো #define file-র শেষ পর্যন্ত চালু থাকে। ওটা function-র ভিতরে, declaration-র ভিতরে, আর নামটা যেখানেই থাকে সেখানেই বদলি বসাবে।
Const মানে এমন variable যেটা compiler বদলাতে দেয় না
const হলো সাধারণ একটা variable, সাথে বাড়তি একটা শব্দ। এর type আছে, এটা memory-তে থাকে, আর যে scope-এ declare হয়েছে সেটাই মানে। এতে assign করলে compiler error দেয়।
#include <stdio.h>
int main(void)
{
const double PI = 3.14159265358979;
const int SEATS_PER_ROW = 8;
double radius = 2.5;
printf("area of the mat : %.2f\n", PI * radius * radius);
printf("seats in 12 rows : %d\n", SEATS_PER_ROW * 12);
return 0;
}
area of the mat : 19.63
seats in 12 rows : 96
দুইটা constant-ই কাজে লেগেছে আর কোনোটাই বদলায়নি। এদের নিচে যেকোনো জায়গায় PI = 3.0; লাইনটা বসাও, program আর build হবে না, আর const-র পুরো কাজটাই এটুকু।
ঠিক কোন কথাটা compiler বলে, আর ও আর কী কী মানা করতে শুরু করে, সেটা পরের lesson। এখানে কথাটা ছোট: const হলো প্রতিশ্রুতি লাগানো একটা variable, আর #define variable-ই না।
Const না #define, আর কোনটা আগে
আগে const ধরো। #define ধরো তখনই, যখন এমন কিছু দরকার যেটা variable হয়ে ওঠা সম্ভব না।
| প্রশ্ন | const | #define |
|---|---|---|
| কে সামলায় | compiler | preprocessor, compiler-র আগে |
| type আছে কি | আছে | নেই, ওটা লেখা |
| type মিলিয়ে দেখা হয় কি | হয় | মেলানোর কিছু নেই |
| একটা block-এ আটকানো যায় কি | যায় | যায় না, ওই লাইন থেকে গোটা file |
| debugger-এ দেখা যায় কি | যায়, নাম ধরে | যায় না, আগেই বদলে গেছে |
| array-র মাপ হিসেবে চলে কি | সত্যিকারের constant হিসেবে না, নিচে দেখো | চলে |
| semicolon লাগে কি | লাগে | লাগে না, আর দিলে সেটাও বদলি হয়ে ঢোকে |
Array-র মাপের সারিটাই একমাত্র সত্যিকারের ফাঁক। C-তে একটা const int হলো এমন variable যেটা তুমি বদলাবে না বলে কথা দিয়েছ, আর ওটা constant expression না।
একটা const int limit দিয়ে int marks[limit]; লিখলে C17-তে সেটা build হয়, তবে তুমি একটা variable length array বানিয়ে ফেলেছ, নির্দিষ্ট মাপের array না। ওটার মানে কী আর কখন গুরুত্বপূর্ণ, সেটা Module 9 বলে।
C++-এ ওই একই লাইন সাধারণ নির্দিষ্ট মাপের array, আর এই কারণেই C++-র কোনো পাতা থেকে তুলে আনা পরামর্শ এখানে চুপচাপ ভুল হতে পারে।
Enum, এক অনুচ্ছেদে
সংখ্যাকে নাম দেওয়ার তৃতীয় একটা পথ আছে, আর একটা সম্পর্কিত সেটের জন্য সেটাই ঠিক পথ। একটা enum পরপর কতগুলো পূর্ণসংখ্যাকে পড়ার মতো নাম দেয়, 0 থেকে শুরু করে উপরে ওঠে, যদি না তুমি অন্য কিছু বলো।
#include <stdio.h>
int main(void)
{
enum status { pending, running, passed, failed };
enum status verdict = passed;
printf("verdict = %d\n", verdict);
return 0;
}
verdict = 2
const int-র মতো না, enum-র নাম সত্যিই একটা constant expression, তাই ওটা দিয়ে array-র মাপ দেওয়া যায়। Module 12 struct-র পাশে এটা নিয়ে ঠিকমতো কাজ করে, আর ততক্ষণ এটাকে একটা ঝলক হিসেবেই নাও।
Magic number মানে যে সংখ্যা কেউ ব্যাখ্যা করতে পারে না
Magic number হলো হিসাবের মাঝখানে বসা খালি একটা literal, যার মানে কেবল লেখকের মাথায় থাকে। ছয় মাস পরে লেখক নিজেই অন্য একজন মানুষ।
নিচে Maria-র till, প্রথমে যেভাবে ও লিখেছিল, তারপর নাম দিয়ে আবার লেখা। দুইটা program একই byte ছাপে।
এখানকার প্রতিটা সংখ্যা ঠিক। লাইনটার বাকিটা না পড়ে কাউকে বোঝাও তো, 75 আর 1000 জিনিস দুইটা কী।
#include <stdio.h>
int main(void)
{
long long rice = 12000;
long long oil = 45000;
printf("Rice : %lld\n", rice + rice * 75 / 1000);
printf("Oil : %lld\n", oil + oil * 75 / 1000);
printf("Total : %lld\n", rice + oil + (rice + oil) * 75 / 1000);
return 0;
}
Rice : 12900
Oil : 48375
Total : 61275
হারটা তিনবার আছে। ওটা বদলানো মানে তিনটা edit, আর দুইটা করে থেমে গেলে compiler তোমাকে সেটা বলতে পারবে না।
Run in Compilerদুইটা declaration, আর হারটা এখন ঠিক এক জায়গায় থাকে।
#include <stdio.h>
int main(void)
{
const long long VAT_PER_MILLE = 75;
const long long PER_MILLE = 1000;
long long rice = 12000;
long long oil = 45000;
long long both = rice + oil;
printf("Rice : %lld\n", rice + rice * VAT_PER_MILLE / PER_MILLE);
printf("Oil : %lld\n", oil + oil * VAT_PER_MILLE / PER_MILLE);
printf("Total : %lld\n", both + both * VAT_PER_MILLE / PER_MILLE);
return 0;
}
Rice : 12900
Oil : 48375
Total : 61275
Byte ধরে ধরে একই output। মেশিনের কিছু লাভ হলো না আর পরের পাঠকের সবটুকু লাভ হলো।
শতাংশ না বলে হাজারাংশ, কারণ 7.5 শতাংশ পূর্ণসংখ্যা না আর এই till সবকিছু পূর্ণ এককে রাখে। কেন, সেটা Module 2-র lesson 3।
Run in Compilerযেটা কখনোই সংখ্যা না তার জন্য #define, আর যা যা সংখ্যা তার জন্য const।
#include <stdio.h>
#define SHOP_NAME "Maria General Store"
int main(void)
{
const long long VAT_PER_MILLE = 75;
const long long PER_MILLE = 1000;
const long long PAISA_PER_TAKA = 100;
long long bill = 57000;
long long vat = bill * VAT_PER_MILLE / PER_MILLE;
long long total = bill + vat;
printf("%s\n", SHOP_NAME);
printf("bill : %lld.%02lld\n", bill / PAISA_PER_TAKA, bill % PAISA_PER_TAKA);
printf("vat : %lld.%02lld\n", vat / PAISA_PER_TAKA, vat % PAISA_PER_TAKA);
printf("total : %lld.%02lld\n", total / PAISA_PER_TAKA, total % PAISA_PER_TAKA);
return 0;
}
Maria General Store
bill : 570.00
vat : 42.75
total : 612.75
শেষ তিন লাইনের % হলো ভাগশেষের operator, আর Module 4 ওটা ঠিকমতো শেখায়। ওটা এখানে আছে কারণ receipt-ই ওর স্বাভাবিক জায়গা, আর Module 4 দুই lesson দূরে।
এটা কোথায় কাজে লাগে
- যে standard library তুমি এখনই ব্যবহার করছ।
BUFSIZ,EOFআরNULLহলো<stdio.h>-র macro। Module 3-র lesson 5-এ যখনEOFলিখবে, compiler দেখার আগেই preprocessor সেখানে -1 বসিয়ে দেবে। - Linux-র file permission।
chmod 0755-র সংখ্যাটা একটা octal literal, আর এর পিছনের C call ওই একই সংখ্যা নেয়। ওখানে শুরুর শূন্যটা ইচ্ছাকৃত আর জরুরি, আর ঠিক এই কারণেই C ওভাবে পড়ে। - Graphics program-এ রঙ। প্রতিটা game engine আর image tool রঙ লেখে
0xFF8800ভাবে, কারণ hex-র প্রতি জোড়া অঙ্ক লাল, সবুজ বা নীলের এক byte। Decimal-এ ওই গড়নটা চাপা পড়ে যেত। - Payroll-র batch job। করের ধাপ, হিসাব গোল করার একক আর বেতনের সময়সীমা একটা file-র মাথায় নাম দেওয়া constant হয়ে বসে থাকে। তাতে বছরের নিয়ম বদল একটামাত্র edit, যেটা reviewer চোখে দেখতে পায়।
যে ভুলগুলো সবাই করে
১. গোছানোর জন্য সংখ্যার আগে শূন্য বসানো।
int product_code = 010;
printf("%d\n", product_code);
দুই command line-র কোনোটাতেই কিছু বলে না, আর ছাপে 8। প্রতিটা অঙ্কই বৈধ octal, তাই compiler-র আপত্তি করার কিছু নেই। Code-টা ছাপার সময় 010 দেখাতে চাইলে সেটা printf-র width আর zero flag-র কাজ, আর এই module-র lesson 3-এ সেগুলো পাবে।
২. #define-র শেষে semicolon দেওয়া।
#define PI 3.14159;
double area = PI * 2.0 * 2.0;
Playground-র GCC 12 বলে error: invalid type argument of unary '*' (have 'double'), আর যে লাইনটা দেখায় সেটা PI ব্যবহারের লাইন, যে লাইনে define হয়েছে সেটা না। Semicolon-টাও বদলি হয়ে ঢুকে গেছে, তাই compiler পড়েছে 3.14159; * 2.0। Macro মানে লেখা, আর লেখার প্রতিটা অক্ষরই ঢোকে।
৩. Macro-র parameter-র চারপাশে বন্ধনী না দেওয়া।
#define SQUARE(x) x * x
printf("%d\n", SQUARE(2 + 3));
কিছুই বলে না, আর ছাপে 11। লেখার বদলিতে দাঁড়ায় 2 + 3 * 2 + 3, আর গুণ আগে হয়। পুরো শরীর আর প্রতিটা parameter মুড়ে দাও: #define SQUARE(x) ((x) * (x))। একটা const-র এই সমস্যাটা হওয়াই সম্ভব না, আর এটাই ওটাকে আগে ধরার আরেকটা কারণ।
৪. #define-র নামটাই আবার variable হিসেবে লেখা।
#define MAX_STUDENTS 60
int main(void)
{
int MAX_STUDENTS = 30;
return 0;
}
GCC 12 বলে error: expected identifier or '(' before numeric constant, সাথে যোগ করে note: in expansion of macro 'MAX_STUDENTS'। ও পড়েছে int 60 = 30;। Macro-র কোনো scope নেই, তাই ওটা তোমার function-র ভিতরে ঢুকে নামটা সেখানেও বদলে দেয়। এই কারণেই macro-র নাম চিরকাল বড় হাতের অক্ষরে চেঁচিয়ে লেখা হয়।
Maria-র till, আবার লেখা, যাতে VAT-র হারটা ঠিক এক লাইনে থাকে। একটা পরিমাণ আর পয়সায় প্রতি এককের দাম পড়ো, তারপর subtotal, VAT আর total ছাপো।
Input. এক লাইনে দুইটা পূর্ণসংখ্যা: পরিমাণ, তারপর পয়সায় প্রতি এককের দাম।
Output. তিন লাইন, প্রতিটায় একটা শব্দ তারপর অঙ্কটা পূর্ণ টাকা, একটা দাঁড়ি-চিহ্ন আর দুই অঙ্কের পয়সা হিসেবে।
Constraints. 1 <= পরিমাণ <= 100000, 1 <= প্রতি এককের দাম <= 10000000, VAT 15 শতাংশ আর পয়সার ভগ্নাংশ বাদ। কোনো double না।
Sample. Input 3 12000 দিলে Subtotal 360.00, VAT 54.00, Total 414.00।
#include <stdio.h>
int main(void)
{
const long long VAT_PERCENT = 15;
const long long PAISA_PER_TAKA = 100;
const long long PERCENT_WHOLE = 100;
long long quantity = 0;
long long unit_paisa = 0;
scanf("%lld %lld", &quantity, &unit_paisa);
/* Three lines. %02lld is what makes five paisa print as 05. */
return 0;
}
named-receipt নামে graded। একশো দুইটার মানে দুই রকম, তাই ওরা দুইটা constant, একটা না।
Amara-র config file permission লেখে base 8-এ আর রঙ লেখে base 16-এ। দুইটাই decimal-এ ছাপো।
Input. এক লাইনে একটা hexadecimal সংখ্যা তারপর একটা octal সংখ্যা, কোনো prefix ছাড়া।
Output. এক লাইনে দুইটাই decimal-এ, মাঝে একটা ফাঁকা জায়গা।
Constraints. Hex 0 থেকে FFFFFF, যেকোনো হাতের অক্ষরে; octal 0 থেকে 7777777।
Sample. Input 1F 755 দিলে 31 493।
#include <stdio.h>
int main(void)
{
unsigned int from_hex = 0;
unsigned int from_octal = 0;
scanf("%x %o", &from_hex, &from_octal);
/* One printf. Both boxes already hold ordinary numbers. */
return 0;
}
base-decoder নামে graded। এই module-র lesson 4 scanf লাইনটা বোঝায়; এখানে সেটা লিখে দেওয়া আছে।
Zara গোল mat কাটছে আর প্রতিটার ক্ষেত্রফল আর কিনারার দৈর্ঘ্য চায়, একটা ব্যাসার্ধ আর নাম দেওয়া একটা pi থেকে।
Input. এক লাইনে একটা দশমিক সংখ্যা, ব্যাসার্ধ।
Output. দুই লাইন: ক্ষেত্রফল, তারপর পরিধি, দুইটাই ঠিক দুই দশমিক ঘর পর্যন্ত।
Constraints. const double PI = 3.14159265358979; ব্যবহার করো আর অঙ্কগুলো একবারই লেখো।
Sample. Input 2.5 দিলে 19.63 তারপর 15.71।
#include <stdio.h>
int main(void)
{
const double PI = 3.14159265358979;
double radius = 0.0;
scanf("%lf", &radius);
/* Two printf calls, both using PI, neither repeating its digits. */
return 0;
}
এই module-এ graded না। তারপর #define PI 3.14159265358979 দিয়ে আরেকবার করো আর দেখে নাও output হুবহু এক। দুইটার তফাত মানুষ নিয়ে, অঙ্ক নিয়ে না।
যে প্রশ্নগুলো সবার মনে আসে
Constant-র নাম বড় হাতের অক্ষরে লেখা হয় কেন?
অভ্যাস, আর কাজের অভ্যাস। শুরুটা macro দিয়ে, যেখানে নাম ভুল জায়গায় পৌঁছে যাওয়া সত্যিকারের বিপদ। তারপর
const-তেও ছড়িয়েছে, কারণ পাঠক তখন এক নজরেই বোঝে মানটা কখনো নড়ে না।#define-র চেয়েconstকি বেশি memory খায়?কাগজে-কলমে হ্যাঁ, বাস্তবে প্রায় কখনোই না। যেকোনো optimisation স্তরে প্রতিটা compiler ছোট একটা
const-কে সোজা ওই instruction-র ভিতরেই বসিয়ে দেয়, ঠিক যেমন macro করত।3.14fকি3.14-র চেয়ে কম নিখুঁত?হ্যাঁ।
fsuffix ওটাকেfloatবানায়, যেটা প্রায় 7 অঙ্ক বয়, আরdoubleবয় 15। Module 2-র lesson 3 দেখিয়েছিল এতে কী হারায়, আর সেখানকার উত্তর ছিল কারণ না থাকলেdoubleব্যবহার করো।যেটা পরে বদলাব, তাতেও কি
constলেখা যায়?না, আর ওটাই তো এর মানে। Compiler assign করাটাই মানা করে দেয়, আর সেটাই পরের lesson।
মানকে নাম দেওয়ার কি
#defineআরconst-ই একমাত্র পথ?না।
enumতৃতীয় পথ, আর সম্পর্কিত পূর্ণসংখ্যার একটা সেটের জন্য তিনটার মধ্যে ওটাই সেরা। Module 12 ওটাকে নিজের একটা lesson দেয়।
মূল কথা
- প্রতিটা খালি মান একটা literal, আর কীভাবে লেখা হয়েছে তা-ই তার type ঠিক করে।
- Suffix দিয়ে তুমি বলো যে
intবাdoubleছাড়া অন্য কিছু চেয়েছিলে। - শুরুতে শূন্য মানে base 8, চুপচাপ, আর
010হলো 8। #defineহলো compiler-র আগে হওয়া একটা লেখার বদল, type নেই আর scope নেই।constহলো type আর scope সহ একটা variable, যাতে compiler assign করতে দেয় না; আগে এটাই ধরো।- Magic number মানে যে মানের অর্থ কেউ আর উদ্ধার করতে পারে না, আর নাম দিলে চলার সময় কোনো খরচ বাড়ে না।
এরপর দেখবে const লিখে ফেলার পর compiler আসলে কী কী মানা করে, আর সেই একটা qualifier-র দেখা পাবে যেটা ওকে চালাকি বন্ধ করতে বলে।
lesson ১ শেষ
শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।
পরেরটা: Const আর volatile: compiler-কে দেওয়া প্রতিশ্রুতি