क्या "स्थिर" या "बाहरी" बिना "इनलाइन" C99 में कभी उपयोगी है?


96

जब मैं इस कोड को बनाने की कोशिश करता हूं

inline void f() {}

int main()
{
    f();
}

कमांड लाइन का उपयोग करना

gcc -std=c99 -o a a.c

मुझे एक लिंकर त्रुटि (अपरिभाषित संदर्भ f) मिलती है । त्रुटि गायब हो जाती है अगर मैं उपयोग करता हूं static inlineया extern inlineइसके बजाय inline, या यदि मैं संकलन करता हूं -O(इसलिए फ़ंक्शन वास्तव में इनलेट है)।

यह व्यवहार C99 मानक के पैरा 6.7.4 (6) में परिभाषित किया गया लगता है:

यदि अनुवाद इकाई में किसी फ़ंक्शन के लिए फ़ाइल स्कोप की सभी घोषणाओं में inlineफ़ंक्शन निर्दिष्ट के बिना शामिल है extern, तो उस अनुवाद इकाई में परिभाषा एक इनलाइन परिभाषा है। इनलाइन परिभाषा फ़ंक्शन के लिए एक बाहरी परिभाषा प्रदान नहीं करती है, और किसी अन्य अनुवाद इकाई में बाहरी परिभाषा को मना नहीं करती है। इनलाइन परिभाषा एक बाहरी परिभाषा का विकल्प प्रदान करती है, जिसका अनुवाद अनुवादक किसी भी कॉल को उसी अनुवाद इकाई में कार्य करने के लिए कार्यान्वित कर सकता है। यह अनिर्दिष्ट है कि फ़ंक्शन के लिए कॉल इनलाइन परिभाषा या बाहरी परिभाषा का उपयोग करता है या नहीं।

यदि मैं यह सब सही ढंग से समझता हूं, inlineतो उपरोक्त उदाहरण में परिभाषित फ़ंक्शन के साथ एक संकलन इकाई केवल लगातार संकलित करता है यदि एक ही नाम के साथ एक बाहरी फ़ंक्शन भी है, और मुझे कभी नहीं पता कि क्या मेरा अपना फ़ंक्शन या बाहरी फ़ंक्शन कहा जाता है।

क्या यह व्यवहार पूरी तरह से गलत नहीं है? यह एक समारोह को परिभाषित करने के कभी उपयोगी है inlineबिना staticया externC99 में? क्या मैं कुछ भूल रहा हूँ?

उत्तरों का सारांश

बेशक मैं कुछ याद कर रहा था, और व्यवहार नहीं है। :)

जैसा कि निमो बताते हैं , विचार यह है कि फ़ंक्शन की परिभाषा रखी जाए

inline void f() {}

हेडर फ़ाइल और केवल एक घोषणा में

extern inline void f();

इसी .c फ़ाइल में। केवल externघोषणा बाहरी रूप से दृश्यमान बाइनरी कोड की पीढ़ी को ट्रिगर करती है। और वास्तव inlineमें एक .c फ़ाइल का कोई उपयोग नहीं है - यह केवल हेडर में उपयोगी है।

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




@ एर्लज़: लिंक के लिए धन्यवाद। मेरा प्रश्न मानक के उद्धृत पैराग्राफ के पीछे तर्क के बारे में है, और यदि कभी भी inlineबिना staticऔर इसके लिए उपयोग के मामले हैं extern। दुर्भाग्य से, इन मुद्दों में से कोई भी उस प्रश्न में शामिल नहीं है।
स्वेन मार्नाच

@ नीमो: मैंने अपना प्रश्न पोस्ट करने से पहले उस प्रश्न और उत्तर को पढ़ा। फिर, मैं यह नहीं पूछ रहा हूं कि "यह कैसे व्यवहार करता है?" बल्कि "इस व्यवहार के पीछे क्या विचार है"? मुझे पूरा यकीन है कि मुझे यहाँ कुछ याद आ रहा है।
स्वेन मार्नाच

1
हाँ, इसीलिए मैंने कहा "बॉर्डरलाइन"। मुझे व्यक्तिगत रूप से लगता है कि यह पूरी तरह से अलग सवाल पूछ रहा है
अर्लज़

जवाबों:


40

