2010-10-02 12 views
10

मैं सोच रहा था:संरचनाएं और सी में कास्टिंग

अगर मैं संरचना परिभाषाएं हैं, उदाहरण के लिए, इस तरह:

struct Base { 
    int foo; 
}; 

struct Derived { 
    int foo; // int foo is common for both definitions 
    char *bar; 
}; 

मैं कुछ इस तरह कर सकते हैं?

void foobar(void *ptr) { 
    ((struct Base *)ptr)->foo = 1; 
} 

struct Derived s; 

foobar(&s); 

ई। जी। अपने foo तक पहुंचने के लिए शून्य सूचक को Base * पर डालें जब इसका प्रकार वास्तव में Derived * है?

+3

क्या आपने इसे आजमाया? क्या हुआ ? –

+4

यह संकलक शिकायत के बिना काम करता है, लेकिन मैं जानना चाहता हूं कि यह एक सामान्य उपयोग योग्य अभ्यास है, या कुछ बुरा हैक (यह अभी मुझे एक टन में मदद करेगा)। – Anon

+1

@PaulR यदि आपका प्रश्न "पूछने के बजाय स्वयं को आज़माएं" कहने के लिए है, तो आपको अनिर्धारित व्यवहार के बारे में जानना चाहिए और यह आज आपके प्रयोग को सही कैसे दिख सकता है लेकिन कल विफल हो सकता है - और उपाध्यक्ष। – hmijail

उत्तर

7

कई वास्तविक दुनिया सी प्रोग्राम मानते हैं कि आपके द्वारा दिखाया गया निर्माण सुरक्षित है, और सी मानक (विशेष रूप से, "सामान्य प्रारंभिक अनुक्रम" नियम, सी 99 §6.5.2.3 पी 5) की व्याख्या है जिसके तहत यह है अनुरूप है। दुर्भाग्यवश, पांच वर्षों में जब मैंने मूल रूप से इस प्रश्न का उत्तर दिया, तो सभी कंपाइलर्स जिन्हें मैं आसानी से प्राप्त कर सकता हूं (जैसे जीसीसी और क्लैंग) ने सामान्य प्रारंभिक अनुक्रम नियम की एक अलग, संक्षिप्त व्याख्या पर अभिसरण किया है, जिसके अंतर्गत आप जो निर्माण दिखाते हैं अपरिभाषित व्यवहार। वस्तुतः, इस कार्यक्रम के साथ प्रयोग:

#include <stdio.h> 
#include <string.h> 

typedef struct A { int x; int y; }   A; 
typedef struct B { int x; int y; float z; } B; 
typedef struct C { A a;   float z; } C; 

int testAB(A *a, B *b) 
{ 
    b->x = 1; 
    a->x = 2; 
    return b->x; 
} 

int testAC(A *a, C *c) 
{ 
    c->a.x = 1; 
    a->x = 2; 
    return c->a.x; 
} 

int main(void) 
{ 
    B bee; 
    C cee; 
    int r; 

    memset(&bee, 0, sizeof bee); 
    memset(&cee, 0, sizeof cee); 

    r = testAB((A *)&bee, &bee); 
    printf("testAB: r=%d bee.x=%d\n", r, bee.x); 

    r = testAC(&cee.a, &cee); 
    printf("testAC: r=%d cee.x=%d\n", r, cee.a.x); 

    return 0; 
} 

जब सक्षम (और -fno-strict-aliasing के बिना) अनुकूलन के साथ संकलन, दोनों जीसीसी और बजना समझेंगे कि testABकरने के लिए दो सूचक तर्क एक ही वस्तु को इंगित नहीं कर सकते, तो मैं मिलता है जैसे

testAB: r=1 bee.x=2 
testAC: r=2 cee.x=2 

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

+0

