2009-08-27 21 views
7

मैंने विजुअल सी ++ 2008 पर इस कोड को आजमाया और यह दिखाता है कि ए और बी के पास एक ही पता नहीं है।सी ++ स्टैक और स्कोप

int main() 
{ 
    { 
     int A; 
     printf("%p\n", &A); 
    } 

    int B; 
    printf("%p\n", &B); 
} 

बढ़ी बल्कि अब मौजूद नहीं है जब बी परिभाषित हो जाता है, यह मेरे कि एक ही ढेर स्थान पुन: उपयोग किया जा सकता है लगता है ...

मुझे समझ नहीं आता क्यों संकलक नहीं है ऐसा लगता है कि एक बहुत ही सरल अनुकूलन की तरह दिखता है (जो उदाहरण के लिए बड़े चर और पुनरावर्ती कार्यों के संदर्भ में महत्वपूर्ण हो सकता है)। और ऐसा लगता है कि यह सीपीयू और न ही स्मृति पर भारी होगा। क्या किसी के पास इसके लिए स्पष्टीकरण है?

मुझे लगता है कि उत्तर "यह दिखने से कहीं अधिक जटिल है" के आधार पर है, लेकिन ईमानदारी से मुझे कोई जानकारी नहीं है।

संपादित करें: नीचे दिए गए उत्तरों और टिप्पणियों के बारे में कुछ सटीक।

इस कोड के साथ समस्या यह है कि प्रत्येक बार जब यह फ़ंक्शन कहा जाता है, तो स्टैक "एक पूर्णांक बहुत अधिक" बढ़ता है। बेशक यह उदाहरण में कोई समस्या नहीं है, लेकिन बड़े चर और रिकर्सिव कॉल पर विचार करें और आपके पास एक स्टैक ओवरफ़्लो है जिसे आसानी से टाला जा सकता है।

जो मैं सुझाव देता हूं वह एक स्मृति अनुकूलन है, लेकिन मुझे नहीं लगता कि यह प्रदर्शन को कैसे नुकसान पहुंचाएगा।

और वैसे, यह रिलीज बनाता है, सभी अनुकूलन पर होगा।

+0

आप एक रिलीज निर्माण या एक डीबग बिल्ड संकलन कर रहे हैं? – Michael

+1

जो आप सुझाव दे रहे हैं वह ** ** ** ** अनुकूलन है, लेकिन आवश्यक रूप से गति अनुकूलन नहीं है। –

+1

यदि सभी स्थानीय लोग कैश लाइन पर फिट होने के लिए बहुत बड़े हैं, तो यह एक गति अनुकूलन बन जाता है क्योंकि आपके पास कैश मिस नहीं है। – Michael

उत्तर

8

इस तरह के स्थानीय लोगों के लिए स्टैक स्पेस का उपयोग करना एक बहुत ही सामान्य अनुकूलन है। वास्तव में, एक अनुकूलित निर्माण पर, यदि आपने स्थानीय लोगों का पता नहीं लिया है, तो संकलक शायद स्टैक स्पेस आवंटित नहीं कर सकता है और वेरिएबल केवल एक रजिस्टर में ही रहेंगे।

शायद आपको यह कारण नहीं दिख रहा है कि कई कारणों से यह अनुकूलन होता है।

पहला, अगर अनुकूलन (एक डीबग बिल्ड की तरह) बंद कर रहे हैं संकलक इनमें से किसी काम नहीं चलेगा आसान डिबगिंग बनाने के लिए - आप इसे समारोह में नहीं रह गया है प्रयोग किया जाता है, भले ही एक के मूल्य में देख सकते हैं।

यदि आप अनुकूलन के साथ संकलित कर रहे हैं, तो मेरा अनुमान होगा कि आप स्थानीय का पता ले रहे हैं और इसे किसी अन्य फ़ंक्शन में पास कर रहे हैं, इसलिए संकलक स्टोर का पुन: उपयोग नहीं करना चाहता क्योंकि यह स्पष्ट नहीं है कि यह कार्य क्या कर रहा है पते के साथ।

कोई भी ऐसे संकलक की कल्पना कर सकता है जो इस अनुकूलन का उपयोग नहीं करेगा जब तक कि फ़ंक्शन द्वारा उपयोग की जाने वाली स्टैक स्पेस कुछ थ्रेसहोल्ड से अधिक न हो। मुझे ऐसा करने वाले किसी भी कंपाइलर के बारे में पता नहीं है, जो अब स्थानीय चर के स्थान का पुन: उपयोग करने के बाद शून्य लागत है और बोर्ड में लागू किया जा सकता है।

