2014-11-07 6 views
12

निम्न पंक्ति (शुद्ध ग) खिड़कियों पर सफाई से संकलित (Win7 64 बिट + 13 + mingw32 codeblocks) और डेबियन (खरखरा 32 बिट + codeblocks 10 + जीसीसी), लेकिन काली (64 बिट्स पर चेतावनी को जन्म देती है + कोडब्लॉक्स + जीसीसी)। कोई टिप्पणी? मेरा मतलब है, मुझे यह चेतावनी क्यों मिलती है, हालांकि एक ही पंक्ति विंडोज़ & डेबियन पर किसी भी चेतावनी को संकलित करती है?मुझे "विभिन्न आकार के पूर्णांक में पॉइंटर से कास्ट" त्रुटि क्यों मिलती है?

void* foo(void *dst, ...) { 
    // some code 
    unsigned int blkLen = sizeof(int); // this line ok. 
    unsigned int offset = (unsigned int) dst % blkLen; // warning here! 
    // some code cont... 
} 

codeblocks में संदेश है: "त्रुटि: विभिन्न आकार [-Werror = सूचक करने वाली पूर्णांक-कास्ट] के पूर्णांक के लिए सूचक से डाली"

ध्यान दें: मेरी संकलक विकल्प -std=c99 -Werror -save-temps हैं (सभी तीन प्रणालियों पर समान)।

संपादित 2: हालांकि मैं यह संकलित किया है करने के लिए w/ओ नीचे पूर्वप्रक्रमक लाइनों का उपयोग कर चेतावनी, @Keith थॉम्पसन (नीचे देखें) मुद्दे के बारे में एक महत्वपूर्ण बिंदु है प्रबंधित किया है। तो, मेरा अंतिम निर्णय uintptr_t का उपयोग करना बेहतर विकल्प होगा।

संपादित करें 1: सभी के लिए धन्यवाद उत्तर दिया। जैसा कि सभी उत्तरों नोट करते हैं, समस्या 64 बिट्स बनाम 64 बिट्स समस्या है। मैं निम्नलिखित पूर्वप्रक्रमक लाइनों डाला है:

#if __linux__ // or #if __GNUC__ 
    #if __x86_64__ || __ppc64__ 
     #define ENVIRONMENT64 
    #else 
     #define ENVIRONMENT32 
    #endif 
#else 
    #if _WIN32 
     #define ENVIRONMENT32 
    #else 
     #define ENVIRONMENT64 
    #endif 
#endif // __linux__ 

#ifdef ENVIRONMENT64 
    #define MAX_BLOCK_SIZE unsigned long long int 
#else 
    #define MAX_BLOCK_SIZE unsigned long int 
#endif // ENVIRONMENT64 

और उसके बाद के रूप में समस्या लाइन की जगह:

unsigned int offset = (MAX_BLOCK_SIZE) dst % blkLen; 

अब, सब कुछ ठीक लगता है।

+0

कुछ सिस्टम/कंपाइलर्स पर, एक int का आकार सूचक के आकार से अलग होता है। हालांकि, एक ठेठ प्रणाली के लिए, ऑफ़सेट का मूल्य 0 ... 3 होगा। तो मॉड्यूलो के हस्ताक्षर किए गए int के परिणाम का थोड़ा सा कास्टिंग समस्या को ठीक करेगा। – user3629249

उत्तर

17

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

एक साफ विकल्प है कि चेतावनी, और गलत परिणाम का एक और बहुत nastier मुद्दे से बचा जाता है जब परिवर्तित मूल्य नकारात्मक है, यह है:

unsigned int offset = (uintptr_t) dst % blkLen; 

आप stdint.h या inttypes.h शामिल करने के लिए uintptr_t उपलब्ध की आवश्यकता होगी।

+0

एक ही समस्या: http://stackoverflow.com/a/21323738/1959808 –

2

शायद 64 बिट्स आर्किटेक्चर पर एक सूचक 64 बिट लंबा है और केवल 32 बिट्स में एक int है?

आप

void* foo(void *dst, ...) { 
    // some code 
    unsigned int blkLen = sizeof(int); // this line ok. 
    uintptr_t offset = (uintptr_t) dst % blkLen; // warning here! 
    // some code cont... 
} 
1

प्रयास करना चाहिए मैं तुम्हें चेतावनी मिलती है क्योंकि पूर्णांक के आकार implemetations जैसे पूर्णांक पर निर्भर करता है 2 बाइट लंबा या 4 बाइट लंबा हो सकता है लगता है। यह चेतावनी का कारण हो सकता है (अगर मैं गलत हूं तो कृपया मुझे सही करें)। लेकिन वैसे भी आप एक सूचक पर एक मॉड्यूल करने की कोशिश क्यों कर रहे हैं।

+1

सापेक्ष एक सूचक पर संरेखण की जाँच करने के समझ में आता है। –

+0

कई ** पर लेकिन सभी ** सिस्टम नहीं। कीथ के जवाब में क्रे उदाहरण देखें जहां यह काम नहीं करेगा। –

3

