c++ - अजीब कोड जो g ++ के साथ संकलित करता है





gcc c++11 g++ (4)


आप इसका उपयोग कर सकते हैं: http://gcc.godbolt.org/ असेंबली देखने के लिए ..

int main()
{
    int(*)() = 0;
    return 0;
}

उत्पन्न करता है:

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    popq    %rbp
    ret

जो बराबर है: int main() {return 0;} तो कोई अनुकूलन के साथ भी, जीसीसी सिर्फ इसके लिए असेंबली उत्पन्न नहीं करता है .. क्या यह चेतावनी या त्रुटि दे सकता है? मेरे पास कोई सुराग नहीं है लेकिन यह अज्ञात func सूचक के लिए कोई परवाह नहीं करता है या कुछ भी नहीं करता है।

तथापि:

int main()
{
    int (*p)() = 0;
    return 0;
}

कोई अनुकूलन उत्पन्न नहीं होगा:

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movq    $0, -8(%rbp)
    movl    $0, %eax
    popq    %rbp
    ret

जो ढेर पर 8 बाइट आवंटित करता है ..

निम्न कोड जी ++ 4.8.1 के साथ सफलतापूर्वक संकलित करता है:

int main()
{
    int(*)();
}

यह काम करने के लिए एक सूचक की एक सरल घोषणा की तरह दिखता है:

int(*f)();

यह क्लैंग 3.4 और वीसी ++ 2013 के साथ संकलित नहीं है।

क्या यह एक कंपाइलर बग या मानक के अंधेरे स्थानों में से एक है?

समान अजीब कोड टुकड़ों की सूची जो g ++ 4.8.1 (अपडेटेड) के साथ ठीक संकलित करती हैं:

  1. int(*)();

  2. int(*);

  3. int(*){};

  4. int(*());

इन अजीब कोड टुकड़ों के साथ लाइव उदाहरण

अपडेट 1: @Ali ने टिप्पणियों में कुछ रोचक जानकारी जोड़े:

सभी 4 मामले क्लैंग 3.5 ट्रंक (2025 9 4) के साथ एक संकलन त्रुटि देते हैं और जीसीसी 4.9 ट्रंक (20140302) के साथ ठीक संकलित करते हैं। int(*){}; को छोड़कर व्यवहार -std=c++98 -pedantic साथ समान है int(*){}; जो समझ में आता है; विस्तारित प्रारंभकर्ता सूचियां केवल -std=c++11 साथ उपलब्ध हैं।

अद्यतन 2: जैसा कि @CantChooseUsernames ने अपने उत्तर में उल्लेख किया है, वे अभी भी प्रारंभिकता के साथ भी ठीक संकलित करते हैं और बिना किसी सक्षम अनुकूलन के भी g ++ (न तो @CantChooseUsernames साथ और न ही प्रारंभिक) द्वारा उनके लिए कोई असेंबली उत्पन्न नहीं होती है:

  1. int(*)() = 0;

  2. int(*) = 0;

  3. int(*){} = 0;

  4. int(*()) = 0;

प्रारंभिकरण के साथ लाइव उदाहरण

अद्यतन 3: मैं वास्तव में यह जानकर आश्चर्यचकित था कि int(*)() = "Hello, world!"; ठीक है, भी (जबकि int(*p)() = "Hello, world!"; संकलन नहीं करता है)।

अद्यतन 4: यह शानदार है लेकिन int(*){} = Hello, world!; ठीक संकलित करता है। और कोड का निम्नलिखित अत्यंत अजीब टुकड़ा भी: int(*){}() = -+*/%&|^~.,:!?$()[]{}; ( लाइव उदाहरण )।

अपडेट 5: जैसा कि @zwol ने अपनी टिप्पणी में उल्लेख किया था

यह और कई संबंधित वाक्य रचनात्मक समस्याओं को जीसीसी बग 68265 के रूप में ट्रैक किया जा रहा है।




मुझे यकीन नहीं है कि इससे कितना मदद मिलती है, लेकिन मैंने निम्नलिखित की कोशिश की (क्लैंग 3.3, जी ++ 4.8.1):

using P = int(*)();
using Q = int*;
P; // warning only
Q; // warning only
int(*)(); // error (but only in clang)
int*;     // error
int(*p)(); // ok
int *q;    // ok

दूसरी ओर, सब कुछ g ++ 4.8.2 और 4.9.0 में ठीक संकलित करता है। दुर्भाग्यवश, मेरे पास 3.4 नहीं है।

