हमें C में इतनी बार एक स्ट्रक्चर क्यों लिखना चाहिए?


406

मैंने कई कार्यक्रमों को देखा है जिसमें नीचे की तरह संरचनाएं हैं

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

इसकी इतनी बार आवश्यकता क्यों है? कोई विशेष कारण या लागू क्षेत्र?


14
अधिक गहन और सटीक उत्तर: stackoverflow.com/questions/612328/…
AbiusX

इसके नुकसान यह है कि मुझे लगता है कि आप गुमनाम संरचना के साथ एक लिंक सूची नहीं बना सकते क्योंकि संरचना के struct * ptrअंदर की रेखा त्रुटि का कारण बन जाएगी
रागिब अहसान

9
'अधिक संपूर्ण और सटीक उत्तर' C ++ में संरचना और टाइपडिफ़ संरचना के बीच अंतर है , और इस क्षेत्र में C और C ++ के बीच महत्वपूर्ण अंतर हैं, जो उस उत्तर को सी के बारे में एक प्रश्न के लिए पूरी तरह से उपयुक्त नहीं बनाते हैं
जोनाथन लेफ़लर

इस प्रश्न में एक डुप्लीकेट टाइपडिफ़ संरचना बनाम संरचना परिभाषाएँ हैं, जिनके तारकीय उत्तर भी हैं।
जोनाथन लेफलर

2
OTOH, kernel.org/doc/html/v4.10/process/coding-style.html हमें बताता है कि हमें इस तरह के टाइपफेड नहीं करने चाहिए।
glglgl

जवाबों:


453

जैसा कि ग्रेग हेवगिल ने कहा, टाइपडिफ का मतलब है अब आपको structसभी जगह लिखना नहीं होगा। यह न केवल कीस्ट्रोक्स को बचाता है, बल्कि यह कोड क्लीनर भी बना सकता है क्योंकि यह एक स्मिगन को अधिक अमूर्तता प्रदान करता है।

सामान की तरह

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

क्लीनर बन जाता है, जब आपको "स्ट्रक्चर" कीवर्ड को पूरे स्थान पर देखने की आवश्यकता नहीं होती है, तो यह अधिक लगता है जैसे कि वास्तव में आपकी भाषा में "प्वाइंट" नामक एक प्रकार है। जो, मेरे बाद typedefका मामला है।

यह भी ध्यान दें कि जब आपका उदाहरण (और मेरा) नामकरण struct स्वयं छोड़ दिया गया था, वास्तव में इसका नामकरण तब भी उपयोगी होता है जब आप एक अपारदर्शी प्रकार प्रदान करना चाहते हैं। फिर आपके पास हेडर में इस तरह का कोड होगा, उदाहरण के लिए:

typedef struct Point Point;

Point * point_new(int x, int y);

और फिर structकार्यान्वयन फ़ाइल में परिभाषा प्रदान करें :

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

इस बाद के मामले में, आप पॉइंट को मूल्य से वापस नहीं कर सकते, क्योंकि इसकी परिभाषा हेडर फ़ाइल के उपयोगकर्ताओं से छिपी हुई है। यह उदाहरण के लिए जीटीके + में व्यापक रूप से उपयोग की जाने वाली तकनीक है ।

अद्यतन यह भी ध्यान रखें अत्यधिक माना सी परियोजनाओं जहां के इस प्रयोग देखते हैं कि typedefछिपाने के लिए structएक बुरा विचार माना जाता है, लिनक्स कर्नेल शायद सबसे प्रसिद्ध ऐसी परियोजना है। लाइनस के गुस्से वाले शब्दों के लिए लिनक्स कर्नेल कोडिंगसेल दस्तावेज़ के अध्याय 5 को देखें । :) मेरा कहना है कि सवाल में "चाहिए" पत्थर में सेट नहीं है, सब के बाद।


58
आप एक अपरकेस अक्षर के बाद अंडरस्कोर वाले पहचानकर्ताओं का उपयोग नहीं करना चाहिए, वे आरक्षित हैं (देखें खंड 7.1.3 पैरा 1)। हालाँकि, किसी समस्या से बहुत अधिक होने की संभावना नहीं है, यह तकनीकी रूप से अपरिभाषित व्यवहार है जब आप उनका उपयोग करते हैं (7.1.3 पैरा 2)।
ड्रीमलैक्स

16
@dreamlax: यदि यह दूसरों के लिए स्पष्ट नहीं था, तो यह केवल एक अंडरस्कोर और एक ऊपरी मामले के साथ एक पहचानकर्ता शुरू कर रहा है जो आपको नहीं करना चाहिए; आप एक पहचानकर्ता के बीच में इसका उपयोग करने के लिए स्वतंत्र हैं।
ब्रायनमर्न्स

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

7
@Rerito फी, C99 ड्राफ्ट के पेज 166 , सभी पहचान begin ers जो एक अंडरस्कोर से शुरू होते हैं और या तो एक अपरकेस लेटर या किसी अन्य अंडरस्कोर हमेशा किसी भी उपयोग के लिए आरक्षित होते हैं। और सभी पहचान that ers जो एक अंडरस्कोर से शुरू होते हैं, हमेशा साधारण और टैग नाम रिक्त स्थान दोनों में the le गुंजाइश के साथ पहचान with ers के रूप में उपयोग के लिए आरक्षित होते हैं।
e2-e4

