java - यह जावा कोड संकलित क्यों करता है?




compiler-construction (10)

विधि या वर्ग के दायरे में, नीचे दी गई रेखा संकलित (चेतावनी के साथ):

int x = x = 1;

कक्षा के दायरे में, जहां चर उनके डिफ़ॉल्ट मान प्राप्त करते हैं , निम्नलिखित 'अपरिभाषित संदर्भ' त्रुटि देता है:

int x = x + 1;

क्या यह पहला x = x = 1 समान 'अपरिभाषित संदर्भ' त्रुटि के साथ समाप्त नहीं होना चाहिए? या शायद दूसरी पंक्ति int x = x + 1 संकलित होना चाहिए? या कुछ ऐसा है जो मुझे याद आ रहा है?


tl; डॉ

क्षेत्रों के लिए , int b = b + 1 अवैध है क्योंकि b लिए एक अवैध आगे संदर्भ है। आप वास्तव में int b = this.b + 1 लिखकर इसे ठीक कर सकते हैं, जो शिकायतों के बिना संकलित करता है।

स्थानीय चर के लिए , int d = d + 1 अवैध है क्योंकि d उपयोग से पहले प्रारंभ नहीं किया गया है। यह फ़ील्ड के लिए मामला नहीं है, जो हमेशा डिफ़ॉल्ट-प्रारंभिक होते हैं।

संकलन करने का प्रयास करके आप अंतर देख सकते हैं

int x = (x = 1) + x;

एक फील्ड घोषणा के रूप में और स्थानीय परिवर्तनीय घोषणा के रूप में। पूर्व विफल हो जाएगा, लेकिन उत्तरार्द्ध अर्थशास्त्र में अंतर के कारण सफल होगा।

परिचय

सबसे पहले, क्षेत्र और स्थानीय चर प्रारंभिक के नियम बहुत अलग हैं। तो यह जवाब दो हिस्सों में नियमों से निपटेंगे।

हम इस परीक्षण कार्यक्रम का संपूर्ण उपयोग करेंगे:

public class test {
    int a = a = 1;
    int b = b + 1;
    public static void Main(String[] args) {
        int c = c = 1;
        int d = d + 1;
    }
}

b की घोषणा अमान्य है और एक illegal forward reference त्रुटि के साथ विफल रहता है।
d की घोषणा अमान्य है और एक variable d might not have been initialized साथ विफल variable d might not have been initialized त्रुटि variable d might not have been initialized

तथ्य यह है कि इन त्रुटियों को अलग-अलग संकेत देना चाहिए कि त्रुटियों के कारण भी अलग हैं।

खेत

जावा में फ़ील्ड प्रारंभकर्ता जेएलएस §8.3.2 , फ़ील्ड के आरंभ में शासित होते हैं।

किसी क्षेत्र का दायरा जेएलएस §6.3 , घोषणापत्र का दायरा में परिभाषित किया गया है।

प्रासंगिक नियम हैं:

  • किसी वर्ग प्रकार सी (§8.1.6) द्वारा घोषित या विरासत में प्राप्त सदस्य m की घोषणा का दायरा सी का पूरा शरीर है, जिसमें किसी भी नेस्टेड प्रकार की घोषणाएं शामिल हैं।
  • आवृत्ति चर के लिए प्रारंभिक अभिव्यक्ति वर्ग द्वारा घोषित या विरासत में प्राप्त किसी स्थैतिक चर के सरल नाम का उपयोग कर सकती है, यहां तक ​​कि जिसकी घोषणा पाठ के बाद में होती है।
  • आवृत्ति चर का उपयोग जिनकी घोषणा कभी-कभी प्रतिबंधित होने के बाद पाठ रूप से दिखाई देती है, भले ही इन आवृत्ति चर के दायरे में हों। आवृत्ति चर के आगे संदर्भ को नियंत्रित करने वाले सटीक नियमों के लिए §8.3.2.3 देखें।

§8.3.2.3 कहते हैं:

किसी सदस्य की घोषणा को टेक्स्ट से पहले प्रकट होने की आवश्यकता होती है, इसका उपयोग केवल तभी किया जाता है जब सदस्य कक्षा या इंटरफ़ेस सी का एक उदाहरण (क्रमशः स्थैतिक) फ़ील्ड है और निम्न सभी स्थितियों में निम्न है:

  • उपयोग सी के एक उदाहरण (क्रमशः स्थैतिक) परिवर्तनीय प्रारंभकर्ता में होता है या सी के उदाहरण (क्रमशः स्थैतिक) प्रारंभकर्ता में होता है।
  • उपयोग असाइनमेंट के बाईं ओर नहीं है।
  • उपयोग एक साधारण नाम के माध्यम से है।
  • सी उपयोग को घेरने वाली सबसे बड़ी कक्षा या इंटरफ़ेस है।