बहुत मोटे तौर पर , एक घोषणा [आईएसओ सेक्शन 7] क्रम में निम्नलिखित भागों में शामिल है:

  1. वैकल्पिक उपसर्ग विनिर्देशक (जैसे static , virtual )
  2. आधार प्रकार (जैसे const double , vector<int> )
  3. घोषणाकर्ता (उदाहरण के लिए n , *p , a[7] , f(int) )
  4. वैकल्पिक प्रत्यय समारोह विनिर्देशक (उदाहरण के लिए noexcept , noexcept )
  5. वैकल्पिक प्रारंभकर्ता या कार्य निकाय (उदाहरण = {1,2,3} या { return 0; }

अब, एक घोषणाकर्ता में लगभग एक नाम होता है और वैकल्पिक रूप से कुछ घोषणाकर्ता ऑपरेटरों [आईएसओ 8/4] होते हैं।

उपसर्ग ऑपरेटर, उदाहरण के लिए:

  • * (सूचक)
  • *const (स्थिर सूचक)
  • & (lvalue संदर्भ)
  • && (रावल्यू संदर्भ)
  • auto (फ़ंक्शन रिटर्न प्रकार, पीछे पीछे)

पोस्टफिक्स ऑपरेटर, उदाहरण के लिए:

  • [] (सरणी)
  • () (समारोह)
  • -> (फ़ंक्शन पीछे वापसी प्रकार)

उपरोक्त ऑपरेटरों को अभिव्यक्तियों में उनके उपयोग को दर्शाने के लिए डिज़ाइन किया गया था। पोस्टफिक्स ऑपरेटर उपसर्ग की तुलना में कड़े बाध्य होते हैं, और ब्रांड्स का उपयोग उनके ऑर्डर को बदलने के लिए किया जा सकता है: int *f() एक पॉइंटर को int लौटने वाला फ़ंक्शन है, जबकि int (*f)() एक फ़ंक्शन रिटर्निंग int लिए एक पॉइंटर है।

शायद मैं गलत हूं, लेकिन मुझे लगता है कि ये ऑपरेटरों नाम के बिना घोषणा में नहीं हो सकते हैं। तो जब हम int *q; लिखते हैं int *q; , तो int आधार प्रकार है, और *q है जिसमें उपसर्ग ऑपरेटर * नाम q बाद होता है। लेकिन int *; खुद से प्रकट नहीं हो सकता है।

दूसरी तरफ, जब हम using Q = int*; परिभाषित using Q = int*; , फिर घोषणा Q; खुद ही ठीक है क्योंकि Q मूल प्रकार है। बेशक, क्योंकि हम कुछ भी घोषित नहीं कर रहे हैं, हम संकलक विकल्पों के आधार पर एक त्रुटि या चेतावनी प्राप्त कर सकते हैं, लेकिन यह एक अलग त्रुटि है।

उपर्युक्त सिर्फ मेरी समझ है। मानक (उदाहरण के लिए N3337) कहता है [आईएसओ 8.3 / 1] है:

प्रत्येक घोषणाकर्ता में बिल्कुल एक घोषणाकर्ता-आईडी होता है ; यह घोषित पहचानकर्ता का नाम है। एक घोषणाकर्ता-आईडी में होने वाली एक अयोग्य आईडी एक विशेष पहचानकर्ता (12.3 [ उपयोगकर्ता परिभाषित रूपांतरण ], 12.4 [विनाशक], 13.5 [अधिभारित ऑपरेटरों]) की घोषणा के अलावा और टेम्पलेट विशेषज्ञता की घोषणा के लिए एक सरल पहचानकर्ता होगी या आंशिक विशेषज्ञता (14.7)।

(स्क्वायर ब्रैकेट में नोट्स मेरा हैं)। तो मैं int(*)(); समझता हूं int(*)(); अमान्य होना चाहिए और मैं यह नहीं कह सकता कि क्यों यह clang और g ++ के विभिन्न संस्करणों में अलग व्यवहार है।




सी ++ मानक के अनुसार (धारा 7 घोषणाओं के पृष्ठ # 6)

6 init-declarator-list में प्रत्येक इनिट-घोषणाकर्ता में बिल्कुल एक घोषणाकर्ता-आईडी होता है , जिसे उस init-declarator द्वारा घोषित किया गया नाम है और इसलिए घोषणापत्र द्वारा घोषित नामों में से एक

तो यह बस एक कंपाइलर बग है।

वैध कोड उदाहरण के लिए देख सकता है (आपके द्वारा दिखाए गए फ़ंक्शन पॉइंटर घोषणा के अलावा) हालांकि मैं इसे अपने एमएस वीसी ++ 2010 के साथ संकलित नहीं कर सकता।

int(*p){};

ऐसा लगता है कि परीक्षण के लिए आप जिस कंपाइलर का उपयोग कर रहे हैं वह घोषणाकर्ता-आईडी के बिना घोषणाओं की अनुमति देता है।

धारा 8.1 प्रकार के नामों के निम्नलिखित पैराग्राफ को भी ध्यान में रखें

1 टाइप रूपांतरणों को स्पष्ट रूप से निर्दिष्ट करने के लिए, और आकार, alignof, new, या टाइपिड के तर्क के रूप में, किसी प्रकार का नाम निर्दिष्ट किया जाएगा। यह एक प्रकार-आईडी के साथ किया जा सकता है, जो वाक्य के रूप में एक चर या उस प्रकार के फ़ंक्शन के लिए घोषणा है जो इकाई के नाम को छोड़ देता है।




यह tutorial उपयोगी हो सकता है (ध्यान दें कि इसमें पुराने सीजी सामान के अलावा जीएलएसएल सामग्री शामिल है)।

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





c++ gcc c++11 g++