क्योंकि void * को unsigned int पर कास्टिंग करना ठीक है, यह चेतावनी है कि यह चेतावनी पकड़ने का इरादा है क्योंकि यह असुरक्षित है। सूचक 64-बिट हो सकता है और int 32-बिट हो सकता है। किसी दिए गए प्लेटफ़ॉर्म के लिए, sizeof(unsigned int)sizeof(void *) होने की गारंटी नहीं है।आपको इसके बजाय uintptr_t का उपयोग करना चाहिए।

9

समस्या यह है कि void* पॉइंटर को unsigned int में परिवर्तित करना स्वाभाविक रूप से गैर-पोर्टेबल है।

आकार में संभावित अंतर समस्या का केवल एक हिस्सा है। uintptr_t का उपयोग कर समस्या का वह हिस्सा हल किया जा सकता है, <stdint.h> और <inttypes.h> में परिभाषित एक प्रकार। uintptr_t को void* से uintptr_t में परिवर्तित करने की गारंटी दी गई है और फिर से मूल सूचक मूल्य (या कम से कम एक सूचक मान जो मूल के बराबर तुलना करता है) उत्पन्न करेगा। एक प्रकार intptr_t भी है, जिस पर हस्ताक्षर किया गया है; आमतौर पर हस्ताक्षरित प्रकार इस तरह की चीज़ के लिए अधिक समझ में आता है। uintptr_t और intptr_t मौजूद होने की गारंटी नहीं है, लेकिन वे किसी भी (सी 99 या बाद में) कार्यान्वयन पर मौजूद होना चाहिए जिसमें उपयुक्त पूर्णांक प्रकार हों।

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

सी मानक कहते हैं, एक गैर मानक फुटनोट में, कि:

The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment.

जो उपयोगी नहीं है जब तक कि आप को पता है कि कि को संबोधित संरचना है होता है।

आप यह निर्धारित करने की कोशिश कर रहे हैं कि void* तर्क का ऑफसेट blkLen के अगले निचले एकाधिक से संबंधित है; दूसरे शब्दों में, आप यह निर्धारित करने की कोशिश कर रहे हैं कि सूचकांक को blkLen-स्मृति के आकार वाले ब्लॉक के संबंध में कैसे गिनती है।

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

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

निष्कर्ष:

  1. आप निश्चित रूप से नहीं बल्कि यह निर्धारित करने के पूर्णांक प्रकार आप उपयोग कर सकते पूर्वप्रक्रमक चाल खेलने की तुलना में uintptr_t उपयोग करना चाहिए। आपके कंपाइलर के कार्यान्वयनकर्ता ने पहले से ही एक पूर्णांक प्रकार निर्धारित करने का काम किया है जो सुरक्षित रूप से परिवर्तित सूचक मूल्य को सुरक्षित रख सकता है। उस विशेष पहिया को फिर से शुरू करने की कोई आवश्यकता नहीं है। (चेतावनी: <stdint.h> को 1 999 के आईएसओ मानक द्वारा सी में जोड़ा गया था। यदि आप एक प्राचीन कंपाइलर का उपयोग कर फंस गए हैं जो इसे लागू नहीं करता है, तो आपको अभी भी #ifdef हैक का उपयोग करने की आवश्यकता हो सकती है। लेकिन मैं अभी भी uintptr_t का उपयोग करने का सुझाव दूंगा यदि यह उपलब्ध है। आप C99 अनुरूपता के परीक्षण के लिए __STDC_VERSION__ >= 199901L का परीक्षण कर सकते हैं - हालांकि कुछ कंपाइलर C99 का समर्थन किए बिना <stdint.h> का समर्थन कर सकते हैं।)

  2. आपको यह पता होना चाहिए कि एक सूचक को एक पूर्णांक में परिवर्तित करना और उसके मूल्य के साथ खेलना गैर-पोर्टेबल है। यह कहना नहीं है कि आपको यह नहीं करना चाहिए; सी की सबसे बड़ी ताकत में से एक है गैर-पोर्टेबल कोड का समर्थन करने की इसकी क्षमता है जब आपको इसकी आवश्यकता होती है।

+0

यह इस मुद्दे से संबंधित ** सबसे मूल्यवान टिप्पणी ** हो सकता है। बहुत बहुत धन्यवाद। मैं खाते में, अपनी बात और पुनः संपादित प्रश्न लेने के सभी अनुयायियों यह का उपयोग करने के लिए की जाती है। – ssd

0

आपने मैक्रो बना दिया है लेकिन आपको लगता है कि यह अभी भी गलत नहीं है। चूंकि आपका पॉइंटर बिना हस्ताक्षरित लंबे लंबे int या unsigned long int में परिवर्तित हो जाएगा जो 86x और 64x ओएस में 32 बिट और 64 बिट होगा लेकिन आपका वैरिएबल ऑफ़सेट अप्रमाणित int है जो 64x और 86x ओएस में 32 बिट है। तो मुझे लगता है कि आपको ऑफ़सेट को संबंधित मैक्रो में भी परिवर्तित करना चाहिए।

या बस आप (लंबे समय तक अर्थात अहस्ताक्षरित int) लंबे समय के लिए सूचक परिवर्तित कर सकते हैं और (लंबी यानी अहस्ताक्षरित int) लंबे समय के लिए ऑफसेट भी।

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