2012-04-06 13 views
21

जीसीसी प्रकार की बाहरी घोषणाओं की अनुमति क्यों देता है? क्या यह एक एक्सटेंशन है या मानक सी है? क्या इसके लिए स्वीकार्य उपयोग हैं?जीसीसी प्रकार शून्य (गैर-सूचक) की बाहरी घोषणाओं की अनुमति क्यों देता है?

मैं इसे एक विस्तार है अनुमान लगा रहा हूँ, लेकिन मैं भी नहीं मिलता है उस पर उल्लेख किया:
http://gcc.gnu.org/onlinedocs/gcc-4.3.6/gcc/C-Extensions.html

$ cat extern_void.c 
extern void foo; /* ok in gcc 4.3, not ok in Visual Studio 2008 */ 
void* get_foo_ptr(void) { return &foo; } 

$ gcc -c extern_void.C# no compile error 

$ gcc --version | head -n 1 
gcc (Debian 4.3.2-1.1) 4.3.2 

foo परिभाषित के रूप में प्रकार शून्य निश्चित रूप से एक संकलन त्रुटि है:

$ gcc -c -Dextern= extern_void.c 
extern_void.c:1: error: storage size of ‘foo’ isn’t known 

तुलना के लिए, विजुअल स्टूडियो 2008 बाहरी घोषणा पर एक त्रुटि देता है:

$ cl /c extern_void.c 
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86 
Copyright (C) Microsoft Corporation. All rights reserved. 

extern_void.c 
extern_void.c(1) : error C2182: 'foo' : illegal use of type 'void' 
+0

दिलचस्प बात यह है कि '-std = c89 -pedantic' gcc के साथ भी इस के साथ अच्छा है। – Dave

+0

