c - यह दावा किया गया है कि डेरीफ्रेंसिंग टाइप-पाइंटेड पॉइंटर चेतावनी कंपाइलर-विशिष्ट क्यों है?




pointers casting (4)

अन्य उत्तरों ने जो कहा है उसके शीर्ष पर, यह C में एक क्लासिक प्रतिमान है, और जिसे आग से जलाया जाना चाहिए। यह इसमें दिखाई देता है:

  1. फ्री-एंड-नल-आउट फ़ंक्शंस जैसे कि आपको चेतावनी मिली है।
  2. आबंटन फ़ंक्शन जो मानक C मुहावरे को वापस करने का void * (जो इस समस्या से ग्रस्त नहीं है क्योंकि इसमें टाइपिंग पाइंट के बजाय एक मान रूपांतरण शामिल है), बजाय एक त्रुटि ध्वज को लौटाए और एक पॉइंटर-टू-पॉइंटर के माध्यम से परिणाम को संग्रहीत करना।

(1) के एक अन्य उदाहरण के लिए, ffmpeg / libavcodec के av_free फ़ंक्शन में एक लंबे समय से कुख्यात मामला था। मेरा मानना ​​है कि यह अंततः एक मैक्रो या किसी अन्य चाल के साथ तय किया गया था, लेकिन मुझे यकीन नहीं है।

(2) के लिए, दोनों cudaMalloc और posix_memalign उदाहरण हैं।

न तो मामले में इंटरफ़ेस को स्वाभाविक रूप से अमान्य उपयोग की आवश्यकता होती है , लेकिन यह दृढ़ता से इसे प्रोत्साहित करता है, और केवल प्रकार के void * अतिरिक्त अस्थायी ऑब्जेक्ट के साथ सही उपयोग को स्वीकार करता है void * जो फ्री-एंड-नल-आउट कार्यक्षमता के उद्देश्य को पराजित करता है, और आवंटन को अजीब बनाता है ।

मैंने स्टैक ओवरफ्लो आरई on various posts पढ़ी हैं: डेरेफेरिंग टाइप-पेंडिंग पॉइंटर एरर। मेरी समझ यह है कि त्रुटि अनिवार्य रूप से एक अलग प्रकार के पॉइंटर के माध्यम से किसी वस्तु तक पहुंचने के खतरे की संकलक चेतावनी है (हालांकि char* लिए एक अपवाद प्रतीत होता है), जो एक समझ और उचित चेतावनी है।

मेरा प्रश्न नीचे दिए गए कोड के लिए विशिष्ट है: एक पॉइंटर के पते को void** क्यों -Werror void** इस चेतावनी के लिए अर्हता प्राप्त करता है ( -Werror माध्यम से त्रुटि को बढ़ावा दिया)?

इसके अलावा, इस कोड को कई लक्ष्य आर्किटेक्चर के लिए संकलित किया गया है, जिसमें से केवल एक ही चेतावनी / त्रुटि उत्पन्न करता है - क्या इसका मतलब यह हो सकता है कि यह वैध रूप से संकलक संस्करण-विशिष्ट कमी है?

// main.c
#include <stdlib.h>

typedef struct Foo
{
  int i;
} Foo;

void freeFunc( void** obj )
{
  if ( obj && * obj )
  {
    free( *obj );
    *obj = NULL;
  }
}

int main( int argc, char* argv[] )
{
  Foo* f = calloc( 1, sizeof( Foo ) );
  freeFunc( (void**)(&f) );

  return 0;
}

यदि मेरी समझ, ऊपर कहा गया है, तो सही है, एक void** , अभी भी सिर्फ एक संकेतक होने के नाते, यह सुरक्षित कास्टिंग होना चाहिए।

