बिना लूप या सशर्त विवरण के 1 से 1000 तक प्रिंट करने वाला C कोड कैसे काम करता है?


148

मैंने Cकोड पाया है कि 1 से 1000 तक प्रिंट बिना लूप या सशर्त के : लेकिन मुझे समझ नहीं आता कि यह कैसे काम करता है। क्या कोई कोड के माध्यम से जा सकता है और प्रत्येक पंक्ति की व्याख्या कर सकता है?

#include <stdio.h>
#include <stdlib.h>

void main(int j) {
  printf("%d\n", j);
  (&main + (&exit - &main)*(j/1000))(j+1);
}

1
क्या आप C या C ++ के रूप में संकलन कर रहे हैं? आपको क्या त्रुटियां दिखाई देती हैं? आप mainC ++ में कॉल नहीं कर सकते ।
नवजलज

@ninjalj मैंने एक C ++ प्रोजेक्ट बनाया है और कोड को कॉपी / पिछले कोड में त्रुटि है: गैरकानूनी, लेफ्ट ऑपरेंड में 'void (__cdecl *) (int) टाइप है और एक्सप्रेशन एक ऑब्जेक्ट के लिए एक पूर्ण ऑब्जेक्ट प्रकार होना चाहिए
ob_dev

1
@ninjalj ये कोड ideone.org पर काम कर रहा है, लेकिन विजुअल स्टूडियो ideone.com/MtJ1M
ob_dev

@oussama ही तरह, लेकिन थोड़ा अधिक कठिन समझने के लिए: ideone.com/2ItXm तुम हो आपका स्वागत है। :)
मार्क

2
मैंने इन पंक्ति (& मुख्य + (और निकास - और मुख्य) * (j / 1000)) (j + 1) से सभी '&' अक्षर हटा दिए हैं; और यह कोड अभी भी काम करता है।
ob_dev

जवाबों:


264

कभी भी ऐसा कोड न लिखें।


के लिए j<1000, j/1000शून्य (पूर्णांक विभाजन) है। इसलिए:

(&main + (&exit - &main)*(j/1000))(j+1);

के बराबर है:

(&main + (&exit - &main)*0)(j+1);

जो है:

(&main)(j+1);

जो mainसाथ बुलाता है j+1

यदि j == 1000, फिर वही रेखाएँ निकलती हैं:

(&main + (&exit - &main)*1)(j+1);

जिसे उबाल आता है

(&exit)(j+1);

जो है exit(j+1)और कार्यक्रम छोड़ देता है।


(&exit)(j+1)और exit(j+1)अनिवार्य रूप से एक ही बात है - C99 ing6.3.2.1 / 4 को उद्धृत करना:

एक फ़ंक्शन डिज़ाइनर एक अभिव्यक्ति है जिसमें फ़ंक्शन प्रकार होता है। सिवाय इसके कि जब यह आकार ऑपरेटर या यूनिरी एंड ऑपरेटर का ऑपरेंड होता है , तो टाइप " फंक्शन रिटर्निंग टाइप " वाला फंक्शन डिज़ाइनर एक ऐसे एक्सप्रेशन में बदल जाता है, जिसमें " पॉइंटर टू फंक्शन रिटर्निंग टाइप " टाइप होता है

exitएक फ़ंक्शन डिज़ाइनर है। यहां तक ​​कि &ऑपरेटर के एकतरफा पते के बिना , इसे कार्य करने के लिए एक संकेतक के रूप में माना जाता है। ( &बस यह स्पष्ट करता है।)

और फ़ंक्शन कॉल §6.5.2.2 / 1 और निम्नलिखित में वर्णित हैं:

एक्सप्रेशन को निरूपित करने के लिए एक्सप्रेशन टाइप करने वाले एक्सप्रेशर में टाइप अर्र्टर होगा और एरे टाइप के अलावा किसी ऑब्जेक्ट को टाइप करने के लिए।

इसलिए exit(j+1)फ़ंक्शन प्रकार के एक पॉइंटर-टू-फंक्शन प्रकार के स्वचालित रूपांतरण के कारण (&exit)(j+1)काम करता है , और एक पॉइंटर-टू-फ़ंक्शन प्रकार के एक स्पष्ट रूपांतरण के साथ काम करता है।

कहा जा रहा है कि, उपरोक्त कोड अनुरूप नहीं है ( mainया तो दो तर्क या कोई भी नहीं लेता है), और &exit - &main, मेरा मानना ​​है कि .56.5.6 / 9 के अनुसार अपरिभाषित है:

जब दो बिंदुओं को घटाया जाता है, तो दोनों एक ही एरे ऑब्जेक्ट के तत्वों को इंगित करेंगे , या एरे ऑब्जेक्ट के अंतिम तत्व को एक अतीत; ...

जोड़ (&main + ...)अपने आप में मान्य होगा, और इस्तेमाल किया जा सकता है, अगर जोड़ा गया मात्रा शून्य था, क्योंकि .56.5.6 / 7 कहते हैं:

इन संचालकों के उद्देश्यों के लिए, एक ऑब्जेक्ट के लिए एक पॉइंटर जो कि एरे का तत्व नहीं है, एक एलीमेंट के समान है जो ऑब्जेक्ट के एलीमेंट के पहले एलीमेंट के साथ ऑब्जेक्ट के प्रकार के समान है।

इसलिए शून्य को जोड़ना &mainठीक होगा (लेकिन ज्यादा उपयोग नहीं)।


4
foo(arg)और (&foo)(arg)वे बराबर हैं, वे तर्क-वितर्क के साथ फू कहते हैं। newty.de/fpt/fpt.html फंक्शन पॉइंटर्स के बारे में एक दिलचस्प पेज है।
मैट

1
@ कृष्णभद्र: पहले मामले में, fooएक पॉइंटर है, &fooउस पॉइंटर का पता है। दूसरे मामले में, fooएक सरणी है, और &fooफू के बराबर है।
Mat

8
अनावश्यक रूप से जटिल, कम से कम C99 के लिए:((void(*[])()){main, exit})[j / 1000](j + 1);
प्रति जोहानसन

1
&foofooजब यह किसी सरणी में आता है तो समान नहीं होता है। &fooसरणी के fooलिए एक सूचक है, पहले तत्व के लिए एक संकेतक है। वे हालांकि एक ही मूल्य है। फ़ंक्शन के लिए, funऔर फ़ंक्शन के लिए &funदोनों संकेत हैं।
प्रति जोहानसन

1
FYI करें, यदि आप ऊपर दिए गए अन्य प्रश्न के प्रासंगिक उत्तर को देखते हैं, तो आप देखेंगे कि वास्तव में अनुकूल C99 में भिन्नता है। डरावना, लेकिन सच है।
डैनियल प्राइडेन

41

यह पुनरावर्ती, सूचक अंकगणितीय का उपयोग करता है, और पूर्णांक विभाजन के गोल व्यवहार का फायदा उठाता है।

यह j/1000शब्द सभी के लिए 0 से नीचे हो जाता है j < 1000; एक बार j1000 तक पहुंचने के बाद , यह 1 का मूल्यांकन करता है।

अब अगर आपके पास a + (b - a) * nहै, जहां nया तो 0 या 1 है, तो आप के साथ खत्म हो aअगर n == 0, और bयदि n == 1। का उपयोग करते हुए &main(का पता main()) और &exitके लिए aऔर b, अवधि (&main + (&exit - &main) * (j/1000))रिटर्न &mainजब j1000 से कम है, &exitअन्यथा। परिणामी फ़ंक्शन पॉइंटर को तब तर्क दिया जाता है j+1

यह संपूर्ण निर्माण पुनरावर्ती व्यवहार का परिणाम है: जबकि j1000 से नीचे है, mainखुद को पुनरावर्ती कहता है; जब jयह 1000 तक पहुंचता है, तो exitइसके बजाय यह कॉल करता है, जिससे प्रोग्राम एग्जिट कोड 1001 से बाहर हो जाता है (जो एक तरह से गंदा है, लेकिन काम करता है)।


1
अच्छा जवाब, लेकिन एक संदेह..कैसे बाहर निकलें 1001 कोड के साथ मुख्य? मुख्य कुछ भी नहीं लौटा रहा है। कोई डिफ़ॉल्ट वापसी मूल्य है?
कृष्णभद्र

2
जब j १००० तक पहुँचता है, तो मुख्य अपने आप में कोई पुनरावृत्ति नहीं करता है; इसके बजाय, यह libc फ़ंक्शन को कॉल करता है exit, जो बाहर निकलने के कोड को अपने तर्क के रूप में लेता है और, ठीक है, वर्तमान प्रक्रिया से बाहर निकलता है। उस बिंदु पर, j 1000 है, इसलिए j + 1 1001 के बराबर है, जो कि निकास कोड बन जाता है।
tdammers 29:11
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.