वास्तव में यह उत्कृष्ट उत्तर भी आपके प्रश्न का उत्तर देता है, मुझे लगता है:

बाहरी इनलाइन क्या करती है?

विचार यह है कि "इनलाइन" का उपयोग हेडर फ़ाइल में किया जा सकता है, और फिर एक .c फ़ाइल में "इनरलाइन इनलाइन"। "एक्सटर्नल इनलाइन" सिर्फ यह है कि आप कंपाइलर को किस तरह से निर्देश देते हैं कि ऑब्जेक्ट फ़ाइल में (बाह्य रूप से दृश्यमान) उत्पन्न कोड होना चाहिए।

[अपडेट, विस्तृत करने के लिए]

मुझे नहीं लगता कि "इनलाइन" ("स्टैटिक" या "एक्सटर्नल") के लिए किसी भी तरह का कोई उपयोग एक सीसी फाइल में होता है। लेकिन हेडर फ़ाइल में यह समझ में आता है, और इसके लिए कुछ .c फ़ाइल में संबंधित "बाहरी इनलाइन" घोषणा की आवश्यकता होती है जो वास्तव में स्टैंड-अलोन कोड उत्पन्न करता है।


1
धन्यवाद, मैं विचार प्राप्त करना शुरू कर रहा हूं!
स्वेन मार्नाच

8
हाँ, मैंने कभी भी इसे अभी तक खुद नहीं समझा, इसलिए :-) पूछने के लिए धन्यवाद। यह अजीब है कि हेडर में गैर-बाहरी "इनलाइन" परिभाषा जाती है (लेकिन जरूरी नहीं कि किसी भी कोड पीढ़ी में परिणाम हो), जबकि "एक्सटर्न इनलाइन" घोषणा .c फ़ाइल में जाती है और वास्तव में कोड का कारण बनती है। उत्पन्न होना।
निमो

3
क्या इसका मतलब है कि मैं inline void f() {}हेडर और extern inline void f();.c फाइल में लिखता हूं ? तो वास्तविक फ़ंक्शन की परिभाषा हेडर में जाती है और .c फ़ाइल में इस मामले में एक मात्र घोषणा होती है, सामान्य क्रम के विपरीत?
स्वेन मार्नाच

1
@endolith: मुझे आपका सवाल समझ नहीं आ रहा है। हम inline बिना चर्चा कर रहे हैं staticया extern। बेशक static inlineठीक है, लेकिन यह वह नहीं है जिसके बारे में यह सवाल और जवाब है।
निमो

2
@ MatthieuMoy आपको -std=c99इसके बजाय उपयोग करने की आवश्यकता है -std=gnu89
a3f

27

मानक से (ISO / IEC 9899: 1999) स्वयं:

परिशिष्ट J.2 अपरिभाषित व्यवहार

  • ...
  • बाहरी लिंकेज के साथ एक inlineफ़ंक्शन एक फ़ंक्शन स्पेसियर के साथ घोषित किया जाता है, लेकिन एक ही अनुवाद इकाई (6.7.4) में भी परिभाषित नहीं किया गया है।
  • ...

C99 समिति ने एक तर्क लिखा , और यह कहता है:

6.7.4 फ़ंक्शन विनिर्देशक

C99 की एक नई सुविधा:inline कीवर्ड, सी से अनुकूलित ++, एक है समारोह-विनिर्देशक है कि केवल समारोह घोषणाओं में इस्तेमाल किया जा सकता। यह प्रोग्राम ऑप्टिमाइज़ेशन के लिए उपयोगी है जिसे कॉल की साइट पर दिखाई देने वाली फ़ंक्शन की परिभाषा की आवश्यकता होती है। (ध्यान दें कि मानक इन अनुकूलन की प्रकृति को निर्दिष्ट करने का प्रयास नहीं करता है।)

यदि फ़ंक्शन में आंतरिक लिंकेज है, या यदि उसके पास बाहरी लिंकेज है, तो दृश्यता सुनिश्चित है और कॉल बाहरी परिभाषा के समान अनुवाद इकाई में है। इन मामलों में, inlineफ़ंक्शन की घोषणा या परिभाषा में कीवर्ड की मौजूदगी का इस बात पर कोई प्रभाव नहीं पड़ता है कि वरीयता के संकेत से परे उस फ़ंक्शन के कॉल को inlineकीवर्ड के बिना घोषित किए गए अन्य फ़ंक्शन के कॉल के लिए प्राथमिकता दी जानी चाहिए ।

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