टिप्पणियां विस्तारित चर्चा के लिए नहीं हैं; यह वार्तालाप [चैट करने के लिए स्थानांतरित हो गया है] (http://chat.stackoverflow.com/rooms/106141/discussion-on-answer-by-zwol- संरचनाएं-and-casting-in-c)। –

+0

@ जॉर्जस्टॉकर मुझे इस मामले में इतनी परवाह नहीं है क्योंकि यह टिप्पणियों में विस्तारित चर्चा नहीं कर रहा था, लेकिन यह पहली बार हुआ है कि यह मेरे स्टैक ओवरफ़्लो उत्तरों में से एक के साथ हुआ है, इसलिए आपको इस प्रकार अधिसूचित किया गया है कि मैं नहीं चाहते कि मॉडरेटर मेरे प्रश्नों और उत्तरों पर * किसी भी * टिप्पणियों को माइग्रेट या हटाना चाहते हैं, न ही किसी भी प्रश्न या उत्तर पर किसी भी परिस्थिति में * किसी भी परिस्थिति में, किसी भी परिस्थिति में या किसी भी परिस्थिति में माइग्रेट या हटाएं। वर्तमान नीति गलत है और साइट पर सक्रिय रूप से हानिकारक है। – zwol

1

विशेष मामलों में यह काम कर सकता है, लेकिन सामान्य रूप से - नहीं, संरचना संरेखण की वजह से।

आप अलग-अलग #pragma एस का उपयोग संरेखण के समान (वास्तव में, प्रयास करने) के लिए कर सकते हैं - और फिर, हाँ, यह काम करेगा।

यदि आप माइक्रोसॉफ्ट विजुअल स्टूडियो का उपयोग कर रहे हैं, तो आपको this article उपयोगी मिल सकता है।

+0

लिंक के अंत में डॉट हटाएं :) – IProblemFactory

+0

यह पता लगाया गया है कि – Anon

-1

सी के बारे में महान/बुरी बात यह है कि आप बस कुछ भी डाल सकते हैं - समस्या यह है कि यह काम नहीं कर सकता है। :) हालांकि, आपके मामले में, यह * होगा, क्योंकि आपके पास दो structs हैं जिनके पहले सदस्य दोनों एक ही प्रकार के हैं; उदाहरण के लिए this program देखें। अब, यदि struct derived के पहले तत्व के रूप में एक अलग प्रकार था - उदाहरण के लिए, char *bar - तो नहीं, आपको अजीब व्यवहार मिल जाएगा।

* मुझे "लगभग हमेशा" के साथ योग्यता प्राप्त करनी चाहिए, मुझे लगता है; वहाँ बहुत सारे सी संकलक हैं, इसलिए कुछ अलग व्यवहार हो सकते हैं। हालांकि, मुझे पता है कि यह जीसीसी में काम करेगा।

+0

"लगभग हमेशा" और उदाहरण के रूप में एक प्रोग्राम का हवाला देते हुए सी और यूबी से संबंधित समस्याओं के बारे में बात करते समय आपदा के लिए एक नुस्खा है। – hmijail

1

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

11

आप

struct Base { 
    int foo; 
}; 

struct Derived { 
    struct Base base; 
    char *bar; 
}; 

करना चाहिए सख्त अलियासिंग को तोड़ने से बचने के लिए; यह एक आम गलत धारणा है कि सी पॉइंटर प्रकारों के मनमानी जानवरों की अनुमति देता है: हालांकि यह अधिकांश कार्यान्वयन में अपेक्षित काम करेगा, यह गैर-मानक है।

यह प्रगमा निर्देशों के उपयोग के कारण किसी भी संरेखण असंगतताओं से बचाता है।

1

आप सीआई आप नीचे दिए गए लिंक पर एक नज़र है करने के लिए कर सकते हैं सुझाव में ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग में लक्ष्य हो रहे हैं के रूप में:

http://www.planetpdf.com/codecuts/pdfs/ooc.pdf

यह एएनएसआई सी में OOP सिद्धांतों से निपटने के तरीके के बारे में विस्तार में चला जाता है ।