10
दिलचस्प रूप से पर्याप्त है, लिनक्स कर्नेल कोडिंग दिशानिर्देशों का कहना है कि हमें टाइप-सीड्स (धारा 5) का उपयोग करने के बारे में बहुत अधिक रूढ़िवादी होना चाहिए: kernel.org/doc/Documentation/CodingStyle
gsingh2011

206

यह आश्चर्यजनक है कि कितने लोगों को यह गलत लगता है। कृपया C में टाइपफ़ीड संरचना न करें, यह अनावश्यक रूप से वैश्विक नामस्थान को प्रदूषित करता है जो आमतौर पर बड़े कार्यक्रमों में पहले से ही बहुत प्रदूषित है।

इसके अलावा, बिना किसी टैग नाम के टाइप किएडिफैड स्ट्रक्चर्स हेडर फाइलों के बीच रिश्तों को आदेश देने के लिए अनावश्यक आवश्यकता का एक प्रमुख कारण है।

विचार करें:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

इस तरह की परिभाषा के साथ, टाइपडिफ्स का उपयोग नहीं करना, यह एक संकलन इकाई के लिए foo.h को FOO_DEFपरिभाषा में शामिल करने के लिए संभव है । यदि यह fooसंरचना के 'बार' के सदस्य को हटाने का प्रयास नहीं करता है, तो "bar.h" फ़ाइल को शामिल करने की कोई आवश्यकता नहीं होगी।

इसके अलावा, चूंकि टैग नामों और सदस्य नामों के बीच नामस्थान भिन्न हैं, इसलिए बहुत पठनीय कोड लिखना संभव है जैसे:

struct foo *foo;

printf("foo->bar = %p", foo->bar);

चूंकि नाम स्थान अलग-अलग हैं, इसलिए उनके संरचनात्मक टैग नाम के साथ संयोग नामकरण चर में कोई संघर्ष नहीं है।

अगर मुझे आपका कोड बनाए रखना है, तो मैं आपके टाइपफीड को हटा दूंगा।


37
क्या अधिक आश्चर्यजनक है कि इस जवाब के दिए जाने के 13 महीने बाद, मैं इसे सबसे पहले उखाड़ने वाला हूँ! टाइपडिफिंग संरचना सी की सबसे बड़ी गालियों में से एक है, और अच्छी तरह से लिखे गए कोड में कोई जगह नहीं है। टाइपेडिफ डी-ऑबफैक्टिंग कन्ट्रोवर्सीड फंक्शन पॉइंटर प्रकारों के लिए उपयोगी है और वास्तव में कोई अन्य उपयोगी उद्देश्य नहीं रखता है।
विलियम पर्ससेल

28
पीटर वैन डेर लिंडेन भी अपनी ज्ञानवर्धक पुस्तक "एक्सपर्ट सी प्रोग्रामिंग - डीप सी सीक्रेट्स" में टाइपराइफिंग स्ट्रक्चर के खिलाफ केस करते हैं। जिस्ट है: आप यह जानना चाहते हैं कि कुछ एक संरचना या संघ है, न कि इसे छिपाएं।
जेन्स

34
लिनक्स कर्नेल कोडिंग शैली स्पष्ट रूप से टाइप किए गए संरचनाओं को रोकती है। अध्याय 5: टाइपिडेफ़्स: "यह संरचनाओं और बिंदुओं के लिए टाइपडिफ का उपयोग करने के लिए एक गलती है।" kernel.org/doc/Documentation/CodingStyle
jasso

63
क्या लाभ, वास्तव में, टाइपिंग "संरचना" बार-बार प्रदान करता है? और प्रदूषण की बात करते हुए, आप एक वैश्विक नाम स्थान में एक ही नाम के साथ एक संरचना और एक फ़ंक्शन / चर / टाइपडेफ़ क्यों चाहते हैं (जब तक कि यह एक ही फ़ंक्शन के लिए एक टाइपफ़ीड न हो)? सुरक्षित पैटर्न का उपयोग करना है typedef struct X { ... } X। इस तरह आप Xपरिभाषा उपलब्ध होने के बाद कहीं भी संरचना को संबोधित करने के लिए संक्षिप्त रूप का उपयोग कर सकते हैं , लेकिन struct Xवांछित होने पर भी आगे-घोषणा और उपयोग कर सकते हैं।
पावेल मिनेव

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

138