यदि स्टैक वृद्धि आपके आवेदन के लिए एक गंभीर चिंता है, यानी, कुछ स्थितियों में आप स्टैक ओवरफ्लो मार रहे हैं, तो आपको स्टैक स्पेस के कंपाइलर के अनुकूलन पर भरोसा नहीं करना चाहिए। आपको बड़े बफर को ढेर पर ढेर पर ले जाना चाहिए और बहुत गहरी रिकर्सन को खत्म करने के लिए काम करना चाहिए। उदाहरण के लिए, विंडोज थ्रेड पर डिफ़ॉल्ट रूप से 1 एमबी स्टैक होता है।आप बह निकला कि क्योंकि आप प्रत्येक ढेर फ्रेम पर स्मृति के 1k आवंटन कर रहे हैं और 1000 पुनरावर्ती कॉल गहरी जा रहा है, ठीक प्रत्येक ढेर फ्रेम के बंद कुछ स्थान बचाने के लिए संकलक मनाना करने की कोशिश करने के लिए नहीं है के बारे में चिंतित हैं, तो।

+0

मुझे नहीं लगता कि संकलक को ऊपर दिए गए उदाहरण में रजिस्टरों में ए और बी डालने की अनुमति है, क्योंकि ए और बी की जरूरत है। – AraK

+0

@ अरक - ठीक है, यही कारण है कि मैंने कहा "अगर आपने पता नहीं लिया"। – Michael

+0

@ माइकल, क्षमा करें मुझे यह नहीं देखा :) – AraK

1

शायद यह है कि संकलक दोनों स्टैक्स फ्रेम पर डाल रहा है। तो भले ही ए इसके दायरे के बाहर पहुंच योग्य नहीं है, फिर भी संकलक स्मृति में किसी स्थान पर बाध्य होने के लिए स्वतंत्र है जब तक कि कोड के अर्थशास्त्र को भ्रष्ट नहीं करता है। संक्षेप में, वे आपके मुख्य निष्पादित करते समय दोनों ही स्टैक पर डाल देते हैं।

+2

यह भी पता है: अपने कंपाइलर से ज्यादा स्मार्ट होने की कोशिश न करें।(कोई अपराध नहीं) – ebo

+0

मुझे पता है कि कंपाइलर को ऐसा करने की अनुमति है, लेकिन मुझे अभी भी आश्चर्य है कि वह ऐसा क्यों करता है :) – Drealmer

1

स्टैक के बाद बी बी कोड में एक (जो वैसे C90 द्वारा अनुमति नहीं है) के बाद घोषित किया जाता है पर आवंटित किया जाता है, लेकिन यह अभी भी मुख्य कार्य के शीर्ष दायरे में है और इस तरह से मौजूद है अंत तक मुख्य की शुरुआत। इसलिए जब मुख्य प्रारंभ होता है तो बी को धक्का दिया जाता है, ए को धक्का दिया जाता है जब आंतरिक दायरा दर्ज किया जाता है और जब यह छोड़ा जाता है तब पॉप किया जाता है, और फिर मुख्य कार्य छोड़ने पर बी पॉप हो जाता है।

+1

कंपाइलर स्टैक पर किसी भी क्रम में ए और बी को आवंटित करने के लिए स्वतंत्र है, क्योंकि कन्स्ट्रक्टर/विनाशकों को सही क्रम और स्थान पर निष्पादित किया जाता है। –

+0

उन्हें ढेर पर भी होने की आवश्यकता नहीं है। यदि उपरोक्त उदाहरण में पता नहीं लिया गया था, तो संकलक उन्हें केवल रजिस्टरों में छोड़ सकता है और फ़ंक्शन शून्य स्टैक स्पेस ले सकता है। – Michael

3

क्यों विधानसभा की जाँच नहीं?

मैं अपने कोड को हल्के से बदल इतना है कि पूर्णांक एक = 1; और int बी = 2; इसे समझने के लिए थोड़ा आसान बनाने के लिए।

जी से ++ डिफ़ॉल्ट सेटिंग के साथ:

.globl main 
    .type main, @function 
main: 
.LFB2: 
    leal 4(%esp), %ecx 
.LCFI0: 
    andl $-16, %esp 
    pushl -4(%ecx) 
.LCFI1: 
    pushl %ebp 
.LCFI2: 
    movl %esp, %ebp 