+0

धन्यवाद, ऐसा पहला संसाधन प्रतीत होता है जो मैं परामर्श कर रहा हूं जो वास्तव में उपयोगी है :) – Anon

+0

इसे "गरीब व्यक्ति का ओओपी" भी कहा जाता है। – ruslik

0

एक और छोटी सी बात यह है कि सहायक या तुम क्या कर रहे करने के लिए संबंधित हो सकता है नहीं है ..

#define SHARED_DATA int id; 

typedef union base_t { 
    SHARED_DATA; 
    window_t win; 
    list_t list; 
    button_t button;   
} 

typedef struct window_t { 
    SHARED_DATA; 
    int something; 
    void* blah; 
} 

typedef struct window_t { 
    SHARED_DATA; 
    int size; 
} 

typedef struct button_t { 
    SHARED_DATA; 
    int clicked; 
} 

अब आप साझा गुणों को SHARED_DATA में डाल सकते हैं और यूनियन में पैक किए गए "सुपरक्लास" के माध्यम से विभिन्न प्रकारों को संभाल सकते हैं .. आप केवल 'क्लास पहचानकर्ता' को स्टोर करने या पॉइंटर स्टोर करने के लिए SHARED_DATA का उपयोग कर सकते हैं .. किसी भी तरह से यह आसान हो गया किसी बिंदु पर मेरे लिए घटना प्रकारों का सामान्य प्रबंधन। उम्मीद है कि मैं इस

0

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

सबसे पहले, यह डाली:

(struct Base *)ptr 

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

हालांकि, व्यावहारिक रूप से, सामान्य प्रणालियों के लिए कंपाइलर आमतौर पर एक पॉइंटर कास्ट का परिणाम उसी ऑब्जेक्ट को संदर्भित करते हैं।

(पॉइंटर कास्ट सी 99 मानक और हाल ही में सी 11 मानक दोनों के खंड 6.3.2.3 में शामिल हैं। नियमों में अनिवार्य रूप से दोनों ही हैं, मुझे विश्वास है)।

अंत में, आपको "सी 99/सी 11 6.5 अनुच्छेद 7) के साथ संघर्ष करने के लिए तथाकथित" सख्त एलियासिंग "नियम मिल गए हैं; मूल रूप से, आपको किसी अन्य प्रकार के पॉइंटर के माध्यम से एक प्रकार की ऑब्जेक्ट तक पहुंचने की अनुमति नहीं है (कुछ अपवादों के साथ, जो आपके उदाहरण में लागू नहीं होते हैं)। "What is the strict-aliasing rule?" देखें, या बहुत गहन चर्चा के लिए, इस विषय पर मेरे blog post पढ़ें।

निष्कर्ष में, आप अपने कोड में जो प्रयास करते हैं, वह काम करने की गारंटी नहीं देता है। यह हमेशा कुछ कंपाइलरों (और कुछ कंपाइलर विकल्पों के साथ) के साथ काम करने की गारंटी दी जा सकती है, और यह कई कंपाइलरों के साथ मौका दे सकता है, लेकिन यह निश्चित रूप से सी भाषा मानक के अनुसार अपरिभाषित व्यवहार का आह्वान करता है।

*((int *)ptr) = 1; 

... अर्थात:

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

+0

"अन्य उत्तरों गलत हैं" -> कौन सा? और आपका उत्तर उनके से अलग कैसे है? – hmijail

+0

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

+0

@hmijail btw स्वीकृत उत्तर मेरी अपनी टिप्पणियों के कारण संशोधित किया गया था, [चर्चा] देखें (http://chat.stackoverflow.com/rooms/106141/discussion-on-answer-by-zwol- संरचनाएं-and-casting -इन-ग)। संयोग से दूसरों से मेरा जवाब सबसे ज्यादा अंतर करता है कि यह वास्तव में सख्त एलियासिंग नियम पर चर्चा करता है। – davmac

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