C ++ में 'स्ट्रक्चर' और 'टाइपफेड स्ट्रक्चर' के बीच अंतर?


जवाबों:


1201

C ++ में, केवल एक सूक्ष्म अंतर है। यह C से होल्डओवर है, जिसमें इससे फर्क पड़ता है।

C भाषा मानक ( C89 §3.1.2.3 , C99 .26.2.3 , और C11 §6.2.3 ) पहचानकर्ताओं की विभिन्न श्रेणियों के लिए अलग-अलग नेमस्पेस अनिवार्य करता है, जिसमें टैग आइडेंटिफ़ायर ( struct/ union/ enum) और सामान्य पहचानकर्ता (के लिए typedefऔर अन्य पहचानकर्ता) शामिल हैं ।

अगर आपने अभी कहा:

struct Foo { ... };
Foo x;

आपको संकलक त्रुटि मिलेगी, क्योंकि Fooकेवल टैग नामस्थान में परिभाषित किया गया है।

आपको इसे घोषित करना होगा:

struct Foo x;

जब भी आप किसी को संदर्भित करना चाहते हैं Foo, तो आपको हमेशा इसे कॉल करना होगा struct Foo। यह तेजी से कष्टप्रद हो जाता है, इसलिए आप एक जोड़ सकते हैं typedef:

struct Foo { ... };
typedef struct Foo Foo;

अब struct Foo(टैग नेमस्पेस में) और सिर्फ प्लेन Foo(साधारण आइडेंटिफ़ायर नेमस्पेस में) दोनों एक ही चीज़ को संदर्भित करते हैं, और आप कीवर्ड के Fooबिना स्वतंत्र रूप से टाइप की वस्तुओं की घोषणा कर सकते हैं struct


निर्माण:

typedef struct Foo { ... } Foo;

सिर्फ घोषणा और के लिए एक संक्षिप्त नाम है typedef


आखिरकार,

typedef struct { ... } Foo;

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


C ++ में, सभी struct/ union/ enum/ classघोषणाएँ ऐसे कार्य करती हैं जैसे कि वे अंतर्निहित typedefरूप से संपादित हों, जब तक कि नाम उसी नाम के साथ किसी अन्य घोषणा द्वारा छिपा न हो। देखें माइकल बर्र के जवाब में पूरी जानकारी के लिए।


53
जबकि आप जो कहते हैं वह सच है, AFAIK, कथन, 'typedef structure {...} Foo;' एक अनाम संरचना के लिए एक उपनाम बनाता है।
20kg पर dirkgently

25
अच्छी पकड़, "टाइपडिफ स्ट्रक्चर फू {{}} फू" के बीच एक सूक्ष्म अंतर है; और "टंकण संरचना {{}} फू;"।
एडम रोसेनफील्ड

8
सी में, उपरोक्त टैग, संघ टैग और गणना टैग एक नेमस्पेस साझा करते हैं, बजाय दो (स्ट्रक्चर एंड यूनियन) ऊपर बताए अनुसार दो का उपयोग करते हुए; टाइपसेफ नामों के लिए संदर्भित नामस्थान वास्तव में अलग है। इसका मतलब है कि आप दोनों 'संघ x {...};' और 'संरचना x {...};' एक दायरे में।
जोनाथन लेफ़लर

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

1
@Lazer: वहाँ रहे हैं सूक्ष्म अंतर है, लेकिन एडम का अर्थ है (के रूप में वह आगे कहते हैं) है कि आप एक typedef बिना चर घोषित करने के लिए 'प्रकार वर' का उपयोग कर सकते हैं।
फ्रेड नर्क

226

में इस DDJ लेख , दान साक्स एक छोटा सा क्षेत्र है जहां कीड़े के माध्यम से रेंगना कर सकते हैं अगर आप अपने structs typedef नहीं बताते हैं (और वर्गों!):

यदि आप चाहते हैं, तो आप कल्पना कर सकते हैं कि C ++ हर टैग नाम के लिए एक टाइपिडेफ़ उत्पन्न करता है, जैसे कि

typedef class string string;

दुर्भाग्य से, यह पूरी तरह से सही नहीं है। काश यह इतना आसान होता, लेकिन ऐसा नहीं है। C ++ असंगतताओं को प्रस्तुत किए बिना स्ट्रक्चर्स, यूनियनों, या एनमों के लिए इस तरह के टाइपफेड उत्पन्न नहीं कर सकता है।

उदाहरण के लिए, मान लीजिए कि एक C प्रोग्राम एक फ़ंक्शन और एक स्ट्रक्चर नामित स्थिति दोनों घोषित करता है:

int status(); struct status;

