विषय पर बढ़ें

अंतिम अद्यतन:2026-10-12

C++ volatile कीवर्ड

लेखक के बारे में:

व्यक्तिगत WeChat सार्वजनिक अकाउंट:

wechat

1.volatile

volatile से qualify हुए वेरिएबल पर read/write ऑपरेशन करते समय कुछ observable side effects पैदा होते हैं। और ये observable side effects प्रोग्राम से बाहर के कारकों से तय होते हैं।

2.volatile के उपयोग

(1) parallel डिवाइसों के hardware register (जैसे status register)। मान लीजिए एक डिवाइस को initialize करना है, इस डिवाइस का एक register 0xff800000 है।

int  *output = (unsigned  int *)0xff800000; //定义一个IO端口;  
int   init(void)  
{  
    int i;  
    for(i=0;i< 10;i++)
    {  
    *output = i;  
    }  
}
कंपाइलर ऑप्टिमाइज़ेशन के बाद, कंपाइलर मानता है कि ऊपर का पूरा लूप बेकार है, अंतिम परिणाम पर कोई असर नहीं, क्योंकि आख़िर में output पॉइंटर की वैल्यू 9 ही है, इसलिए कंपाइलर का बनाया कोड इसके बराबर होगा:
int  init(void)  
{  
    *output = 9;  
}
अगर इस बाहरी डिवाइस के initialization की प्रक्रिया में ऊपर के कोड की तरह क्रम से वैल्यू देना ज़रूरी हो, तो ज़ाहिर है ऑप्टिमाइज़ेशन प्रक्रिया मक़सद हासिल नहीं कर सकती। इसके उलट अगर आप इस पोर्ट पर बार-बार write नहीं, बल्कि read ऑपरेशन कर रहे हैं, तो परिणाम वैसा ही है, कंपाइलर ऑप्टिमाइज़ करने के बाद शायद आपका कोड इस पते से read ऑपरेशन सिर्फ़ एक बार करे। जबकि कोड के नज़रिए कोई समस्या नहीं है। इसी समय volatile से कंपाइलर को बताना चाहिए कि यह वेरिएबल unstable है, इसे देखकर optimize न करे।

(2) ऐसा वेरिएबल जिसे interrupt service subroutine में एक्सेस किया जाता है;

static int i=0;

int main()
{
    while(1)
    {
    if(i) dosomething();
    }
}

/* Interrupt service routine */
void IRS()
{
    i=1;
}
उपरोक्त उदाहरण प्रोग्राम का मतलब यह है कि interrupt पैदा होने पर interrupt service subroutine IRS interrupt संभाले, प्रोग्राम वेरिएबल i बदले, ताकि main फ़ंक्शन में dosomething फ़ंक्शन कॉल हो, लेकिन, क्योंकि कंपाइलर देखता है कि main फ़ंक्शन में i नहीं बदला, इसलिए वह i से किसी register तक read ऑपरेशन शायद सिर्फ़ एक बार करे, और फिर हर if जाँच उस register के अंदर के "i की copy" से करे, जिससे dosomething कभी कॉल नहीं होगा। अगर वेरिएबल i पर volatile लगा दें, तो कंपाइलर वेरिएबल i के read/write ऑपरेशन के optimize न होने की गारंटी देता है, जिससे वेरिएबल i के बाहरी प्रोग्राम से बदले जाने पर मूल प्रोग्राम को तुरंत पता चल जाता है।

(3) multithreaded एप्लिकेशन में कई tasks द्वारा साझा किया गया वेरिएबल। जब कई threads कोई वेरिएबल साझा करते हैं, और वह वेरिएबल किसी thread से बदला जा सकता है, तो उसे volatile से declare करना चाहिए। इसका काम है कंपाइलर के ऑप्टिमाइज़ेशन से वेरिएबल को मेमोरी से CPU register में जाने से रोकना, जब एक thread वेरिएबल बदलता है, और वह दूसरे threads में समय पर sync न हो तो प्रोग्राम ग़लत हो जाता है। volatile का मतलब है कि कंपाइलर इस वेरिएबल पर हर ऑपरेशन पर मेमोरी से सचमुच वैल्यू ले, register में पहले से मौजूद वैल्यू उपयोग न करे। उदाहरण नीचे दिया गया है:

volatile  bool bStop=false;  //bStop 为共享全局变量  
//第一个线程
void threadFunc1()
{
    ...
    while(!bStop){...}
}
//第二个线程终止上面的线程循环
void threadFunc2()
{
    ...
    bStop = true;
}
दूसरे thread से पहले thread का लूप रोकने के लिए, अगर bStop को volatile से परिभाषित न किया जाए, तो यह लूप infinite loop बन जाएगा, क्योंकि bStop पहले ही register में पढ़ा जा चुका है, register में bStop की वैल्यू कभी FALSE नहीं होगी, volatile लगाने पर प्रोग्राम निष्पादन के समय हर बार bStop की वैल्यू मेमोरी से पढ़ी जाती है, इसलिए infinite loop नहीं बनता।

क्या volatile के उपयोग के केस समझना C/C++ प्रोग्रामर और embedded डेवलपर में अंतर करने का असरदार तरीका है, embedded काम करने वाले अक्सर hardware, interrupt, RTOS आदि से पेश आते हैं, इन सबमें volatile वेरिएबल की ज़रूरत होती है, volatile को न समझना प्रोग्राम डिज़ाइन की आपदा ला सकता है।

3.volatile के आम सवाल

नीचे के सवालों से देखा जा सकता है कि interview देने वाला सचमुच volatile समझता है या नहीं। (1) क्या एक पैरामीटर const भी हो सकता है और volatile भी? क्यों? हाँ। एक उदाहरण है read-only status register। यह volatile इसलिए है क्योंकि इसे अनजाने में बदला जा सकता है। यह const इसलिए है क्योंकि प्रोग्राम को इसे बदलने की कोशिश नहीं करनी चाहिए।

(2) क्या कोई पॉइंटर volatile हो सकता है? क्यों? हाँ। भले ही यह आम नहीं है। एक उदाहरण है जब कोई interrupt service subroutine किसी buffer की ओर पॉइंट करने वाला पॉइंटर बदलता है।

(3) नीचे दिए फ़ंक्शन में क्या ग़लती है?

int square(volatile int *ptr) 
{ 
return *ptr * *ptr; 
} 
यह कोड थोड़ा अजीब है, इसका मक़सद पॉइंटर ptr की इंगित वैल्यू का वर्ग लौटाना है, लेकिन, क्योंकि ptr एक volatile टाइप पैरामीटर की ओर पॉइंट करता है, कंपाइलर नीचे जैसा कोड बनाएगा:
int square(volatile int *ptr) 
{ 
int a,b; 
a = *ptr; 
b = *ptr; 
return a * b; 
} 
क्योंकि *ptr की वैल्यू अनजाने में बदली जा सकती है, इसलिए a और b अलग हो सकते हैं। नतीजतन, यह कोड वह वर्ग लौटा सकता है जो आप चाहते नहीं! सही कोड नीचे है:
long square(volatile int *ptr) 
{ 
int a=*ptr; 
return a * a; 
} 

4.volatile का उपयोग

  • volatile कीवर्ड एक type modifier है, इससे declare किया टाइप वेरिएबल दर्शाता है कि उसे कंपाइलर के अज्ञात कारकों (ऑपरेटिंग सिस्टम, hardware, दूसरे threads आदि) से बदला जा सकता है। इसलिए volatile का उपयोग कंपाइलर को बताता है कि ऐसे ऑब्जेक्ट को optimize न करे।

  • volatile कीवर्ड से declare किए वेरिएबल की हर एक्सेस पर मेमोरी से वैल्यू लेनी होगी (volatile से qualify न हुए वेरिएबल, कंपाइलर के ऑप्टिमाइज़ेशन के कारण CPU register से वैल्यू ले सकते हैं)

  • const, volatile हो सकता है (जैसे read-only status register)
  • पॉइंटर volatile हो सकता है

कोड सीखें:

संबंधित विषय

टिप्पणियाँ