कुछ मामलों में छोड़कर, आप वास्तव में घोषित किए जाने से पहले फ़ील्ड का संदर्भ ले सकते हैं। इन प्रतिबंधों का उद्देश्य कोड को रोकने के लिए है

int j = i;
int i = j;

संकलन से। जावा स्पेक का कहना है, "उपरोक्त प्रतिबंधों को संकलित समय, परिपत्र या अन्यथा विकृत प्रारंभिकताओं पर पकड़ने के लिए डिज़ाइन किया गया है।"

वास्तव में इन नियमों को कैसे उबालते हैं?

संक्षेप में, नियम मूल रूप से कहते हैं कि आपको उस क्षेत्र के संदर्भ के पहले एक फ़ील्ड घोषित करना होगा यदि (ए) संदर्भ प्रारंभकर्ता में है, (बी) संदर्भ को असाइन नहीं किया जा रहा है, (सी) संदर्भ एक है सरल नाम ( this. तरह कोई क्वालीफायर नहीं this. ) और (डी) इसे किसी भीतरी कक्षा से एक्सेस नहीं किया जा रहा है। तो, एक अगली संदर्भ जो सभी चार स्थितियों को पूरा करता है वह अवैध है, लेकिन कम से कम एक शर्त पर विफल होने वाला एक आगे संदर्भ ठीक है।

int a = a = 1; संकलित करता है क्योंकि यह उल्लंघन करता है (बी): संदर्भ को असाइन किया जा रहा है, इसलिए पूर्ण घोषणा के पहले से ही संदर्भित करना कानूनी है।

int b = this.b + 1 भी संकलित करता है क्योंकि यह उल्लंघन करता है (सी): संदर्भ this.b एक साधारण नाम नहीं है (यह इसके साथ योग्य है this. )। यह अजीब निर्माण अभी भी पूरी तरह से परिभाषित है, क्योंकि इस this.b के मान शून्य है।

इसलिए, मूल रूप से, प्रारंभिकरणों के भीतर फ़ील्ड संदर्भों पर प्रतिबंध int a = a + 1 को सफलतापूर्वक संकलित होने से रोकते हैं।

निरीक्षण करें कि फ़ील्ड घोषणा int b = (b = 1) + b संकलित करने में विफल रहेगी, क्योंकि अंतिम b अभी भी एक अवैध आगे संदर्भ है।

स्थानीय चर

स्थानीय परिवर्तनीय घोषणाएं जेएलएस §14.4 , स्थानीय परिवर्तनीय घोषणा वक्तव्य द्वारा शासित होती हैं।

स्थानीय चर का दायरा जेएलएस §6.3 में परिभाषित किया गया है, घोषणा का दायरा:

  • ब्लॉक (§14.4) में स्थानीय परिवर्तनीय घोषणा का दायरा शेष ब्लॉक है जिसमें घोषणा प्रकट होती है, अपने प्रारंभिक शुरुआत से शुरू होती है और स्थानीय परिवर्तनीय घोषणा विवरण में दाईं ओर किसी और घोषणाकर्ता शामिल होती है।

ध्यान दें कि प्रारंभकर्ता घोषित किए जा रहे चर के दायरे में हैं। तो int d = d + 1; क्यों नहीं है int d = d + 1; संकलन?

कारण निश्चित असाइनमेंट ( जेएलएस §16 ) पर जावा के नियम के कारण है। असीमित असाइनमेंट मूल रूप से कहता है कि स्थानीय चर के प्रत्येक एक्सेस में उस चर के लिए एक पूर्ववर्ती असाइनमेंट होना चाहिए, और जावा कंपाइलर यह सुनिश्चित करने के लिए लूप और शाखाओं की जांच करता है कि किसी भी उपयोग से पहले असाइनमेंट हमेशा होता है (यही कारण है कि निश्चित असाइनमेंट में एक संपूर्ण विनिर्देश अनुभाग समर्पित है इसे करने के लिए)। मूल नियम है:

  • स्थानीय चर या रिक्त अंतिम फ़ील्ड x की प्रत्येक पहुंच के लिए, x को निश्चित रूप से एक्सेस से पहले असाइन किया जाना चाहिए, या संकलन-समय त्रुटि उत्पन्न होती है।

int d = d + 1; , d तक पहुंच को स्थानीय चर ठीक करने के लिए हल किया गया है, लेकिन d से पहले d को असाइन नहीं किया गया है, इसलिए संकलक एक त्रुटि जारी करता है। int c = c = 1 , c = 1 पहले होता है, जो c निर्दिष्ट करता है, और फिर उस असाइनमेंट के परिणामस्वरूप c प्रारंभ होता है (जो 1 है)।

