2016-02-26 7 views
37

मैं कॉन्स्ट-सही कोड बनाने की कोशिश करते समय एक छोटी सी समस्या पर ठोकर खाई।क्या जीसीसी मुझे सी 99 में एक कॉन्स स्ट्रक्चर के क्षेत्रों को संशोधित करने के बारे में चेतावनी दे सकता है?

मुझे कंपाइलर को बताने के लिए एक कॉन्स स्ट्रक्चर को पॉइंटर लेना पसंद होता, "कृपया मुझे बताएं कि क्या मैं संरचना को संशोधित कर रहा हूं, क्योंकि मैं वास्तव में नहीं चाहता हूं"। ,

struct A 
{ 
    char *ptrChar; 
}; 

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

कौन सा समझा जा सकता है क्योंकि वास्तव स्थिरांक है सूचक ही नहीं, बल्कि प्रकार यह ओर इशारा करता है:

यह अचानक मेरे दिमाग कि संकलक मुझे ऐसा करने की अनुमति देगा के लिए आया था। मैं संकलक के बारे में बताना चाहता हूं कि मैं ऐसा कुछ कर रहा हूं जो मैं नहीं करना चाहता, हालांकि, यदि यह भी संभव है।

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

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

क्या इस मुद्दे को हल करना संभव है?

+0

यह मामला सदस्य के असाइनमेंट के लिए काम करता है। E.g 'ptrA-> ptrChar = malloc (2); 'कौन सा सदस्य इंगित कर रहा है ऐसा नहीं है। – BLUEPIXY

+0

[इसी तरह का सवाल] (http://stackoverflow.com/questions/13181546/const-correctness-for-structs-with-pointers) –

+0

इस उद्देश्य के लिए मेरे पास आमतौर पर एक शीर्षलेख होता है जो 'आगे एक संरचना' घोषित करता है, और, 'a_do_something (const struct * a) में कुछ फ़ंक्शन घोषणाएं;' हेडर फ़ाइल में 'स्ट्रक्चर ए' की पूरी परिभाषा को शामिल करना शायद ही आवश्यक है। आपने अलग-अलग अनुवाद इकाइयों में अपने 'f' जैसे कई फ़ंक्शंस डाले हैं, जहां' f' केवल इस तरह की सामग्री = a_do_something (ptrA) की तरह पीआरटीए का उपयोग करेगा; ' –

उत्तर

19

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

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 अब अपारदर्शी प्रकार के रूप में लागू किया जा सकता , कॉलर के लिए कार्यान्वयन छुपाने के लिए।

+1

मुझे संघ पसंद है और मुझे पूरा यकीन है कि यह अभ्यास में काम करेगा; क्या इसकी अनुमति है? संरचना प्रकार ar, iiuc, असंगत। –

+0

@ पीटर ए। स्कैनइडर मुझे विश्वास है कि "सख्त एलियासिंग नियम" (6.5/7) और यूनियन "टाइप पनिंग नियम" (6.2.6.1/7) के अनुसार। हालांकि बाद के अनुसार, यह अनिर्दिष्ट व्यवहार हो सकता है। यह वास्तव में कोई फर्क नहीं पड़ता, क्योंकि आप कहते हैं, यह हमेशा अभ्यास में काम करेगा। मैं नहीं देखता कि संकलक को इस कोड को अपेक्षा से अलग तरीके से लागू करने के लिए कैसे समझदारी होगी, चाहे मानक में कोई सूक्ष्म नियम न हो। हालांकि यह सी ++ में काम नहीं कर सकता है, क्योंकि आईआईआरसी सी ++ यूनियनों के माध्यम से दंडित करने की अनुमति नहीं देता है। – Lundin

+5

@ लंदन: जीसीसी से सावधान रहें। यह धारणाओं के आधार पर अनुकूलित करने के बारे में काफी आक्रामक है कि यह सख्त एलियासिंग नियम से प्राप्त हो सकता है। –

2

नहीं है, जब तक आप struct परिभाषा बदलने: एक अन्य जटिल समाधान है कि पुराने struct परिभाषा को वैसे ही रखता

struct A 
{ 
    const char *ptrChar; 
}; 

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

+0

मुझे आश्चर्य है कि एक संरचना में एक साधारण कलाकार जो केवल अलग है सदस्यों की सीवी योग्यता यूबी होगी? –

+0

@ पीटर ए। स्केनेडर हां यह होगा, प्रकार असंगत होंगे। – 2501

7

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

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

5

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

5

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

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_ptrChar पर निरंतर के लिए एक सूचक है। फिर मैक्रो निर्णय लेता है कि इसके पैरामीटर के प्रकार के आधार पर कौन सा संदर्भ देना है।

एकमात्र समस्या यह है कि मैक्रो ptrChar_m केवल इस संरचना के सूचक या वस्तु के साथ काम कर सकता है, दोनों नहीं।

+1

अच्छा समाधान, हालांकि आपको '।' मामलों को छोड़ना है, या आपको मैक्रो विस्तार के साथ समस्याएं मिलेंगी।अंडरस्कोर से शुरू होने वाले परिवर्तनीय नामों से भी बचें (7.1.3 देखें)। – Lundin

+4

कृपया यह मत करें। यह खराब है। कोई भी इसे एक साल में समझने वाला नहीं है, यहां तक ​​कि आप भी नहीं। – fuz

+0

मुझे यूनियन दंड पसंद नहीं है। क्या हम इसके बजाय '_Generic (ए, स्ट्रक्चर ए *: ए-> पीआरटीएचएचआर, कॉन्स स्ट्रक्चर ए *: (कॉन्स चार *) ए-> पीआरटीएचएचआर 'का उपयोग कर सकते हैं? इस तरह, हमें संरचना की परिभाषा को बदलने की जरूरत नहीं है। – chi

3

हम कुछ "एक्सेसर" कार्यों के पीछे जानकारी को छुपाने सकता है:

// header 
struct A;   // incomplete type 
char *getPtr(struct A *); 
const char *getCPtr(const struct A *); 

// implementation 
struct A { char *ptr }; 
char *getPtr(struct A *a) { return a->ptr; } 
const char *getCPtr(const struct A *a) { return a->ptr; } 
1

मुझे C99 में एक स्थिरांक struct के क्षेत्रों को संशोधित करने के बारे में चेतावनी जीसीसी कर सकते हैं?

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

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

आप जहां एक struct एक के चार क्षेत्र अंक (कि struct एक के माध्यम से) तो

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

लेकिन का उपयोग पर मूल्य के लिए परिवर्तन की अनुमति के लिए हो सकता है कि आप क्या सोचते आप में क्या कर रहे हैं नहीं चाहते हैं एफ

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

कुछ ऐसा है जो संकलक से त्रुटि प्राप्त करेगा।

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

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