जैसा कि मैं समझता हूं, प्रकार 'शून्य' प्रकार के एक चर को परिभाषित करना बीमार है, अपूर्ण प्रकार पर 'बाहरी' को लागू करना बीमार नहीं है, उदाहरण के लिए [उदाहरण] (http://ideone.com/v7VkF): ' अज्ञात आकार के साथ सरणी पर बाहरी ', जिसे बाद में परिभाषित किया गया है। हालांकि, ** §6.2.5.19 ** कहता है * "शून्य प्रकार में मूल्यों का एक खाली सेट होता है; यह एक अपूर्ण ऑब्जेक्ट प्रकार है जिसे पूरा नहीं किया जा सकता है।" *, यह देखते हुए कि आपके कोड को बाधा उल्लंघन के रूप में माना जाना चाहिए। तथ्य यह है कि यह '-pedantic' के साथ साफ रूप से संकलित करता है, यह कहता है कि यह एक विस्तार नहीं है यह जीसीसी बग या अस्पष्टता है जिसमें एमएसवीसी और जीसीसी मानक की व्याख्या करते हैं। –

उत्तर

6

आश्चर्यजनक रूप से पर्याप्त (या शायद अजीब नहीं ...) यह मुझे लगता है जैसे जीसीसी इसे स्वीकार करने के लिए सही है।

यदि यह घोषित किया गया था static बजाय extern की है, तो यह आंतरिक संबंध है, और §6.9.2/3 लागू होगा:

If the declaration of an identifier for an object is a tentative definition and has internal linkage, the declared type shall not be an incomplete type.

यह किसी भी भंडारण वर्ग (extern निर्दिष्ट नहीं किया है, तो में इस मामले), तो §6.7/7 लागू होगा:

If an identifier for an object is declared with no linkage, the type for the object shall be complete by the end of its declarator, or by the end of its init-declarator if it has an initializer; in the case of function arguments (including in prototypes), it is the adjusted type (see 6.7.5.3) that is required to be complete.

मैं इनमें से किसी दशा, void काम नहीं है, क्योंकि (§6.2.5/19):

The void type [...] is an incomplete type that cannot be completed.

इनमें से कोई भी लागू होता है, हालांकि। मुझे लगता है void है - मुझे यकीन है कि वास्तव में जानबूझकर है नहीं कर रहा हूँ

At least one type specifier shall be given in the declaration specifiers in each declaration, and in the specifier-qualifier list in each struct declaration and type name. Each list of type specifiers shall be one of the following sets (delimited by commas, when there is more than one set on a line); the type specifiers may occur in any order, possibly intermixed with the other declaration specifiers.

  • void
  • char
  • signed char

[ ... more types elided]

: यह §6.7.2/2, किस प्रकार void के साथ एक नाम की घोषणा की अनुमति देने के लिए लगता है की केवल आवश्यकताओं को छोड़ने के लिए लगता है वास्तव में व्युत्पन्न प्रकारों (जैसे, पॉइंटर टू शून्य) या फ़ंक्शन से रिटर्न प्रकार जैसी चीजों के लिए लक्षित है, लेकिन मुझे कुछ भी नहीं मिला जो सीधे उस प्रतिबंध को निर्दिष्ट करता है।

+0

§6.9.2 के मेरे पढ़ने वास्तव में तुम्हारा के विपरीत है: एक वस्तु एक प्रारंभकर्ता बिना गुंजाइश फ़ाइल है कि के लिए एक पहचानकर्ता के एक घोषणा, और एक भंडारण-वर्ग विनिर्देशन के बिना या भंडारण-वर्ग विनिर्देशक स्थिर साथ , का गठन किया एक टेटेबेटिव परिभाषा। इसका मतलब यह नहीं है कि यह एक टिकाऊ परिभाषा नहीं है और इस प्रकार हम एक अपूर्ण प्रकार की अनुमति नहीं है? Comau, आईएमओ, सही ढंग से शिकायत करता है। – dirkgently

+0

@dirkgently: यह निश्चित रूप से एक टेंटेटिव परिभाषा नहीं है, जिसे (§6.9.2/2) के रूप में परिभाषित किया गया है: "किसी ऑब्जेक्ट के लिए पहचानकर्ता की घोषणा जिसमें प्रारंभकर्ता के बिना फ़ाइल स्कोप है, और स्टोरेज-क्लास विनिर्देशक के बिना या स्टोरेज-क्लास विनिर्देशक स्थैतिक के साथ, एक * टेंटेटिव परिभाषा * का गठन करता है। " मुझे कुछ भी नहीं दिखता है जो कहता है कि अपूर्ण प्रकार की अनुमति नहीं है क्योंकि यह एक टिकाऊ परिभाषा नहीं है। –

+1

जीसीसी और एमएसवीसी के बीच अस्पष्टता *** §6.2.5.19 *** की व्याख्या में प्रतीत होती है, यदि 'शून्य 'एक अधूरा प्रकार है जिसे कभी पूरा नहीं किया जा सकता है, तो तर्क को' बाहरी 'होने की अनुमति दी जाती है एक अपूर्ण प्रकार पर लागू किया जाना चाहिए जो कभी पूरा नहीं हो सकता है, मेरा मानना ​​है कि यह कार्यान्वयन व्याख्या के लिए जगह छोड़ देता है। किसी भी तरह से, प्रत्येक कंपाइलर के लिए एक त्रुटि होगी, भले ही संकलन या लिंकिंग के दौरान त्रुटि उत्सर्जित हो, क्या संभवतः भिन्न हो सकता है और लगता है एक बढ़त मामला जो वे बहुत परेशान नहीं करना चाहते हैं। –

1

जीसीसी (भी, एलएलवीएम सी फ्रंटएंड) निश्चित रूप से छोटी है। हालांकि Comau और MS दोनों त्रुटियों की रिपोर्ट करना प्रतीत होता है।

ओपी के स्निपेट है कम से कम दो निश्चित यूबीएस और एक लाल हेरिंग:

J2. Undefined Behavior

[...] A program in a hosted environment does not define a function named main using one of the specified forms (5.1.2.2.1).

[यूबी #:

N1570 से

[यूबी # 1] की मेजबानी वातावरण में main लापता 2] यहां तक ​​कि यदि हम उपरोक्त को अनदेखा करते हैं, तब भी void अभिव्यक्ति का पता लेने का मुद्दा अभी भी जारी है जो स्पष्ट रूप से प्रतिबंधित है:

6.3.2.1 Lvalues, arrays, and function designators

1 An lvalue is an expression (with an object type other than void) that potentially designates an object;64)

और:

6.5.3.2 Address and indirection operators

Constraints

1T he operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.

: इसके अलावा, वहाँ मानक में एक खंड है [नोट lvalue मेरा पर जोर] विशेष रूप से void पर:

6.3.2.2 void

1 The (nonexistent) value of a void expression (an expression that has type void) shall not be used in any way, and implicit or explicit conversions (except to void) shall not be applied to such an expression.

एक फ़ाइल-गुंजाइश परिभाषा है एक प्राथमिक अभिव्यक्ति (6.5)। तो, foo द्वारा निर्दिष्ट ऑब्जेक्ट का पता ले रहा है। बीटीडब्ल्यू, उत्तरार्द्ध यूबी का आह्वान करता है। इस प्रकार स्पष्ट रूप से इनकार किया जाता है।

हमारे मामले में, प्रति foo रूप §6.2.2/5 के लिए:

5 [...] If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

यानी भले ही क्या पता लगा की बात है, तो दूर करने extern क्वालीफायर ऊपर वैध है या नहीं बनाता है हमने extern छोड़ा हम अभी भी एक ही समस्या में उतरेंगे।

+0

एक ऐसे लाभा का पता लेना कानूनी है जिसका मूल्य उपयोग योग्य नहीं है (उदाहरण के लिए एक स्थानीय चर जो कभी नहीं लिखा गया है)। यदि 'foo' को' void' प्रकार के बाहरी पहचानकर्ता के रूप में घोषित किया गया है, तो 'voo'' अभिव्यक्ति 'void * 'के वैध मान को प्राप्त करेगी। सी कोई माध्यम प्रदान नहीं करता है जिसके द्वारा 'foo' के लिए एक लिंक-टाइम रिज़ॉल्यूबल परिभाषा बनाई जा सकती है, लेकिन यदि कोई अन्य भाषा ऐसे पहचानकर्ता की परिभाषा की अनुमति देती है तो मुझे सी कंपेलरों को सी कोड को अपने पते तक पहुंचने की अनुमति देने से मना करने का कोई उपयोगी उद्देश्य नहीं दिखता है। – supercat

4

मैं

extern void foo; 

जब foo एक लिंक प्रतीक (एक बाहरी प्रतीक लिंकर द्वारा परिभाषित) कि अनिर्दिष्ट प्रकार का ऑब्जेक्ट का पता दर्शाता है है है की घोषणा के लिए ही वैध का उपयोग मिल गया है।

यह वास्तव में उपयोगी है क्योंकि लिंक प्रतीक अक्सर स्मृति की सीमा को संवाद करने के लिए उपयोग किए जाते हैं; यानी .text सेक्शन प्रारंभ पता, .text सेक्शन लंबाई, आदि

इस तरह, इन प्रतीकों का उपयोग करके उनके कोड को उचित मूल्य पर कास्टिंग करके कोड के लिए महत्वपूर्ण है। उदाहरण के लिए, यदि foo वास्तव में एक स्मृति क्षेत्र की लंबाई है:

uint32_t textLen; 

textLen = (uint32_t)foo; 

या, यदि foo है कि एक ही स्मृति क्षेत्र की शुरुआत का पता है:

uint8_t *textStart; 

textStart = (uint8_t *)foo; 

केवल वैकल्पिक एक लिंक संदर्भित करने के लिए जिस तरह से

extern uint8_t foo[]; 

मैं वास्तव में, void घोषणा पसंद करते हैं यह यह बनाता है के रूप में: "सी" में प्रतीक है कि मैं के बारे में पता एक बाहरी सरणी के रूप में यह घोषणा करने के लिए है स्पष्ट करें कि लिंकर परिभाषित प्रतीक में कोई अंतर्निहित "प्रकार" नहीं है।

+0

क्या आपके पास एक कोड उदाहरण है जहां इस विधि का उपयोग किया जाता है जिसे आप लिंक कर सकते हैं? –

+0

परिदृश्य में जहां 'foo' एक वास्तविक पता है, मुझे लगता है कि इसे सी कोड के भीतर एक पते के रूप में घोषित किया जाना चाहिए। एकमात्र समय मुझे लगता है कि 'शून्य' उचित होगा, ऐसे मामलों में जहां 'foo' वास्तव में एक पता नहीं है। आईएमएचओ, यह मानक के लिए यह उपयोगी होगा कि 'बाहरी शून्य' का अर्थ कार्यान्वयन परिभाषित किया गया है, एक प्रावधान के साथ कि कार्यान्वयन जो भी उपयुक्त दिखता है (जो इसे पूरी तरह से बाहर कर रहा है) पर जो भी प्रतिबंध लगाता है उसे लगा सकता है। – supercat

1

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

यह दृष्टिकोण बहुत ज्यादा प्लेटफ़ॉर्म-विशिष्ट है (विधानसभा भाषा का प्रयोग कर कुछ भी हो सकता है के रूप में), लेकिन यह संभव कुछ निर्माणों कि अन्य प्रकार से समस्या होगी बनाता है। इसका कुछ बुरा पहलू यह है कि यदि लेबल को unsigned char[] जैसे प्रकार के रूप में परिभाषित किया गया है, तो यह इंप्रेशन व्यक्त करेगा कि पता को संदर्भित किया जा सकता है या उस पर अंकगणित किया जा सकता है। एक संकलक void foo; स्वीकार करेंगे, तो (int)&foo लिंकर से सौंपा पता foo के लिए एक पूर्णांक के लिए एक ही सूचक करने वाली पूर्णांक अर्थ विज्ञान का उपयोग कर के रूप में किसी अन्य `शून्य के साथ लागू किया जाएगा परिवर्तित कर देंगे *।

मैं मैं कभी भी उस उद्देश्य के लिए उपयोग किया है void नहीं लगता कि (मैं हमेशा extern unsigned char[] का उपयोग किया है), लेकिन लगता होगा अगर कुछ एक वैध विस्तार (सी मानक में कुछ भी नहीं है कि भी आवश्यकता होती है के रूप में परिभाषित void क्लीनर होगा कोई भी लिंकर लिंकर प्रतीक बनाने के लिए कहीं भी मौजूद है जिसे एक विशिष्ट गैर-शून्य प्रकार के अलावा किसी अन्य चीज़ के रूप में उपयोग किया जा सकता है; प्लेटफ़ॉर्म पर जहां कोई लिंकर पहचानकर्ता बनाने के लिए कोई साधन मौजूद नहीं होगा, जिसे एक प्रोग्राम extern void के रूप में परिभाषित कर सकता है, वहां कोई आवश्यकता नहीं होगी कंप्यूटर्स के लिए इस वाक्यविन्यास की अनुमति देने के लिए)।

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

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