2010-07-14 18 views
43

मैंने बड़े प्रोग्राम के हिस्से के रूप में फ़ाइलों से डेटा पढ़ने के लिए कोड के निम्न भाग का उपयोग किया।डीरफ्रेंसिंग प्रकार-दंडित सूचक सख्त-एलियासिंग नियमों को तोड़ देगा

double data_read(FILE *stream,int code) { 
     char data[8]; 
     switch(code) { 
     case 0x08: 
      return (unsigned char)fgetc(stream); 
     case 0x09: 
      return (signed char)fgetc(stream); 
     case 0x0b: 
      data[1] = fgetc(stream); 
      data[0] = fgetc(stream); 
      return *(short*)data; 
     case 0x0c: 
      for(int i=3;i>=0;i--) 
       data[i] = fgetc(stream); 
      return *(int*)data; 
     case 0x0d: 
      for(int i=3;i>=0;i--) 
       data[i] = fgetc(stream); 
      return *(float*)data; 
     case 0x0e: 
      for(int i=7;i>=0;i--) 
       data[i] = fgetc(stream); 
      return *(double*)data; 
     } 
     die("data read failed"); 
     return 1; 
    } 

अब मैं -O2 उपयोग करने के लिए बताया गया है और मैं जीसीसी चेतावनी निम्नलिखित हो: warning: dereferencing type-punned pointer will break strict-aliasing rules

Googleing मैं दो ओर्थोगोनल जवाब मिला:

बनाम

अंत मैं चेतावनी की अनदेखी नहीं करना चाहते हैं। आप क्या सुझाव देंगे?

[अद्यतन] मैंने वास्तविक कार्य के साथ खिलौना उदाहरण को प्रतिस्थापित किया।

+0

आपका समारोह एक डबल लौटा रहा है, लेकिन आप एक int में अपनी वापसी कास्ट करें। डबल क्यों नहीं डाला? –

+0