दान सक्स के एक पुराने लेख से ( http://www.ddj.com/cpp/184403396?pgno=3 ):


नामकरण संरचनाओं के लिए सी भाषा के नियम थोड़े विलक्षण हैं, लेकिन वे बहुत हानिरहित हैं। हालाँकि, जब C ++ में कक्षाएं बढ़ाई जाती हैं, तो वही नियम बग के लिए क्रॉल करने के लिए थोड़ी दरारें खोलते हैं।

C में, नाम दिखाई दे रहा है

struct s
    {
    ...
    };

एक टैग है। एक टैग नाम एक प्रकार का नाम नहीं है। उपरोक्त परिभाषा को देखते हुए, घोषणाएँ जैसे

s x;    /* error in C */
s *p;   /* error in C */

सी। में त्रुटियाँ हैं। आप उन्हें इस रूप में अवश्य लिखें

struct s x;     /* OK */
struct s *p;    /* OK */

यूनियनों और गणनाओं के नाम भी प्रकारों के बजाय टैग हैं।

सी में, टैग अन्य सभी नामों (कार्यों, प्रकारों, चर, और गणना स्थिरांक) से अलग हैं। सी संकलक एक प्रतीक तालिका में टैग बनाए रखते हैं जो वैचारिक रूप से है यदि भौतिक रूप से उस तालिका से अलग नहीं है जो अन्य सभी नामों को रखती है। इस प्रकार, एक सी प्रोग्राम के लिए एक टैग और एक अन्य नाम एक ही दायरे में समान वर्तनी के साथ होना संभव है। उदाहरण के लिए,

struct s s;

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

कई प्रोग्रामर (आपके सहित वास्तव में) प्रकार के नामों के रूप में संरचना के नामों के बारे में सोचना पसंद करते हैं, इसलिए वे टाइप टाइप का उपयोग करके टैग के लिए एक उपनाम को परिभाषित करते हैं। उदाहरण के लिए, परिभाषित करना

struct s
    {
    ...
    };
typedef struct s S;

जैसा कि आप संरचना एस के स्थान पर एस का उपयोग करते हैं

S x;
S *p;

एक प्रोग्राम S को एक प्रकार और एक चर (या फ़ंक्शन या एन्यूमरेशन स्थिर) दोनों के नाम के रूप में उपयोग नहीं कर सकता है।

S S;    // error

यह अच्छा है।

एक संरचना, संघ, या enum परिभाषा में टैग नाम वैकल्पिक है। कई प्रोग्रामर टाइप परिभाषा में संरचना की परिभाषा को मोड़ते हैं और टैग के साथ पूरी तरह से फैलाते हैं, जैसे:

typedef struct
    {
    ...
    } S;

लिंक किए गए लेख में इस बात की भी चर्चा है कि कैसे C ++ के व्यवहार की आवश्यकता नहीं होने के typedefकारण सूक्ष्म नाम छिपाने की समस्या हो सकती है। इन समस्याओं को रोकने के लिए, यह typedefC ++ में आपकी कक्षाओं और संरचनाओं के लिए एक अच्छा विचार है, भले ही, पहली नज़र में यह अनावश्यक प्रतीत हो। सी ++ में, typedefनाम छिपाने के साथ एक त्रुटि हो जाती है जो संकलक संभावित समस्याओं के छिपे हुए स्रोत के बजाय आपको बताती है।


2
एक उदाहरण जहां टैग नाम एक गैर-टैग नाम के समान है, int stat(const char *restrict path, struct stat *restrict buf)फ़ंक्शन के साथ (POSIX या Unix) प्रोग्राम में है। वहाँ आप statसाधारण नाम अंतरिक्ष struct statमें और टैग नाम अंतरिक्ष में एक समारोह है।
जोनाथन लेफ़लर

2
आपका कथन, एसएस; // त्रुटि .... गलत है यह अच्छी तरह से काम करता है। मेरा मतलब है कि आपका कथन है कि "हमारे पास
टाइपडेफ

63

एक का प्रयोग typedefसे बचा जाता है लिखने के लिए होने structहर बार जब आप उस प्रकार के एक चर घोषित:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct

2
ठीक है हम C ++ में वह समस्या नहीं है। तो क्यों न कोई भी उस गड़बड़ को C के कंपाइलर से हटा दे और C ++ की तरह ही बना दे। ok C ++ में कुछ अलग एप्लीकेशन एरिया हैं और इसलिए इसमें एडवांस फीचर्स हैं। लेकिन क्या हम C को बदले बिना उनमें से कुछ को इनहेरिट नहीं कर सकते। मूल सी?
मनोज संदेह 12

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

41
यह सी संकलक में एक गड़बड़ नहीं है, यह डिजाइन का हिस्सा है। उन्होंने सी ++ के लिए यह बदल दिया, जो मुझे लगता है कि चीजों को आसान बनाता है, लेकिन इसका मतलब यह नहीं है कि सी का व्यवहार गलत है।
हर्म्स

5
दुर्भाग्य से कई 'प्रोग्रामर' एक संरचना को परिभाषित करते हैं, फिर इसे कुछ 'असंबंधित' नाम के साथ टाइप करते हैं (जैसे संरचनात्मक myStruct ...; typedef struct myStruct susan *; लगभग सभी उदाहरणों में एक typedef कुछ भी नहीं करता है - कोड को अव्यवस्थित करना, वास्तविक परिभाषा को छिपाना; एक चर / पैरामीटर, और कोड के मूल लेखक सहित सभी को गलत तरीके से ले जाता है।
user3629249

1
@ user3629249 मैं आपसे सहमत हूँ कि उल्लेखित प्रोग्रामिंग शैली भयानक है लेकिन यह typedefसामान्य रूप से आईएनजी संरचनाओं को बदनाम करने का कारण नहीं है । आप भी कर सकते हैं typedef struct foo foo;। बेशक structकीवर्ड की आवश्यकता अधिक नहीं है, जो एक उपयोगी संकेत हो सकता है कि जिस प्रकार का उपनाम हम देखते हैं वह संरचना के लिए एक उपनाम है लेकिन यह सामान्य रूप से खराब नहीं है। एक ऐसे मामले पर भी विचार करें जहां परिणामी प्रकार के उपनाम की पहचान typedefयह इंगित करती है कि यह संरचना के लिए एक उपनाम है, fe typedef struct foo foo_struct;:।
रॉबर्ट्स

38

इस समस्या से हमेशा टाइप किए गए एनफॉम और स्ट्रक्चर्स के लिए एक और अच्छा कारण:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

संरचना में EnumDef में टाइपो (Enu u mDef) को नोटिस करें? यह त्रुटि (या चेतावनी) के बिना संकलित है और (सी मानक की शाब्दिक व्याख्या के आधार पर) सही है। समस्या यह है कि मैंने अपनी संरचना के भीतर एक नई (खाली) गणना परिभाषा बनाई है। मैं पिछले परिभाषा EnumDef का उपयोग करके (जैसा कि इरादा था) नहीं हूं।

एक टाइपडीएफ के साथ इसी प्रकार के टाइपो में एक अज्ञात प्रकार का उपयोग करने के लिए एक कंपाइलर त्रुटियां होती हैं:

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

मैं हमेशा टाइप किए गए ढांचे और गणनाओं की वकालत करूंगा।

न केवल कुछ टाइपिंग को बचाने के लिए (कोई सज़ा नहीं;)), लेकिन क्योंकि यह सुरक्षित है।


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

3
यह परिभाषा: 'typedef {FIRST_ITEM, SECOND_ITEM} EnumDef;' एक दुश्मनी को परिभाषित नहीं करता है। मैंने सैकड़ों विशाल कार्यक्रम लिखे हैं और दूसरों द्वारा लिखे गए कार्यक्रमों पर रखरखाव करने का दुर्भाग्य था। अनुभव के कठिन हाथ से, एक संरचना पर टाइपडिफ का उपयोग करने से केवल समस्याएं होती हैं। उम्मीद है कि प्रोग्रामर इतना विकलांग नहीं है कि जब उन्हें एक संरचनात्मक उदाहरण घोषित करने में पूरी परिभाषा लिखने में समस्या हो। C मूल नहीं है, इसलिए कुछ और अक्षर टाइप करना कार्यक्रम के संचालन के लिए हानिकारक नहीं है।
user3629249

2
उन लोगों के लिए जो वर्णों की पूर्ण न्यूनतम संख्या से अधिक टाइप करने के लिए कुछ घृणा महसूस करते हैं, क्या मैं कुछ समूह में शामिल होने का सुझाव दे सकता हूं जो किस्ट्रोक्स की न्यूनतम संख्या के साथ आवेदन लिखने की कोशिश करता है। बस एक काम के माहौल में अपने नए 'कौशल' का उपयोग न करें, विशेष रूप से एक काम का माहौल जो
दृढ़ता से

ध्यान दें, यह आमतौर पर enumएक प्रकार के रूप में उपयोग करने के लिए हतोत्साहित किया जाता है, क्योंकि कई कंपाइलर 'गलत' का उपयोग करते समय अजीब चेतावनी देते हैं। उदाहरण के लिए, enum0 से इनिशियलाइज़ करना एक 'पूर्णांक स्थिरांक को enum' चेतावनी में नहीं दे सकता है। फॉरवर्ड-घोषणा करना enumभी अनुमति नहीं है। इसके बजाय int(या unsigned int) का उपयोग करना चाहिए ।
21

5
यह उदाहरण संकलन नहीं करता है, न ही मैं इसकी अपेक्षा करूंगा। संकलन डीबग / test.o test.c: 10: 17: एरर: फील्ड में अधूरा टाइप 'एनम एनुमडेफ' एनम एन्यूमडिफ माय माय है; ^ test.c: 10: 8: नोट: आगे की घोषणा 'एनम एनुमडेफ' एनम एनुमडिफ माय ईनम; ^ 1 त्रुटि उत्पन्न हुई। gnuc, std = c99 के साथ।
नटेरोज़

30

लिनक्स कर्नेल कोडिंग स्टाइल चैप्टर 5 बड़े पेशेवरों और विपक्षों (अधिकतर कॉन्स) को उपयोग करने की सुविधा देता है typedef

कृपया "vps_t" जैसी चीजों का उपयोग न करें।

यह संरचनाओं और बिंदुओं के लिए टाइपडिफ का उपयोग करने के लिए एक गलती है। जब आप ए

vps_t a;

स्रोत में, इसका क्या मतलब है?

इसके विपरीत यदि यह कहे

struct virtual_container *a;

आप वास्तव में बता सकते हैं कि "ए" क्या है।

बहुत से लोग सोचते हैं कि टाइपडेफ "पठनीयता में मदद करते हैं"। ऐसा नहीं। वे केवल इसके लिए उपयोगी हैं:

(ए) पूरी तरह से अपारदर्शी वस्तुएं (जहां टाइपडिफ सक्रिय रूप से उस वस्तु को छिपाने के लिए उपयोग किया जाता है)।

उदाहरण: "pte_t" आदि अपारदर्शी वस्तुएं जिन्हें आप केवल उचित सहायक कार्यों का उपयोग करके एक्सेस कर सकते हैं।

ध्यान दें! अस्पष्टता और "अभिगम कार्य" अपने आप में अच्छे नहीं हैं। हमारे पास उनके लिए pte_t आदि जैसी चीज़ों का कारण यह है कि वास्तव में वहाँ शून्य पूरी तरह से सुलभ जानकारी है।

(b) पूर्णांक प्रकार साफ़ करें, जहाँ अमूर्तता भ्रम से बचने में मदद करती है चाहे वह "int" हो या "long"।

u8 / u16 / u32 पूरी तरह से ठीक प्रकार के हैं, हालांकि वे यहां से बेहतर (डी) श्रेणी में आते हैं।

ध्यान दें! फिर - इसके लिए एक कारण होने की आवश्यकता है । अगर कुछ "लंबे समय से अटूट है", तो ऐसा करने का कोई कारण नहीं है

typedef unsigned long myflags_t;

लेकिन अगर कोई स्पष्ट कारण है कि कुछ परिस्थितियों में यह "अहस्ताक्षरित int" हो सकता है और अन्य विन्यासों के तहत "अहस्ताक्षरित लंबा" हो सकता है, तो हर तरह से आगे बढ़ें और एक typedef का उपयोग करें।

(c) जब आप विरल का उपयोग वस्तुतः टाइप-जाँच के लिए एक नया प्रकार बनाने के लिए करते हैं।

(d) नए प्रकार जो कुछ असाधारण परिस्थितियों में, मानक C99 प्रकारों के समान हैं।

हालाँकि यह आंखों और मस्तिष्क को 'uint32_t' जैसे मानक प्रकारों का आदी होने में केवल कुछ समय लगेगा, कुछ लोगों को वैसे भी उनके उपयोग पर आपत्ति है।

इसलिए, लिनक्स-विशिष्ट 'u8 / u16 / u32 / u64' प्रकार और उनके हस्ताक्षरित समकक्ष जो मानक प्रकारों के समान हैं - की अनुमति है - हालांकि वे आपके स्वयं के नए कोड में अनिवार्य नहीं हैं।

मौजूदा कोड को संपादित करते समय जो पहले से ही एक या दूसरे प्रकार के सेट का उपयोग करता है, आपको उस कोड में मौजूदा विकल्पों के अनुरूप होना चाहिए।

(e) उपयोगकर्ताप में उपयोग के लिए सुरक्षित प्रकार।

कुछ संरचनाओं में जो उपयोक्ता के लिए दृश्यमान हैं, हमें C99 प्रकारों की आवश्यकता नहीं हो सकती है और ऊपर 'u32' फॉर्म का उपयोग नहीं किया जा सकता है। इस प्रकार, हम सभी संरचनाओं में __u32 और इसी तरह के प्रकारों का उपयोग करते हैं जो उपयोगकर्ताओं के साथ साझा किए जाते हैं।

हो सकता है कि कुछ अन्य मामले भी हों, लेकिन नियम तब तक होना चाहिए जब तक कि आप उन नियमों में से एक का स्पष्ट रूप से मिलान न कर सकें, जब तक कि वे किसी प्रकार का उपयोग न करें।

सामान्य तौर पर, एक पॉइंटर, या एक संरचना जिसमें ऐसे तत्व होते हैं, जिन्हें उचित रूप से सीधे एक्सेस किया जा सकता है, उन्हें कभी भी टाइपडिफ नहीं होना चाहिए ।


4
'अपारदर्शिता और "अभिगम कार्य" अपने आप में अच्छे नहीं हैं।' कोई समझा सकता है क्यों? मुझे लगता है कि जानकारी छिपाना और एनकैप्सुलेशन एक बहुत अच्छा विचार होगा।
यवर

5
@ यवर मैं सिर्फ इस दस्तावेज़ को पढ़ता था और बिल्कुल वैसा ही सोचा था। यकीन है, सी वस्तु केंद्रित नहीं है, लेकिन अमूर्त अभी भी एक बात है।
बाल्ड्रिक

12

यह पता चला है कि पेशेवरों और विपक्ष हैं। सूचना का एक उपयोगी स्रोत सेमिनल बुक "एक्सपर्ट सी प्रोग्रामिंग" ( अध्याय 3 ) है। संक्षेप में, C में आपके कई नामस्थान हैं: टैग, प्रकार, सदस्य नाम और पहचानकर्ताtypedefएक प्रकार के लिए एक उपनाम का परिचय देता है और इसे टैग नेमस्पेस में ढूँढता है। अर्थात्,

typedef struct Tag{
...members...
}Type;

दो चीजों को परिभाषित करता है। टैग नेमस्पेस में एक टैग और टाइप नेमस्पेस में एक टाइप। तो आप दोनों Type myTypeऔर कर सकते हैं struct Tag myTagType। घोषणाएँ जैसे कि struct Type myTypeया Tag myTagTypeअवैध हैं। इसके अलावा, इस तरह से एक घोषणा में:

typedef Type *Type_ptr;

हम अपने प्रकार के लिए एक पॉइंटर को परिभाषित करते हैं। इसलिए अगर हम घोषणा करते हैं:

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

उसके बाद var1, var2और myTagType1टाइप करने के लिए संकेत कर रहे हैं , लेकिन myTagType2नहीं।

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

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

तुम कर सकते हो:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

इसलिए आप कास्टिंग के माध्यम से बाहरी flagsसंरचना ( MyPipe) द्वारा एक बाहरी सदस्य ( ) तक पहुंच सकते हैं । मेरे लिए यह (struct MyWriter_ *) s;इस तरह की कार्यक्षमता करने के लिए हर बार की तुलना में पूरे प्रकार कास्ट करने के लिए कम भ्रमित नहीं है । इन मामलों में संक्षिप्त संदर्भ एक बड़ी बात है, खासकर यदि आप अपने कोड में तकनीक को भारी रूप से नियोजित करते हैं।

अंत में, typedefएड प्रकार के साथ अंतिम पहलू मैक्रोज़ के विपरीत, उन्हें विस्तारित करने में असमर्थता है। यदि उदाहरण के लिए, आपके पास:

#define X char[10] or
typedef char Y[10]

फिर आप घोषणा कर सकते हैं

unsigned X x; but not
unsigned Y y;

हम वास्तव में इस संरचना के लिए परवाह नहीं करते हैं क्योंकि यह भंडारण विनिर्देश ( volatileऔर const) पर लागू नहीं होता है ।


1
MyPipe *s; MyWriter *self = (MyWriter *) s;और आपने अभी-अभी सख्त अलियासिंग को तोड़ा है।
जोनाथन रेइनहर्ट

@JonathonReinhart यह उल्लेख करना निराशाजनक होगा कि इससे कैसे बचा जा सकता है, उदाहरण के लिए कैसे बेहद खुश-ख़ुश GTK + इसके आसपास काम करता है: Bugzilla.gnome.org/show_bug.cn?id=140722 / mail.gnome.org/archives/gtk -वेल-सूची / 2004-अप्रैल / msg00196.html
अंडरस्कोर_ड

"टाइपडीफ एक प्रकार के लिए एक उपनाम का परिचय देता है और इसे टैग नेमस्पेस में स्थान देता है। सामान्य रूप से typedef struct Tag{ ...members... }Type; " दो चीजों को परिभाषित करता है "काफी समझ में नहीं आता है।" अगर टाइपडिफ टैग को परिभाषित करता है तो यहां 'टाइप' भी टैग होना चाहिए। सच्चाई परिभाषा 2 टैग और 1 प्रकार परिभाषित करता है (या 2 प्रकार और 1 टैग यकीन नहीं।) है: struct Tag, Tagऔर Typestruct Tagनिश्चित रूप से एक प्रकार है। Tagएक टैग है। लेकिन भ्रम यह है कि क्या Typeएक टैग या एक प्रकार है
Qwertyzw

12

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

शैली: C ++ में टाइपडिफ का उपयोग काफी मायने रखता है। कई और / या चर मापदंडों की आवश्यकता वाले टेम्पलेट्स से निपटने के दौरान यह लगभग आवश्यक हो सकता है। टाइम्डिफ नामकरण को सीधा रखने में मदद करता है।

C प्रोग्रामिंग लैंग्वेज में ऐसा नहीं है। टंकण का उपयोग अक्सर डेटा संरचना के उपयोग को बाधित करने के लिए बिना किसी उद्देश्य के होता है। चूंकि केवल {स्ट्रक्चर (6), एनम (4), यूनियन (5)} कीस्ट्रोक्स की संख्या का उपयोग डेटा प्रकार घोषित करने के लिए किया जाता है, संरचना के अलियासिंग के लिए लगभग कोई उपयोग नहीं है। क्या वह डेटा एक संघ या एक संरचना है? सीधे-सीधे गैर-टाइप किए गए घोषणा का उपयोग करने से आपको तुरंत पता चल जाता है कि यह किस प्रकार का है।

ध्यान दें कि कैसे लिनक्स को इस अलियासिंग बकवास टाइपिडेफ के सख्त परिहार के साथ लिखा गया है। परिणाम एक न्यूनतम और साफ शैली है।


10
स्वच्छ structहर जगह दोहरा नहीं होगा ... टाइप्डिफ नए प्रकार बनाते हैं। तुम क्या इस्तेमाल करते हो? प्रकार। हम इसकी परवाह नहीं करते हैं कि यह एक संरचना, मिलन या एनम है, इसलिए हम इसे टाइप करते हैं।
GManNickG

13
नहीं, हम परवाह करते हैं कि यह एक संरचना या संघ है, बनाम एक एनम या कुछ परमाणु प्रकार। आप किसी पूर्णांक या किसी सूचक (या उस मामले के लिए किसी भी अन्य प्रकार) के लिए एक संरचना के लिए बाध्य नहीं कर सकते हैं, जो कि आप सभी को कभी न कभी किसी संदर्भ को संग्रहीत करना होगा। आसपास 'संरचना' या 'संघ' कीवर्ड होने से तर्क की स्थानीयता में सुधार होता है। कोई नहीं कहता है कि आपको यह जानना होगा कि संरचना के अंदर क्या है ।
बेरंड जेन्ड्रीसेक

1
@BerndJendrissek: संरचनाएं और यूनियनें अन्य प्रकारों से भिन्न हैं, लेकिन क्या क्लाइंट कोड को उन दो चीजों (स्ट्रक्चर या यूनियन) में से किसी एक के बारे में परवाह करनी चाहिए FILE?
सुपरकैट

4
@supercat FILE टाइपडीफ का एक अच्छा उपयोग है। मुझे लगता है कि typedef है overused , नहीं है कि यह भाषा की एक misfeature है। IMHO सब कुछ के लिए typedef का उपयोग कर "सट्टा overgenerality" कोड गंध है। ध्यान दें कि आप चर को FILE * foo के रूप में घोषित करते हैं, कभी भी FILE foo के रूप में नहीं। मेरे लिए, यह मायने रखता है।
बर्नजेंड्रीसेक

2
@supercat: "यदि फ़ाइल की पहचान करने वाले चर FILE के बजाय FILE के प्रकार के होते हैं ..." लेकिन यह वास्तव में अस्पष्टता है जो टाइप किए गए एफईएफ को सक्षम करता है! हम सिर्फ FILE लेने के लिए उपयोग कर रहे हैं * इसलिए हम इसके बारे में झल्लाहट नहीं करते हैं, लेकिन हर बार जब आप टाइप करते हैं तो आप एक और बिट कॉग्निटिव ओवरहेड शुरू कर रहे हैं: क्या यह API foo_t args या foo_t * चाहता है? स्पष्ट रूप से 'संरचना' को साथ ले जाने से तर्क की स्थानीयता में सुधार होता है, अगर प्रति फ़ंक्शन परिभाषा में कुछ और वर्णों की कीमत पर।
बेरंड जेन्ड्रीसेक

4

आइए मूल बातों से शुरू करें और अपने तरीके से काम करें।

यहाँ संरचना परिभाषा का एक उदाहरण है:

struct point
  {
    int x, y;
  };

यहां नाम pointवैकल्पिक है।

एक संरचना को इसकी परिभाषा के दौरान या उसके बाद घोषित किया जा सकता है।

परिभाषा के दौरान घोषणा

struct point
  {
    int x, y;
  } first_point, second_point;

परिभाषा के बाद घोषणा

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

अब, ऊपर के अंतिम मामले को ध्यान से देखें; struct pointयदि आप अपने कोड में बाद में उस प्रकार का निर्माण करने का निर्णय लेते हैं, तो आपको उस प्रकार की संरचनाएं घोषित करने के लिए लिखना होगा।

दर्ज करें typedef। यदि आप एक ही खाका का उपयोग करके अपने कार्यक्रम में बाद के समय में नई संरचना (संरचना एक कस्टम डेटा-प्रकार) बनाने का इरादा रखते हैं, तो typedefइसकी परिभाषा के दौरान उपयोग करना एक अच्छा विचार हो सकता है क्योंकि आप कुछ टाइपिंग को आगे बढ़ने से बचा सकते हैं।

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

अपने कस्टम प्रकार का नामकरण करते समय सावधानी का एक शब्द

कुछ भी आपको अपने कस्टम प्रकार के नाम के अंत में _t प्रत्यय का उपयोग करने से रोकता है लेकिन POSIX मानक मानक लाइब्रेरी प्रकार नामों को निरूपित करने के लिए प्रत्यय _t के उपयोग को सुरक्षित रखता है।


3

जो नाम आप (वैकल्पिक रूप से) देते हैं वह संरचना टैग नाम कहलाता है और, जैसा कि नोट किया गया है, अपने आप में एक प्रकार नहीं है। टाइप करने के लिए स्ट्रक्चर प्रीफिक्स की जरूरत होती है।

GTK + एक तरफ, मुझे यकीन नहीं है कि tagname का उपयोग कुछ भी किया जाता है जैसे कि आमतौर पर टाइप टाइप के रूप में संरचना प्रकार के लिए, इसलिए C ++ में जो मान्यता प्राप्त है और आप संरचना कीवर्ड को छोड़ सकते हैं और टाइप नाम के रूप में भी tagname का उपयोग कर सकते हैं:


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;


1

टाइपडीफ डेटा संरचनाओं का सह-निर्भर सेट प्रदान नहीं करेगा। यह आप typdef के साथ नहीं कर सकते हैं:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

बेशक आप हमेशा जोड़ सकते हैं:

typedef struct foo foo_t;
typedef struct bar bar_t;

वास्तव में क्या है?


1

डेटा प्रकारों के लिए अधिक सार्थक समानार्थी शब्द बनाने की अनुमति देकर प्रोग्राम के अर्थ और प्रलेखन में ए> टाइपडीएफ एड्स । इसके अलावा, वे पोर्टेबिलिटी समस्याओं (K & R, pg147, C prog lang) के खिलाफ एक प्रोग्राम को पैरामीटर बनाने में मदद करते हैं।

B> एक संरचना एक प्रकार को परिभाषित करती है । संरचनाएं एकल इकाई के रूप में हैंडलिंग की सुविधा (K & R, pg127, Cg lang।) के लिए var के संग्रह के सुविधाजनक समूहन की अनुमति देती हैं।

C> टाइपडिफिंग एक संरचना ए में ऊपर बताई गई है।

D> मेरे लिए, संरचनाएँ कस्टम प्रकार या कंटेनर या संग्रह या नाम स्थान या जटिल प्रकार हैं, जबकि एक टाइपडेफ़ केवल अधिक उपनाम बनाने का एक साधन है।


0

C99 टाइपफेड में टर्न आउट आवश्यक है। यह पुराना है, लेकिन बहुत सारे उपकरण (ala HackRank) अपने शुद्ध C कार्यान्वयन के रूप में c99 का उपयोग करते हैं। और वहां टाइप्डिफ़ की आवश्यकता है।

मैं यह नहीं कह रहा हूं कि उन्हें बदलना चाहिए (शायद दो सी विकल्प हैं) यदि आवश्यकता बदल गई, तो हम में से जो साइट पर साक्षात्कार के लिए अध्ययन कर रहे हैं वे एसओएल होंगे।


2
"C99 में टर्न आउट typedefकी आवश्यकता है।" क्या मतलब?
जूलियन लोपेज

सवाल यह है कि Cनहीं C++। में Ctypedefs हैं 'आवश्यक' (और सबसे अधिक संभावना हमेशा हो जाएगा)। 'आवश्यक' के रूप में, आप एक चर घोषित करने में सक्षम नहीं होंगे Point varName;और टाइप struct Point;बिना पर्यायवाची हो सकते हैं typedef struct Point Point;
21

0

'C' प्रोग्रामिंग लैंग्वेज में 'typeedef' कीवर्ड का इस्तेमाल किसी ऑब्जेक्ट (स्ट्रक्चर, एरे, फंक्शन..नम टाइप) के लिए एक नया नाम घोषित करने के लिए किया जाता है। उदाहरण के लिए, मैं 'संरचना-एस' का उपयोग करूंगा। 'सी' में हम अक्सर 'मेन' ​​फंक्शन के बाहर 'स्ट्रक्चर' घोषित करते हैं। उदाहरण के लिए:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

हर बार जब मैं एक संरचनात्मक प्रकार का उपयोग करने का निर्णय लेता हूं, तो मुझे इस कीवर्ड की संरचना 'कुछ' नाम की आवश्यकता होगी। तो हमारा कोड होगा:

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

यदि आपके पास कुछ स्थानीय वस्तु (संरचना, सरणी, मूल्यवान) है जो आपके पूरे कार्यक्रम में उपयोग की जाएगी, तो आप इसे केवल एक नाम लिख सकते हैं 'टाइप्डिफ'।


-2

सभी में, सी भाषा में, स्ट्रक्चर / यूनियन / एनम सी भाषा प्रीप्रोसेसर द्वारा संसाधित मैक्रो इंस्ट्रक्शन हैं (प्रीप्रोसेसर के साथ गलती न करें जो "#include" और अन्य का इलाज करते हैं)

इसलिए :

struct a
{
   int i;
};

struct b
{
   struct a;
   int i;
   int j;
};

संरचना ख कुछ इस तरह से खर्च की जाती है:

struct b
{
    struct a
    {
        int i;
    };
    int i;
    int j;
}

और इसलिए, संकलन समय पर यह स्टैक पर विकसित होता है जैसे कि: बी: इंट एआईआई इंट आई इंट जे

यह भी कि यह आत्मनिर्भर संरचनाएं होने के लिए अलग क्यों है, एक प्रसार लूप में सी प्रीप्रोसेसर राउंड को पूरा नहीं कर सकता है।

टाइपराइफ़ टाइप स्पेसिफ़ायर हैं, इसका मतलब है कि केवल सी कंपाइलर प्रक्रिया है और यह ऐसा कर सकता है जैसे वह कोडांतरक कोड कार्यान्वयन के लिए चाहता है। यह भी प्रकार के बराबर का सदस्य खर्च नहीं करता है जैसे कि préprocessor स्ट्रक्चर्स के साथ करता है, लेकिन अधिक जटिल संदर्भ निर्माण एल्गोरिथ्म का उपयोग करता है, इसलिए निर्माण निम्न है:

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

अनुमति है और पूरी तरह कार्यात्मक है। यह कार्यान्वयन कंपाइलटर प्रकार के रूपांतरण तक पहुंच प्रदान करता है और निष्पादन थ्रेड के प्रारंभिक कार्य को छोड़ देने पर कुछ बगिंग प्रभाव को हटा देता है।

इसका मतलब यह है कि सी टाइलेडिफ्स में अकेला पैटर्न की तुलना में सी ++ वर्ग के पास अधिक है।


3
सेल्फ रेफ़रेंशियल स्ट्रक्चर्स का होना मुश्किल नहीं है। संरचना फू {संरचनात्मक फू * अगला; int बात; }
बर्नजेंड्रीसेक

4
...क्या? कह रही है पूर्वप्रक्रमक के संकल्प का वर्णन करने के structsलिए और typedefएस बुरा पर्याप्त है, लेकिन अपने लेखन के बाकी तो भ्रामक है कि मैं यह मुश्किल यह से किसी भी संदेश प्राप्त करने के लिए लगता है। एक बात मैं कह सकता हूं, हालांकि, यह है कि आपका विचार यह है कि एक गैर- typedefडी structको आगे घोषित नहीं किया जा सकता है या एक अपारदर्शी (पॉइंटर) सदस्य के रूप में उपयोग किया जाता है, पूरी तरह से गलत है। आपके पहले उदाहरण में, struct bतुच्छ रूप से एक struct a *, कोई typedefआवश्यक नहीं हो सकता है। जो कथ्य structकेवल स्थूल-विस्तार के गूंगे टुकड़े हैं, और जो typedefउन्हें क्रांतिकारी नई शक्तियाँ प्रदान करते हैं, वे दर्दनाक रूप से गलत हैं
अंडरकॉकर_ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.