.LCFI3: 
    pushl %ecx 
.LCFI4: 
    subl $36, %esp 
.LCFI5: 
    movl $1, -8(%ebp) 
    leal -8(%ebp), %eax 
    movl %eax, 4(%esp) 
    movl $.LC0, (%esp) 
    call printf 
    movl $2, -12(%ebp) 
    leal -12(%ebp), %eax 
    movl %eax, 4(%esp) 
    movl $.LC0, (%esp) 
    call printf 
    movl $0, %eax 
    addl $36, %esp 
    popl %ecx 
    popl %ebp 
    leal -4(%ecx), %esp 
    ret 
.LFE2: 

अंत में इसे पसंद संकलक सिर्फ उन्हें एक ही पते में डालने के लिए परेशान नहीं किया लग रहा है। इसमें कोई फैंसी लुकहेड ऑप्टिमाइज़ेशन शामिल नहीं था। या तो यह अनुकूलित करने की कोशिश नहीं कर रहा था, या यह तय किया कि कोई फायदा नहीं हुआ।

सूचना एक असाइन किया गया है, और फिर मुद्रित। फिर बी को मूल स्रोत की तरह ही असाइन और मुद्रित किया जाता है। बेशक यदि आप विभिन्न कंपाइलर सेटिंग्स का उपयोग करते हैं तो यह पूरी तरह से अलग दिख सकता है।

2

मेरी जानकारी के बी के लिए जगह तक लाइन

int B; 

पर मुख्य करने के लिए प्रवेश पर आरक्षित है, और नहीं आपको लगता है कि लाइन से पहले डीबगर में तोड़ने के हैं, तो आप फिर भी की पता प्राप्त करने में सक्षम हैं बी। इस लाइन के बाद स्टैकपोइंटर भी नहीं बदलता है। इस लाइन पर होने वाली एकमात्र चीज यह है कि बी के निर्माता को बुलाया जाता है।

+0

सी ++ मानक में कुछ भी नहीं है जो इसे मना करता है या अनुमति देता है। तो, यह काफी संभव है कि आपका कंपाइलर वास्तव में बी के लिए स्मृति आवंटित करता है। अन्य कंपाइलर नहीं करते हैं। – MSalters

-1

संकलक वास्तव में इस मामले में कोई विकल्प नहीं है। यह printf() के किसी विशेष व्यवहार को नहीं मान सकता है। नतीजतन, यह मानना ​​चाहिए कि printf()&A पर तब तक लटका सकता है जब तक कि स्वयं अस्तित्व में न हो। इसलिए, एक स्वयं पूरे दायरे में रहता है जहां इसे परिभाषित किया जाता है।

+2

-1: * 'ए 'स्वयं पूरे दायरे में रहता है जहां इसे परिभाषित किया जाता है *। यही कारण है कि यह '{...} 'निर्माण में है, इसलिए जब संकलक' बी' के समय उस समय बाहर परिभाषित नहीं किया गया है। तो कंपाइलर ** ** एक विकल्प है। –

1

मेरे काम का एक बड़ा हिस्सा कंपाइलर्स से लड़ रहा है, और मुझे यह कहना है कि वे हमेशा ऐसा नहीं करते हैं जो हम, इंसान, उन्हें उम्मीद करते हैं। यहां तक ​​कि जब आपने कंपाइलर प्रोग्राम किया है, तब भी आप परिणामों पर आश्चर्यचकित हो सकते हैं, इनपुट मैट्रिक्स 100% भविष्यवाणी करना असंभव है।

कंपाइलर का अनुकूलन हिस्सा बहुत जटिल है और जैसा कि आपने देखा है, जो आपने देखा है, सेटिंग के लिए स्वैच्छिक प्रतिक्रिया के कारण हो सकता है, लेकिन यह आसपास के कोड के प्रभाव का परिणाम हो सकता है, या तर्क में इस अनुकूलन की अनुपस्थिति भी।

किसी भी मामले में, के रूप में माइकल कहते हैं, आप नहीं संकलक पर ढेर अतिप्रवाह को रोकने के लिए, भरोसा किया जाना चाहिए, क्योंकि आप बस समस्या को आगे बढ़ाने के लिए किया जा सकता है बाद में, जब सामान्य कोड रखरखाव या इनपुट का एक अलग सेट किया जाता है, और यह पाइपलाइन में शायद उपयोगकर्ता के हाथों में और अधिक दुर्घटनाग्रस्त हो जाएगा।

संबंधित मुद्दे