ब्

ब्लॉक में C

{one: "1 अभ्यास", other: "%{count} अभ्यास"}

ब्लॉक के बारे में

ब्लॉक स्टेटमेंट और घोषणाओं का एक ऐसा समूह है, जो एक ही सिंटैक्स इकाई में बँधा होता है। ब्लॉक अक्सर घुंघराले ब्रैकेट के अंदर परिभाषित किए जाते हैं, हालाँकि हमेशा नहीं। ब्लॉक फंक्शन के अंदर और दूसरे ब्लॉक के अंदर भी नेस्टेड हो सकते हैं।

नीचे दिए गए उदाहरण में 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 फंक्शन को flag आइडेंटिफायर पहली बार अंतिम printf स्टेटमेंट में दिखता है। अगर अंतिम 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 कहीं भी परिभाषित हो, उसका नाम उसी फंक्शन के अंदर अनोखा होना चाहिए। कोड को काम कराने के लिए किसी एक label का नाम बदलना पड़ेगा, ताकि दोनों अनोखे हो जाएँ, जैसा कि नीचे दिए गए उदाहरण में है

#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 स्टेटमेंट है, और उस 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'
GitHub के ज़रिए संपादित करें यह लिंक नई विंडो या टैब में खुलता है।