फिर, यह बुरा अभ्यास हो सकता है, लेकिन यह सी है। इस कार्यक्रम में, स्थिति (स्वयं के द्वारा) फ़ंक्शन को संदर्भित करता है; संरचना की स्थिति प्रकार को संदर्भित करती है।

यदि C ++ ने टैग्स के लिए स्वचालित रूप से टाइपराइफ उत्पन्न किया है, तो जब आप इस प्रोग्राम को C ++ के रूप में संकलित करते हैं, तो कंपाइलर उत्पन्न होगा:

typedef struct status status;

दुर्भाग्य से, इस प्रकार का नाम फ़ंक्शन नाम के साथ विरोध करेगा, और प्रोग्राम संकलित नहीं करेगा। इसीलिए C ++ प्रत्येक टैग के लिए केवल एक टाइफाइड उत्पन्न नहीं कर सकता है।

C ++ में, टैग केवल टाइप किए गए नामों की तरह कार्य करते हैं, सिवाय इसके कि कोई प्रोग्राम किसी ऑब्जेक्ट, फ़ंक्शन या एनुमर को उसी नाम और एक टैग के समान स्कोप के साथ घोषित कर सकता है। उस स्थिति में, ऑब्जेक्ट, फ़ंक्शन, या एन्यूमरेटर नाम टैग नाम छुपाता है। प्रोग्राम केवल टैग नाम के सामने कीवर्ड वर्ग, संरचना, संघ, या एनम (उपयुक्त के रूप में) का उपयोग करके टैग नाम को संदर्भित कर सकता है। एक प्रकार का नाम जिसमें इन टैगों में से एक टैग शामिल है, एक विस्तृत प्रकार-विशेष है। उदाहरण के लिए, संरचना की स्थिति और enum महीने विस्तृत प्रकार के विनिर्देशक हैं।

इस प्रकार, एक सी प्रोग्राम जिसमें दोनों शामिल हैं:

int status(); struct status;

C ++ के रूप में संकलित होने पर समान व्यवहार करता है। अकेले नाम की स्थिति फ़ंक्शन को संदर्भित करती है। कार्यक्रम केवल विस्तृत-प्रकार-निर्दिष्ट संरचना स्थिति का उपयोग करके प्रकार को संदर्भित कर सकता है।

तो यह कैसे कीड़ों को कार्यक्रमों में रेंगने की अनुमति देता है? लिस्टिंग 1 में कार्यक्रम पर विचार करें । यह प्रोग्राम एक डिफॉल्ट कंस्ट्रक्टर के साथ एक क्लास फू को परिभाषित करता है, और एक कन्वर्जन ऑपरेटर जो एक फयू ऑब्जेक्ट को चार कॉन्स्ट * में बदलता है। भाव

p = foo();

मुख्य में एक फू वस्तु का निर्माण करना चाहिए और रूपांतरण ऑपरेटर को लागू करना चाहिए। बाद का आउटपुट स्टेटमेंट

cout << p << '\n';

वर्ग फू प्रदर्शित करना चाहिए, लेकिन यह नहीं है। यह फंक्शन फू को प्रदर्शित करता है।

यह आश्चर्यजनक परिणाम तब होता है क्योंकि कार्यक्रम में शीर्षलेख lib.h शामिल होता है जिसे लिस्टिंग 2 में दिखाया गया है । यह हेडर एक फ़ंक्शन को परिभाषित करता है जिसे फू भी कहा जाता है। फ़ंक्शन का नाम foo वर्ग का नाम foo छुपाता है, इसलिए मुख्य रूप से foo का संदर्भ फ़ंक्शन को संदर्भित करता है, न कि क्लास को। मुख्य वर्ग का उल्लेख केवल एक विस्तृत-प्रकार-विनिर्देशक का उपयोग करके कर सकता है, जैसे कि

p = class foo();

पूरे कार्यक्रम में इस तरह के भ्रम से बचने का तरीका वर्ग नाम फू के लिए निम्नलिखित टाइपफेड जोड़ना है:

typedef class foo foo;

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

मैं ऐसे किसी व्यक्ति के बारे में नहीं जानता जो वास्तव में इन टाईपडेफ़्स को पाठ्यक्रम के रूप में लिखता है। इसके लिए बहुत अनुशासन की जरूरत होती है। चूंकि लिस्टिंग 1 में त्रुटियों की घटना संभवतः बहुत छोटी है, इसलिए आप बहुत से इस समस्या से कभी नहीं बचते हैं। लेकिन अगर आपके सॉफ़्टवेयर में कोई त्रुटि शारीरिक चोट का कारण बन सकती है, तो आपको लिखना चाहिए कि कोई त्रुटि नहीं होने की स्थिति में कोई बात नहीं है।