एक प्रोग्राम में एक बाहरी परिभाषा के साथ एक अनुवाद इकाई, एक इनलाइन परिभाषा के साथ एक अनुवाद इकाई और एक घोषणा के साथ एक अनुवाद इकाई हो सकती है, लेकिन एक फ़ंक्शन के लिए कोई परिभाषा नहीं होती है। बाद की अनुवाद इकाई में कॉल बाहरी परिभाषा का हमेशा की तरह उपयोग करेगी।

किसी फ़ंक्शन की इनलाइन परिभाषा को बाहरी परिभाषा की तुलना में एक अलग परिभाषा माना जाता है। यदि funcबाहरी लिंकेज के साथ कुछ फ़ंक्शन के लिए कॉल होता है जहां एक इनलाइन परिभाषा दिखाई देती है, तो व्यवहार वैसा ही होता है जैसे कि कॉल किसी अन्य फ़ंक्शन के लिए किया गया था, कहते हैं __func, आंतरिक लिंकेज के साथ। एक अनुरूपता कार्यक्रम पर निर्भर नहीं होना चाहिए कि कौन सा फ़ंक्शन कहा जाता है। यह मानक में इनलाइन मॉडल है।

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

inline const char *saddr(void)
{
    static const char name[] = "saddr";
    return name;
}
int compare_name(void)
{
    return saddr() == saddr(); // unspecified behavior
}

चूंकि कार्यान्वयन कॉल में से एक के लिए इनलाइन परिभाषा का उपयोग कर सकता है saddrऔर दूसरे के लिए बाहरी परिभाषा का उपयोग कर सकता है, समानता ऑपरेशन 1 (सच) का मूल्यांकन करने की गारंटी नहीं है। इससे पता चलता है कि इनलाइन परिभाषा के भीतर परिभाषित स्थिर वस्तुएं बाहरी परिभाषा में अपनी संबंधित वस्तु से अलग हैं। इसने constइस प्रकार की एक गैर- वस्तु को परिभाषित करने के खिलाफ बाधा को भी प्रेरित किया ।

इनलाइनिंग को मानक में इस तरह से जोड़ा गया था कि इसे मौजूदा लिंकर तकनीक के साथ लागू किया जा सके, और C99 इनलाइनिंग का एक उपसमूह C ++ के साथ संगत है। यह आवश्यक है कि इनलाइन फ़ंक्शन की परिभाषा वाले एक अनुवाद इकाई को फ़ंक्शन के लिए बाहरी परिभाषा प्रदान करने वाले के रूप में निर्दिष्ट किया जाए। क्योंकि उस विनिर्देश में केवल एक घोषणा होती है जिसमें या तो inlineकीवर्ड की कमी होती है , या जिसमें दोनों होते हैं inlineऔर externयह C ++ अनुवादक द्वारा भी स्वीकार किया जाएगा।

C99 में Inlining दो तरीकों से C ++ विनिर्देशन का विस्तार करता है। सबसे पहले, यदि कोई कार्य inlineएक अनुवाद इकाई में घोषित किया जाता है, तो उसे inlineहर दूसरी अनुवाद इकाई में घोषित करने की आवश्यकता नहीं है । यह अनुमति देता है, उदाहरण के लिए, एक लाइब्रेरी फ़ंक्शन जिसे लाइब्रेरी के भीतर इनलाइन किया जाना है, लेकिन केवल एक बाहरी परिभाषा के माध्यम से कहीं और उपलब्ध है। बाहरी फ़ंक्शन के लिए आवरण फ़ंक्शन का उपयोग करने के विकल्प के लिए एक अतिरिक्त नाम की आवश्यकता होती है; और यह भी प्रतिकूल प्रभाव प्रदर्शन कर सकते हैं अगर एक अनुवादक वास्तव में इनलाइन प्रतिस्थापन नहीं करता है।