आपूर्ति किए गए लिंक का मेरा पठन: bytes.com लिंक अधिकतर गलत लगता है (वास्तव में जीसीसी 4.x जारी होने के बाद चीजें बदल गई हैं), जबकि SO लिंक ठीक प्रतीत होता है। मैं कर रहा हूँ एक सा त्रुटि संदेश से उलझन में क्योंकि मैंने सोचा अलियासिंग नियमों बाहर रखा गया 'char' प्रकार (यानी एक' char' सूचक हमेशा अन्य संकेत उर्फ ​​करने के लिए अनुमति दी है, जब तक यह है 'प्रतिबंधित C99," 6.5 भाव ", खंड 7. – Dummy00001

+0

देखें 'ed।) हो सकता है कि आपको इसे लागू करने के लिए इसे 'हस्ताक्षरित चार' बनाना होगा ..? मुझे सही जवाब देखने में दिलचस्पी होगी। –

उत्तर

25

यह क्या तुम सच में fread उपयोग करना चाहते हैं के रूप में एक बहुत लग रहा है:

int data; 
fread(&data, sizeof(data), 1, stream); 

कहा, यदि आप वर्ण पढ़ने, फिर उन्हें किसी पूर्णांक, सुरक्षित तरीका है के रूप में पुनर्व्याख्या के मार्ग से जाने के लिए चाहते हैं सी में यह कर (लेकिन नहीं C++) है एक संघ का उपयोग करें:

union 
{ 
    char theChars[4]; 
    int theInt; 
} myunion; 

for(int i=0; i<4; i++) 
    myunion.theChars[i] = fgetc(stream); 
return myunion.theInt; 

मुझे यकीन है कि क्यों अपने मूल कोड में data की लंबाई 3. है मुझे लगता है आप 4 बाइट चाहता था नहीं हूँ, कम से कम मुझे किसी भी सिस्टम के बारे में पता नहीं है जहां एक int 3 बाइट्स है।

ध्यान दें कि आपका कोड और मेरा दोनों गैर-पोर्टेबल हैं।

संपादित करें: यदि आप एक फ़ाइल से विभिन्न लंबाई की ints को पढ़ने के लिए portably चाहते हैं, कुछ इस तरह का प्रयास करें:

unsigned result=0; 
for(int i=0; i<4; i++) 
    result = (result << 8) | fgetc(stream); 

(नोट: कोई वास्तविक कार्यक्रम में, आप अतिरिक्त वापसी मान का परीक्षण करना चाहते हैं ईएफओ के खिलाफ fgetc() के।)

यह 4-बाइट फ़ाइल को थोड़ा-एंडियन प्रारूप, में फ़ाइल से हस्ताक्षरित नहीं करता है, चाहे सिस्टम की अंतहीनता के बावजूद। यह किसी भी सिस्टम पर काम करना चाहिए जहां एक हस्ताक्षरित कम से कम 4 बाइट्स है।

यदि आप एंडियन-तटस्थ होना चाहते हैं, तो पॉइंटर्स या यूनियनों का उपयोग न करें; इसके बजाय बिट-शिफ्ट का उपयोग करें।

+6

+1। फिर से तनाव के लिए: एक संघ कोड को सख्त अलियासिंग अनुपालन रखने का एक आधिकारिक तरीका है। यह जीसीसी विशिष्ट नहीं है, यह सिर्फ जीसीसी के अनुकूलक सम्मान में अधिक टूटा हुआ है। चेतावनियों को अनदेखा नहीं किया जाना चाहिए: या तो स्पष्ट रूप से -फिक्ट-अलियासिंग अनुकूलन अक्षम करें या कोड को ठीक करें। – Dummy00001

+0

मैंने '3-बाइट-इंट' तय किया। क्या एक संघ पोर्टेबल होगा? – Framester

+1

@ फ्रैमेस्टर: आप जिस पोर्ट को पोर्ट करना चाहते हैं उस पर निर्भर करता है। अधिकांश डेस्कटॉप सिस्टम और किन का मतलब 32-बिट 'int' से एक ही बात है, लेकिन कुछ बड़े-एंडियन हैं और कुछ छोटे-एंडियन हैं, जिसका अर्थ है' int 'में बाइट्स का क्रम भिन्न हो सकता है। –

1

असल में आप जीसीसी के संदेश को लड़के के रूप में पढ़ सकते हैं जिसे आप परेशानी की तलाश में हैं, ऐसा न कहें कि मैंने को चेतावनी नहीं दी है।

एक 0 बाइट वर्ण सरणी को int पर कास्ट करना सबसे खराब चीजों में से एक है जिसे मैंने कभी देखा है। आम तौर पर आपके int में कम से कम 4 बाइट हैं। तो चौथे के लिए (और शायद int व्यापक है) आपको यादृच्छिक डेटा मिलता है। और फिर आप इसे double पर डाल दें।

बस इनमें से कोई भी नहीं करें। एलआईसीई समस्या जो जीसीसी के बारे में चेतावनी देती है वह आप जो कर रहे हैं उसके मुकाबले निर्दोष है।

+4

हाय, मैंने वास्तविक समारोह के साथ खिलौना उदाहरण को प्रतिस्थापित किया। और 3 बाइट्स के साथ int सिर्फ मुझसे एक टाइपो था। – Framester

-4

स्पष्ट रूप से मानक आकार (int *) को आकार (int *) से अलग होने की अनुमति देता है, इसलिए जब आप प्रत्यक्ष कलाकार का प्रयास करते हैं तो जीसीसी शिकायत करता है। शून्य * एक छोटा सा विशेष है जिसमें सबकुछ शून्य से आगे और आगे परिवर्तित किया जा सकता है। प्रैक्टिस में मुझे कई आर्किटेक्चर/कंपाइलर नहीं पता हैं, जहां एक पॉइंटर हमेशा सभी प्रकार के लिए समान नहीं होता है, लेकिन जीसीसी एक चेतावनी को छोड़ने का अधिकार है, भले ही यह परेशान हो।

मुझे लगता है कि जिस तरह से सुरक्षित

int i, *p = &i; 
char *q = (char*)&p[0]; 

या

होगा
char *q = (char*)(void*)p; 

आप भी इस कोशिश करते हैं और आप क्या मिलता देख सकते हैं:

char *q = reinterpret_cast<char*>(p); 
+3

'reinterpret_cast' सी ++ है। यह सी – ptomato

+3

"_ मानक मानक (char *) आकार (int *) _ से भिन्न होने की अनुमति देता है" या उनके पास एक ही आकार हो सकता है लेकिन अलग-अलग प्रतिशोध हो सकता है, लेकिन फिर भी इस समस्या के साथ इसका कोई लेना-देना नहीं है। यह सवाल टाइप-पनिंग के बारे में है, पॉइंटर प्रतिनिधित्व नहीं। "' char * q = (char *) और p [0] '" समस्या यह नहीं है कि विभिन्न प्रकार के दो बिंदुओं को एक ही पते पर इंगित करने के लिए कैसे मिलता है। यह प्रश्न टाइप-पनिंग के बारे में है, पॉइंटर कास्ट नहीं है। – curiousguy

7

एक संघ का प्रयोग नहीं है यहां करने के लिए सही चीज है। संघ के एक अवांछित सदस्य से पढ़ना अनिर्धारित है - यानी संकलक अनुकूलन करने के लिए स्वतंत्र है जो आपके कोड को तोड़ देगा (लिखने को अनुकूलित करने की तरह)।

char data[8]; 
... 
return *(double*)data; 

लेकिन जीसीसी मानता है कि अपने कार्यक्रम हालांकि विभिन्न प्रकार के संकेत दिए गए चर का उपयोग कभी नहीं होगा: क्योंकि आप एक double* के माध्यम से एक चार-सरणी का उपयोग

+0

"संघ के एक अनजान सदस्य से अपरिभाषित है_" इस साधारण मामले में: 'संघ यू {int i; निकर; } यू; अमेरिकी = 1; वापसी यू.आई; ', हाँ। सामान्य में, यह निर्भर करता है। – curiousguy

+2

सी में संघ अच्छी तरह से परिभाषित व्यवहार है; सी ++ में यह अपरिभाषित व्यवहार है। –

36

समस्या होती है। यह धारणा कुछ अनुकूलन बनाने के लिए सख्त अलियासिंग कहा जाता है और अनुमति देता है संकलक:

संकलक जानता है कि अपने *(double*)data[] साथ कोई रास्ता नहीं ओवरलैप में, यह चीजों के सभी प्रकार के में अपने कोड को पुन: क्रम की तरह दी जाने वाली अनुमति कर सकते हैं:

return *(double*)data; 
for(int i=7;i>=0;i--) 
    data[i] = fgetc(stream); 

पाश सबसे अधिक संभावना दूर अनुकूलित है और आप बस के साथ अंत:

return *(double*)data; 

कौन सा अपने डेटा [] अप्रारंभीकृत छोड़ देता है। इस विशेष मामले में संकलक यह देख सकता है कि आपके पॉइंटर्स ओवरलैप हो गए हैं, लेकिन अगर आपने इसे char* data घोषित कर दिया है, तो यह बग दे सकता था।

लेकिन, सख्त-एलियासिंग नियम कहता है कि एक char * और शून्य * किसी भी प्रकार पर इंगित कर सकता है। तो आप इसे फिर से लिख सकते हैं:

double data; 
... 
*(((char*)&data) + i) = fgetc(stream); 
... 
return data; 

सख्त एलियासिंग चेतावनियां समझने या ठीक करने के लिए वास्तव में महत्वपूर्ण हैं। वे ऐसे प्रकार की बग का कारण बनते हैं जो घर में पुन: उत्पन्न करना असंभव हैं क्योंकि वे केवल एक विशेष मशीन पर एक विशेष ऑपरेटिंग सिस्टम पर और विशेष रूप से पूर्ण-चंद्रमा पर और एक वर्ष में एक विशेष ऑपरेटिंग सिस्टम पर होते हैं।

0

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

int x; 
int foo(double *d) 
{ 
    x++; 
    *d=1234; 
    return x; 
} 

एक संकलक ग्रहण करने के लिए है कि करने के लिए * घ जीता लिखने के हकदार हो जाएगा एक्स को प्रभावित नहीं करते हैं। स्टैंडर्ड के लेखकों कि प्रकार पूरी तरह से मैच की आवश्यकता के बिना, स्थितियों कि किसी अज्ञात स्रोत से एक सूचक प्राप्त ऊपर की तरह एक समारोह ग्रहण करने के लिए है कि यह एक मालूम होता है-असंबंधित वैश्विक उर्फ ​​सकता है के लिए होता है, जहां सूची करना चाहता था। दुर्भाग्य से, जबकि औचित्य दृढ़ता से पता चलता है कि स्टैंडर्ड के लेखकों जहां एक संकलक अन्यथा विश्वास है कि चीजें उर्फ ​​सकता है कोई कारण नहीं होता मामलों में कम से कम अनुरूपता के लिए एक मानक का वर्णन करने का इरादा, शासन की आवश्यकता होती है कि compilers में aliasing पहचानने में विफल रहता है ऐसे मामलों में जहां यह स्पष्ट है और जीसीसी के लेखकों का फैसला किया है कि वे बल्कि, छोटी से छोटी कार्यक्रम यह जबकि स्टैंडर्ड की खराब लिखित भाषा के अनुरूप कर सकते हैं उत्पन्न चाहते हैं उससे अधिक कोड जो वास्तव में उपयोगी है उत्पन्न, और बदले में अलियासिंग को पहचानने में ऐसे मामलों में जहां यह स्पष्ट है बल्कि वे कि प्रोग्रामर memcpy उपयोग करते हैं, इस प्रकार संभावना के लिए अनुमति देने के लिए एक संकलक की आवश्यकता होती है की आवश्यकता होती है चाहते हैं (जबकि अभी भी माना कि चीजें हैं जो वे करेंगे उर्फ ​​की तरह नहीं है, नहीं होगा सक्षम किया जा रहा) इस बात का संकेत अज्ञात उत्पत्ति केवल कुछ भी हो सकती है, इस प्रकार लगाया जाता है आईएनजी अनुकूलन।

4

यह दस्तावेज़ स्थिति सार रखते हैं: http://dbp-consulting.com/tutorials/StrictAliasing.html

वहाँ कई अलग अलग समाधान कर रहे हैं, लेकिन सबसे पोर्टेबल/सुरक्षित एक memcpy उपयोग करने के लिए है()। इस के साथ

return *(short*)data; 

: (। फ़ंक्शन कॉल बाहर अनुकूलित किया जा सकता है, तो यह रूप में अक्षम के रूप में यह प्रतीत होता है नहीं है) उदाहरण के लिए, इस की जगह

short temp; 
memcpy(&temp, data, sizeof(temp)); 
return temp; 
+0

यह सबसे अच्छा जवाब है। – Bob

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