क्या इस संकलक-विशिष्ट चेतावनी / त्रुटि को शांत करने वाले अंतराल का उपयोग नहीं करने के लिए एक समाधान है? यानी मैं समझता हूं कि यह क्यों और इस मुद्दे को हल करेगा, लेकिन मैं इस दृष्टिकोण से बचना चाहूंगा क्योंकि मैं freeFunc() लाभ उठाना चाहता freeFunc() NULL आईएनजी एक इच्छित आउट-अर्ग:

void* tmp = f;
freeFunc( &tmp );
f = NULL;

समस्या संकलक (एक में से एक):

[email protected]:/build$ /usr/local/crosstool/x86-fc3/bin/i686-fc3-linux-gnu-gcc --version && /usr/local/crosstool/x86-fc3/bin/i686-fc3-linux-gnu-gcc -Wall -O2 -Werror ./main.c
i686-fc3-linux-gnu-gcc (GCC) 3.4.5
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

./main.c: In function `main':
./main.c:21: warning: dereferencing type-punned pointer will break strict-aliasing rules

[email protected]:/build$

शिकायतकर्ता संकलक (कई में से एक):

[email protected]:/build$ /usr/local/crosstool/x86-rh73/bin/i686-rh73-linux-gnu-gcc --version && /usr/local/crosstool/x86-rh73/bin/i686-rh73-linux-gnu-gcc -Wall -O2 -Werror ./main.c
i686-rh73-linux-gnu-gcc (GCC) 3.2.3
Copyright (C) 2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[email protected]:/build$

अद्यतन: मैंने आगे पाया है कि चेतावनी विशेष रूप से तब उत्पन्न होती है जब -O2 साथ संकलित किया जाता है (फिर भी केवल "समस्या संकलक" के साथ)


एक void * को विशेष रूप से सी मानक द्वारा भाग में माना जाता है क्योंकि यह एक अपूर्ण प्रकार का संदर्भ देता है। यह उपचार void ** तक विस्तारित नहीं होता है क्योंकि यह पूर्ण प्रकार को इंगित करता है , विशेष रूप से void *

सख्त अलियासिंग नियम कहते हैं कि आप एक प्रकार के पॉइंटर को दूसरे प्रकार के पॉइंटर में नहीं बदल सकते हैं और बाद में उस पॉइंटर को डाइरेक्ट करते हैं क्योंकि ऐसा करने का अर्थ है एक प्रकार के बाइट्स को दूसरे के रूप में फिर से समझना। एकमात्र अपवाद तब होता है जब एक चरित्र प्रकार में परिवर्तित किया जाता है जो आपको किसी वस्तु के प्रतिनिधित्व को पढ़ने की अनुमति देता है।

आप किसी फ़ंक्शन के बजाय फ़ंक्शन जैसी मैक्रो का उपयोग करके इस सीमा के आसपास प्राप्त कर सकते हैं:

#define freeFunc(obj) (free(obj), (obj) = NULL)

जिसे आप इस तरह से कॉल कर सकते हैं:

freeFunc(f);

हालाँकि, इसकी एक सीमा है, क्योंकि उपरोक्त मैक्रो दो बार obj मूल्यांकन करेगा। यदि आप GCC का उपयोग कर रहे हैं, तो इसे कुछ एक्सटेंशन, विशेषकर typeof कीवर्ड और स्टेटमेंट एक्सप्रेशन के साथ टाला जा सकता है:

#define freeFunc(obj) ({ typeof (&(obj)) ptr = &(obj); free(*ptr); *ptr = NULL; })

प्रकार void** का मान void** प्रकार के void** ऑब्जेक्ट के लिए एक संकेतक है void*Foo* प्रकार की वस्तु void* प्रकार की वस्तु नहीं है।

Foo* और void* प्रकार के मूल्यों के बीच एक अंतर्निहित रूपांतरण है। यह रूपांतरण मूल्य के प्रतिनिधित्व को बदल सकता है। इसी तरह, आप int n = 3; double x = n; लिख सकते हैं int n = 3; double x = n; int n = 3; double x = n; और इसमें x को मान 3.0 पर सेट करने का अच्छी तरह से परिभाषित व्यवहार है, लेकिन double *p = (double*)&n; अपरिभाषित व्यवहार किया है (और व्यवहार में किसी भी सामान्य वास्तुकला पर "सूचक 3.0 " करने के लिए p सेट नहीं होगा)।

आर्किटेक्चर जहां विभिन्न प्रकार के पॉइंटर्स टू ऑब्जेक्ट्स के अलग-अलग प्रतिनिधित्व होते हैं वे आजकल दुर्लभ हैं, लेकिन उन्हें सी मानक द्वारा अनुमति दी जाती है। शब्द संकेत के साथ (दुर्लभ) पुरानी मशीनें हैं जो स्मृति और बाइट पॉइंटर्स में एक शब्द के पते हैं जो इस शब्द में बाइट ऑफसेट के साथ एक शब्द के पते हैं; Foo* एक वर्ड पॉइंटर होगा और void* ऐसे आर्किटेक्चर पर बाइट पॉइंटर होगा। वसा बिंदुओं के साथ (दुर्लभ) मशीनें हैं, जिसमें न केवल वस्तु के पते के बारे में जानकारी होती है, बल्कि इसके प्रकार, इसके आकार और इसकी पहुंच नियंत्रण सूचियों के बारे में भी जानकारी होती है; एक निश्चित प्रकार के लिए एक पॉइंटर से एक अलग प्रतिनिधित्व हो सकता है void* जिसे रनटाइम पर अतिरिक्त प्रकार की जानकारी की आवश्यकता होती है।

ऐसी मशीनें दुर्लभ हैं, लेकिन सी मानक द्वारा अनुमत हैं। और कुछ सी कंपाइलर कोड को ऑप्टिमाइज़ करने के लिए टाइप-पाइंट पॉइंटर्स को अलग-अलग मानने की अनुमति का लाभ उठाते हैं। कोड का अनुकूलन करने के लिए एक संकलक की क्षमता के लिए पॉइंटर्स अलियासिंग का जोखिम एक प्रमुख सीमा है, इसलिए संकलक ऐसी अनुमतियों का लाभ उठाते हैं।

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

आप एक मैक्रो freefunc कर सकते हैं:

#define FREE_SINGLE_REFERENCE(p) (free(p), (p) = NULL)

यह मैक्रोज़ की सामान्य सीमाओं के साथ आता है: प्रकार की सुरक्षा की कमी, p का दो बार मूल्यांकन किया जाता है। ध्यान दें कि यह आपको केवल चारों ओर झूलने वाले बिंदुओं को न छोड़ने की सुरक्षा देता है यदि p मुक्त वस्तु का एकल सूचक था।


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

मानक के लिखे जाने से पहले, सभी पॉइंटर प्रकारों के लिए समान प्रतिनिधित्व का उपयोग करने वाले प्लेटफार्मों के लिए कार्यान्वयन सर्वसम्मति से एक void** का उपयोग करने की अनुमति देगा, कम से कम उपयुक्त कास्टिंग के साथ, "किसी भी पॉइंटर के लिए पॉइंटर" के रूप में। मानक के लेखकों ने लगभग निश्चित रूप से मान्यता दी कि यह उन प्लेटफार्मों पर उपयोगी होगा जो इसका समर्थन करते थे, लेकिन चूंकि यह सार्वभौमिक रूप से समर्थित नहीं हो सका, इसलिए उन्होंने इसे अनिवार्य करने से इनकार कर दिया। इसके बजाय, उन्हें उम्मीद थी कि गुणवत्ता कार्यान्वयन ऐसे निर्माणों को संसाधित करेगा जो कि राशनले एक "लोकप्रिय विस्तार" के रूप में वर्णित करेंगे, ऐसे मामलों में जहां ऐसा करने का कोई मतलब होगा।





casting