एसस क्या GCC मुझे C99 में एक कास्ट स्ट्रक्चर के क्षेत्रों को संशोधित करने के बारे में चेतावनी दे सकता है?




सामान्य जाति लिस्ट (6)

मैं एक छोटे से मुद्दे पर ठोकर खाई, जबकि सही-सही कोड बनाने की कोशिश कर रहा था।

मैं एक फ़ंक्शन लिखना पसंद करता हूं जो एक संकरा संरचना के लिए एक संकेतक लेता है, संकलक को बताने के लिए "कृपया मुझे बताएं कि क्या मैं संरचना को संशोधित कर रहा हूं, क्योंकि मैं वास्तव में नहीं चाहता"

यह अचानक मेरे दिमाग में आया कि संकलक मुझे ऐसा करने की अनुमति देगा:

struct A
{
    char *ptrChar;
};

void f(const struct A *ptrA)
{
    ptrA->ptrChar[0] = 'A'; // NOT DESIRED!!
}

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

मैंने अपने संकलक के रूप में जीसीसी का उपयोग किया। हालाँकि मुझे पता है कि ऊपर दिया गया कोड कानूनी होना चाहिए, फिर भी मैंने जाँच की कि क्या यह चेतावनी चेतावनी जारी करेगा, लेकिन कुछ भी नहीं आया। मेरी कमांड लाइन थी:

gcc -std=c99 -Wall -Wextra -pedantic test.c

क्या इस मुद्दे के आसपास जाना संभव है?


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


क्या GCC मुझे C99 में एक कास्ट स्ट्रक्चर के क्षेत्रों को संशोधित करने के बारे में चेतावनी दे सकता है?

आप किसी कांस्टेबल संरचना के क्षेत्रों को संशोधित नहीं कर रहे हैं।

संरचना A के मान में एक नॉन-कॉस्ट चार के लिए एक पॉइंटर होता है। ptrA एक const संरचना ए का सूचक है। इसलिए आप * ptrA पर संरचना A मान को बदल नहीं सकते। इसलिए आप पॉइंटर को (* ptrA) पर नहीं बदल सकते हैं ।Char उर्फ ​​ptrA-> ptrChar। लेकिन आप उस मान को बदल रहे हैं, जहां ptrA-> ptrChar अंक, यानी मान पर * (ptrA-> चार) उर्फ ​​ptrA-> चार [0]। यहाँ केवल एकमात्र संरचना के रूप में संरचित हैं और आप एक संरचना A को नहीं बदल रहे हैं, इसलिए "वांछित नहीं" क्या सटीक है?

यदि आप उस मूल्य पर परिवर्तन करने की अनुमति नहीं देना चाहते हैं जहां एक संरचना A के चार फ़ील्ड बिंदु (उस संरचना A के माध्यम से) का उपयोग करें

struct A
{
    const char *ptrChar; // or char const *ptrChar;
};

लेकिन शायद आपको लगता है कि आप एफ में कर रहे हैं कुछ ऐसा है

void f(const struct A *ptrA)
{
    const char c = 'A';
    ptrA->ptrChar = &c;
}

जिसे कंपाइलर से एरर मिलेगा।


इसके चारों ओर अपने तरीके से डिजाइन करने के लिए, यदि आवश्यक हो, तो एक ही वस्तु के लिए दो अलग-अलग प्रकारों का उपयोग करना है: एक रीड / राइट टाइप और एक रीड-ओनली टाइप।

typedef struct
{
  char *ptrChar;
} A_rw;

typedef struct
{
  const char* ptrChar;
} A_ro;


typedef union
{
  A_rw rw;
  A_ro ro;  
} A;

यदि किसी फ़ंक्शन को ऑब्जेक्ट को संशोधित करने की आवश्यकता होती है, तो यह रीड-राइट टाइप को पैरामीटर के रूप में लेता है, अन्यथा यह रीड-ओनली टाइप लेता है।

void modify (A_rw* a)
{
  a->ptrChar[0] = 'A';
}

void print (const A_ro* a)
{
  puts(a->ptrChar);
}

कॉलर इंटरफ़ेस को सुंदर बनाने और इसे सुसंगत बनाने के लिए, आप अपने ADT में सार्वजनिक इंटरफ़ेस के रूप में आवरण कार्यों का उपयोग कर सकते हैं:

inline void A_modify (A* a)
{
  modify(&a->rw);
}

inline void A_print (const A* a)
{
  print(&a->ro);
}

इस पद्धति के साथ, A को अब अपारदर्शी प्रकार के रूप में लागू किया जा सकता है, कॉल करने वाले के लिए कार्यान्वयन को छिपाने के लिए।


नहीं, जब तक आप संरचना की परिभाषा को नहीं बदलते हैं:

struct A
{
    const char *ptrChar;
};

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


हो सकता है कि यदि आप C11 का उपयोग करने का निर्णय लेते हैं तो आप एक जेनेरिक मैक्रो को लागू कर सकते हैं जो या तो एक ही सदस्य के निरंतर या परिवर्तनशील संस्करण को संदर्भित करता है (आपको अपनी संरचना में एक संघ भी शामिल करना चाहिए)। कुछ इस तरह:

struct A
{
    union {
        char *m_ptrChar;

        const char *m_cptrChar;
    } ;
};

#define ptrChar_m(a) _Generic(a, struct A *: a->m_ptrChar,        \
                                 const struct A *: a->m_cptrChar)//,  \
                                 //struct A: a.m_ptrChar,        \
                                 //const struct A: a.m_cptrChar)

void f(const struct A *ptrA)
{
    ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
}

संघ एक सदस्य के लिए 2 व्याख्या बनाता है। m_cptrChar निरंतर m_cptrChar का सूचक है और m_ptrChar गैर-स्थिरांक के लिए। फिर मैक्रो तय करता है कि यह किस प्रकार के पैरामीटर के आधार पर संदर्भित किया जाए।

एकमात्र समस्या यह है कि मैक्रो ptrChar_m केवल इस संरचना के सूचक या ऑब्जेक्ट के साथ काम कर सकता है और दोनों नहीं।


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

C में कोई भी ऐसा फंक्शन डिफाइन कर सकता है जो एक पॉइंटर को पैरामीटर के रूप में स्ट्रक्चर में ले जाता है और पॉइंटर को कॉन्स्ट चर में लौटाता है। उन उपयोगकर्ताओं के लिए जो इन संरचनाओं के उदाहरण नहीं बनाते हैं, कोई भी "उपयोगकर्ता हेडर" प्रदान कर सकता है जो संरचना के कार्यान्वयन को लीक नहीं करता है, लेकिन केवल (कारखाने की तरह) लौटने वाले कार्यों में हेरफेर करने वाले कार्यों को परिभाषित करता है। यह संरचना को एक अपूर्ण प्रकार छोड़ देता है (ताकि उदाहरण के लिए केवल संकेत का उपयोग किया जा सके)। यह पैटर्न प्रभावी रूप से यह बताता है कि this सूचक के साथ सी + + पर्दे के पीछे क्या करता है।







c99