ध्यान दें कि निश्चित असाइनमेंट नियमों के कारण, स्थानीय परिवर्तनीय घोषणा int d = (d = 1) + d; सफलतापूर्वक संकलित होगा (फील्ड घोषणा int b = (b = 1) + b विपरीत ), क्योंकि d निश्चित रूप से अंतिम d तक पहुंचने के समय निश्चित रूप से असाइन किया जाता है।


x x = x + 1 में प्रारंभ नहीं किया गया है;

जावा प्रोग्रामिंग भाषा स्थाई रूप से टाइप की गई है, जिसका अर्थ है कि सभी चरों को पहले इस्तेमाल किए जाने से पहले घोषित किया जाना चाहिए।

आदिम डेटा प्रकार देखें


कोड की रेखा चेतावनी से संकलित नहीं होती है क्योंकि कोड वास्तव में कैसे काम करता है। जब आप कोड int x = x = 1 कोड चलाते हैं, तो जावा पहले परिभाषित वैरिएबल x बनाता है। फिर यह असाइनमेंट कोड चलाता है ( x = 1 )। चूंकि x पहले ही परिभाषित है, सिस्टम में x से 1 को सेट करने में कोई त्रुटि नहीं है। यह मान 1 देता है, क्योंकि अब यह x का मान है। इसके लिए, x अब अंततः 1 के रूप में सेट है।
जावा मूल रूप से कोड को निष्पादित करता है जैसे कि यह था:

int x;
x = (x = 1); // (x = 1) returns 1 so there is no error

हालांकि, कोड के आपके दूसरे भाग में, int x = x + 1 , + 1 कथन के लिए x को परिभाषित करने की आवश्यकता होती है, जो तब तक नहीं है। चूंकि असाइनमेंट स्टेटमेंट्स का मतलब हमेशा होता है कि = के दाईं ओर वाला कोड पहले चलाया जाता है, कोड विफल हो जाएगा क्योंकि x अपरिभाषित है। जावा इस तरह कोड चलाएगा:

int x;
x = x + 1; // this line causes the error because `x` is undefined

कोड के आपके पहले भाग में प्लस के बजाय दूसरा = एक होता है। यह कहीं भी संकलित होगा जबकि कोड का दूसरा भाग किसी भी स्थान पर संकलित नहीं होगा।


जावा में या किसी भी आधुनिक भाषा में, असाइनमेंट दाएं से आता है।

मान लीजिए कि यदि आपके पास दो चर x और y हैं,

int z = x = y = 5;

यह कथन मान्य है और इस तरह संकलक उन्हें विभाजित करता है।

y = 5;
x = y;
z = x; // which will be 5

लेकिन आपके मामले में

int x = x + 1;

कंपाइलर ने अपवाद दिया क्योंकि, यह इस तरह विभाजित है।

x = 1; // oops, it isn't declared because assignment comes from the right.

दाएं से बाएं से शिकायत पढ़ने वाले बयान और हमने विपरीत करने के लिए डिज़ाइन किया है। यही कारण है कि यह पहली बार नाराज था। दाएं से बाएं बयान (कोड) पढ़ने के लिए इसे एक खरगोश बनाएं, आपको ऐसी समस्या नहीं होगी।


यह लगभग बराबर है:

int x;
x = 1;
x = 1;

सबसे पहले, int <var> = <expression>; हमेशा के बराबर है

int <var>;
<var> = <expression>;

इस मामले में, आपकी अभिव्यक्ति x = 1 , जो एक कथन भी है। x = 1 एक मान्य कथन है, क्योंकि var x पहले ही घोषित कर दिया गया है। यह मान 1 के साथ एक अभिव्यक्ति भी है, जिसे फिर x को असाइन किया जाता है।


int x = x + 1; आप एक्स में 1 जोड़ते हैं, तो x का मान क्या है, यह अभी तक नहीं बनाया गया है।

लेकिन int x=x=1; कोई त्रुटि के साथ संकलित होगा क्योंकि आप 1 से x असाइन करते हैं।


int x = x + 1;

चेतावनी के साथ विजुअल स्टूडियो 2008 में सफलतापूर्वक संकलित करता है

warning C4700: uninitialized local variable 'x' used`

int x = x = 1;

के बराबर है

int x = 1;
x = x; //warning here

जब में

int x = x + 1; 

सबसे पहले हमें x+1 गणना करने की आवश्यकता है लेकिन x का मान ज्ञात नहीं है, इसलिए आपको एक त्रुटि मिलती है (संकलक जानता है कि x का मान ज्ञात नहीं है)





compiler-construction