दूसरा, एक इनलाइन फ़ंक्शन की सभी परिभाषाएँ "बिल्कुल समान" होने की आवश्यकता को इस आवश्यकता से बदल दिया जाता है कि प्रोग्राम का व्यवहार इस बात पर निर्भर नहीं होना चाहिए कि क्या एक दृश्य इनलाइन परिभाषा के साथ लागू किया गया है, या बाहरी परिभाषा, समारोह। यह एक इनलाइन परिभाषा को एक विशेष अनुवाद इकाई के भीतर इसके उपयोग के लिए विशेषीकृत करने की अनुमति देता है। उदाहरण के लिए, किसी लाइब्रेरी फ़ंक्शन की बाहरी परिभाषा में कुछ तर्क सत्यापन शामिल हो सकते हैं जो उसी लाइब्रेरी में अन्य फ़ंक्शन से किए गए कॉल के लिए आवश्यक नहीं हैं। ये एक्सटेंशन कुछ फायदे प्रदान करते हैं; और प्रोग्रामर जो संगतता के बारे में चिंतित हैं, वे सख्त सी ++ नियमों का पालन कर सकते हैं।

ध्यान दें कि मानक हेडर में मानक पुस्तकालय कार्यों की इनलाइन परिभाषा प्रदान करने के लिए कार्यान्वयन के लिए यह उचित नहीं है क्योंकि यह कुछ विरासत कोड को तोड़ सकता है जो मानक पुस्तकालय कार्यों को उनके हेडर सहित शामिल करने के बाद पुनः परिभाषित करता है। inlineकीवर्ड केवल कार्यों को इनलाइन करने का सुझाव देने के लिए एक पोर्टेबल तरीके के साथ उपयोगकर्ताओं को प्रदान करने का इरादा है। क्योंकि मानक शीर्षकों को पोर्टेबल होने की आवश्यकता नहीं है, कार्यान्वयन में अन्य विकल्प हैं:

#define abs(x) __builtin_abs(x)

या अन्य गैर-पोर्टेबल तंत्र मानक पुस्तकालय कार्यों को इनलाइन करने के लिए।


धन्यवाद, यह बहुत विस्तृत है - और केवल मानक के रूप में पठनीय है। :-) मुझे पता था कि मुझे कुछ याद आ रहा होगा। क्या आप एक संदर्भ दे सकते हैं कि आपको यह कहाँ से मिला है?
स्वेन मार्नाच

यह मेरे दिमाग को पार कर गया है कि आप लिंक खोजने के लिए Google का उपयोग कर सकते हैं: open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
स्वेन मार्नाच

@Sven: मैंने आपकी खोज से URL उधार लिया और उत्तर में डाल दिया। मैंने जिस दस्तावेज़ का उपयोग किया था, वह पहले (2005 में) सहेजे गए तर्क की एक प्रति थी, लेकिन यह भी V5.10 था।
जोनाथन लेफ़लर

4
एक बार फिर धन्यवाद। मुझे उत्तर से मिली सबसे मूल्यवान अंतर्दृष्टि यह है कि सी 99 मानक के औचित्य वाला एक दस्तावेज है (और शायद अन्य मानकों के लिए भी दस्तावेज हैं)। जबकि निमो के जवाब ने मुझे एक मछली दी, इसने मुझे मछली सिखाई।
स्वेन मार्नाच

0

> मुझे एक लिंकर त्रुटि मिलती है (अपरिभाषित संदर्भ f)

यहां काम करता है: लिनक्स x86-64, जीसीसी 4.1.2। आपके कंपाइलर में बग हो सकता है; मुझे उस मानक से उद्धृत पैराग्राफ में कुछ भी दिखाई नहीं देता है जो दिए गए प्रोग्राम को मना करता है। नोट के उपयोग करता है, तो बजाय iff

इनलाइन परिभाषा एक बाहरी परिभाषा का विकल्प प्रदान करती है , जिसका अनुवाद अनुवादक किसी भी कॉल को उसी अनुवाद इकाई में कार्य करने के लिए कार्यान्वित कर सकता है

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


2
मानक कहता है कि कंपाइलर हमेशा मान सकता है कि एक ही नाम के साथ एक बाहरी फ़ंक्शन है और इनलाइन संस्करण के बजाय इसे कॉल करें, इसलिए मुझे नहीं लगता कि यह एक कंपाइलर बग है। मैंने gcc के साथ कोशिश की 4.3, 4.4 और 4.5, सभी एक लिंकर त्रुटि देते हैं।
स्वेन मार्नाच
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.