मैं कल्पना नहीं कर सकता कि कोई भी वर्ग के नाम को किसी फ़ंक्शन या ऑब्जेक्ट नाम के साथ वर्ग के समान दायरे में क्यों छिपाना चाहेगा। सी में छिपने के नियम एक गलती थे, और उन्हें सी ++ में कक्षाओं तक नहीं बढ़ाया जाना चाहिए था। वास्तव में, आप गलती को सुधार सकते हैं, लेकिन इसके लिए अतिरिक्त प्रोग्रामिंग अनुशासन और प्रयास की आवश्यकता होती है जो आवश्यक नहीं होना चाहिए।


11
यदि आप "क्लास फू ()" की कोशिश करते हैं और यह विफल रहता है: आईएसओ सी ++ में, "क्लास फू ()" एक अवैध निर्माण है (मानकीकरण से पहले यह लेख '97 लिखा गया था, ऐसा लगता है)। आप "typedef class foo foo" डाल सकते हैं। मुख्य में, फिर आप कह सकते हैं "फू ();" (क्योंकि तब, टाइपडिफ-नाम फ़ंक्शन के नाम की तुलना में बहुत करीब है)। सिंथेटिक रूप से, टी में (), टी को एक सरल-प्रकार-निर्दिष्ट होना चाहिए। विस्तृत प्रकार के विनिर्देशक की अनुमति नहीं है। फिर भी यह एक अच्छा जवाब है।
जोहान्स स्काउब -

Listing 1और Listing 2लिंक टूट गए हैं। एक नज़र देख लो।
प्रसून सौरव

3
यदि आप कक्षाओं और कार्यों के लिए अलग-अलग नामकरण सम्मेलनों का उपयोग करते हैं, तो आप अतिरिक्त टाइपराइफ जोड़ने की आवश्यकता के बिना नामकरण संघर्ष से भी बचते हैं।
zstewart

64

एक और महत्वपूर्ण अंतर: typedefएस आगे घोषित नहीं किया जा सकता है। तो typedefविकल्प के लिए आपके पास #includeवह फ़ाइल होनी चाहिए typedef, जिसका अर्थ यह है कि #includeआपके पास जो कुछ .hभी है, उसमें वह फ़ाइल भी शामिल है, चाहे उसे सीधे इसकी आवश्यकता है या नहीं, और इसी तरह। यह निश्चित रूप से बड़ी परियोजनाओं पर आपके निर्माण समय को प्रभावित कर सकता है।

बिना typedef, कुछ मामलों में आप केवल struct Foo;अपनी .hफ़ाइल के शीर्ष पर एक आगे की घोषणा को जोड़ सकते हैं , और आपकी फ़ाइल में केवल #includeसंरचनात्मक परिभाषा .cpp


संरचना की परिभाषा को उजागर करने का निर्माण समय क्यों प्रभावित करता है? क्या कंपाइलर अतिरिक्त जाँच करता है, भले ही इसके लिए ज़रूरत न हो (टाइप किए गए विकल्प को देखते हुए, ताकि कंपाइलर को परिभाषा पता हो) जब यह Foo * nextFoo जैसी कोई चीज़ देखता है ; ?
रिच

3
यह वास्तव में अतिरिक्त जाँच नहीं है, इसका सिर्फ एक और कोड है जिसे कंपाइलर को निपटना होगा। हर cpp फ़ाइल के लिए जो उस टाइपपैड को कहीं न कहीं सामना करती है, उसमें चेन टाइपिंग शामिल है। बड़ी परियोजनाओं में .हेड टाइपफ वाली फ़ाइल को आसानी से सैकड़ों बार संकलित किया जा सकता है, हालाँकि पहले से तैयार हेडर बहुत मदद करता है। यदि आप आगे की घोषणा का उपयोग करके दूर हो सकते हैं, तो .h को पूर्ण संरचना विनिर्देशन में शामिल करने को सीमित करने के लिए केवल कोड की वास्तव में परवाह है, और इसलिए संबंधित फ़ाइल को शामिल करना कम बार संकलित है।
जो

कृपया पिछले टिप्पणीकार (पोस्ट के मालिक के अलावा)। मैं लगभग उर उत्तर याद किया। लेकिन सूचना के लिए धन्यवाद।
रिच

यह सी 11 में अब और सच नहीं है, जहां आप एक ही नाम के साथ कई बार एक ही संरचना टाइप कर सकते हैं।
माइकललेमेयर

32

वहाँ है एक अंतर है, लेकिन सूक्ष्म। इसे इस तरह देखें: struct Fooएक नए प्रकार का परिचय देता है। दूसरा एक अनाम structप्रकार के लिए फू (और नया प्रकार नहीं) नामक एक उपनाम बनाता है ।

.१.३ टाइप्डिफ निर्दिष्ट

1 [...]

टाइप्डिफ स्पेसिफायर के साथ घोषित एक नाम टाइपडेफ-नाम बन जाता है। इसकी घोषणा के दायरे में, एक टाइपराइफ-नाम वाक्य-विन्यास के अनुसार एक कीवर्ड के समान होता है और क्लॉज़ 8. में वर्णित तरीके से पहचानकर्ता के साथ जुड़े प्रकार को टाइप करता है। एक टाइप-बीफ़-नाम इस प्रकार दूसरे प्रकार का एक पर्याय है। एक टाइप-बीफ़-नाम एक नए प्रकार का परिचय नहीं देता है जिस तरह एक वर्ग घोषणा (9.1) या एनम घोषणा करता है।

8 यदि टाइप किए गए घोषणा में अनाम वर्ग (या एनम) को परिभाषित किया जाता है, तो घोषणा के द्वारा घोषित पहला टाइपडेफ-नाम उस वर्ग प्रकार (या एनम प्रकार) का उपयोग केवल लिंकेज उद्देश्यों के लिए वर्ग प्रकार (या एनम प्रकार) को दर्शाने के लिए किया जाता है ( 3.5)। [ उदाहरण:

typedef struct { } *ps, S; // S is the class name for linkage purposes

तो, एक टाइप्डेफ हमेशा एक प्लेसहोल्डर / एक अन्य प्रकार के पर्याय के रूप में उपयोग किया जाता है।


10

आप टाइपडिफ़ संरचना के साथ आगे की घोषणा का उपयोग नहीं कर सकते।

संरचना स्वयं एक अनाम प्रकार है, इसलिए आपके पास घोषणा करने के लिए वास्तविक नाम नहीं है।

typedef struct{
    int one;
    int two;
}myStruct;

इस अभ्यस्त काम की तरह एक आगे की घोषणा:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types

मुझे फ़ंक्शन प्रोटोटाइप के लिए दूसरी त्रुटि नहीं मिलती है। इसे "पुनर्परिभाषित; भिन्न मूल प्रकार" क्यों कहते हैं? संकलक को यह जानने की जरूरत नहीं है कि myStruct की परिभाषा कैसी दिखती है, है ना? कोड के टुकड़े से लिया गया कोई भी मामला (टाइप किए गए एक, या आगे की घोषणा एक), MyStruct एक संरचना प्रकार को दर्शाता है, है ना?
रिच

@Rich यह शिकायत कर रहा है कि नामों का टकराव है। एक आगे की घोषणा करते हुए कहा गया है कि "myStruct नामक एक संरचना की तलाश करें" और फिर वहाँ typedef है जो "myStruct" के रूप में एक अनाम संरचना का नाम बदल रहा है।
योचाई टिमर

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

: @Rich जीसीसी एक ही त्रुटि देता है, पाठ थोड़ा भिन्न होता है gcc.godbolt.org/...
Yochai टिमर

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

0

C टाइप में 'टाइपकेफ स्ट्रक्चर' और 'स्ट्रक्चर' के बीच एक महत्वपूर्ण अंतर यह है कि 'टाइपडीफ स्ट्रक्चर' में इनलाइन मेंबर इनिशियलाइजेशन काम नहीं करेगा।

// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;

// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };

3
सच नहीं। दोनों मामलों xमें आरम्भिक है। कोलिरु ऑनलाइन आईडीई में परीक्षण देखें (मैंने इसे 42 से शुरू किया था इसलिए यह शून्य से अधिक स्पष्ट है कि असाइनमेंट में वास्तव में जगह है)।
कॉलिन डी बेनेट

दरअसल, मैंने इसे विजुअल स्टूडियो 2013 में टेस्ट किया और इसे इनिशियलाइज़ नहीं किया गया। यह एक समस्या थी जो हमें उत्पादन कोड में आई। सभी संकलक अलग-अलग हैं और केवल कुछ मानदंडों को पूरा करना है।
2027 पर user2796283

-3

C ++ में कोई अंतर नहीं है, लेकिन मुझे विश्वास है कि C में यह आपको स्पष्ट रूप से बिना संरचना फू के उदाहरणों को घोषित करने की अनुमति देगा:

struct Foo bar;

3
@ Dirkgently के जवाब में देखो --- वहाँ है एक अंतर है, लेकिन यह सूक्ष्म है।
कीथ पिंसन

-3

संरचना एक डेटा प्रकार बनाने के लिए है। टाइपसेफ एक डेटा प्रकार के लिए एक उपनाम सेट करना है।


12
आपको सवाल समझ में नहीं आया।
शीतकालीन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.