टाइप पनिंग

From Vigyanwiki

कंप्यूटर विज्ञान में, टाइप पनिंग वह प्रोग्रामिंग तकनीक होती है, जिसमे प्रोग्रामिंग भाषा के टाइप प्रणाली को विकृत या बाधित करती है, जो औपचारिक भाषा को सीमा के भीतर प्राप्त करना जटिल या असंभव होता है।

C और C++ में, सूचक प्रकार रूपांतरण जैसे निर्माण - C++ इस सूची मे संदर्भ प्रकार रूपांतरण और reinterpret_cast युग्मित करता है — कई प्रकार की टाइप पनिंग की अनुमति देने के लिए प्रदान किया जाता है, चूँकि कुछ प्रकार वास्तव में मानक भाषा द्वारा समर्थित नहीं होते हैं।

पास्कल प्रोग्रामिंग भाषा में, भिन्न प्रकार के रिकॉर्ड का उपयोग विशेष डेटा टाइप को एक से अधिक विधियों से या सामान्य रूप से अनुमत प्रणाली से संसाधित करने के लिए किया जा सकता है।

सॉकेट उदाहरण

टाइप पनिंग का एक उत्कृष्ट उदाहरण बर्कले सॉकेट अंतराफलक में पाया जाता है। एक विवृत, किन्तु अप्रारंभित सॉकेट को आईपी पते से बांधने का कार्य निम्नानुसार घोषित किया गया है:

int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
बाइंड फलन को सामान्यतः इस प्रकार कहा जाता है:
struct sockaddr_in sa = {0};
int sockfd = ...;
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
bind(sockfd, (struct sockaddr *)&sa, sizeof sa);

बर्कले सॉकेट लाइब्रेरी मूल रूप से इस तथ्य पर निर्भर करती है कि C प्रोग्रामिंग भाषा में, struct sockaddr_in के लिए एक पॉइंटर struct sockaddr के लिए एक पॉइंटर में स्वतंत्र रूप से परिवर्तनीय होता है;और, इसके अतिरिक्त, दोनों संरचना समान प्रकार से मेमोरी लेआउट साझा करते हैं। इसलिए, संरचना क्षेत्र my_addr->sin_family (जहाँ my_addrstruct sockaddr*प्रकार का है) का संदर्भ वास्तव में क्षेत्र sa.sin_family (जहां sa struct sockaddr_in प्रकार का है) को संदर्भित करेगा।दूसरे शब्दों में, सॉकेट लाइब्रेरी बहुरूपता या वंशानुक्रम के अल्पविकसित रूप को लागू करने के लिए टाइप पनिंग का उपयोग करती है।

प्रोग्रामिंग को अधिकांशतः देखा जाता है कि "पैडेड" डेटा संरचनाओं का उपयोग विभिन्न प्रकार के मूल्यों को प्रभावी ढंग से एक ही भंडारण स्पेस में संग्रहीत करने की अनुमति देता है। यह अधिकांशतः तब देखा जाता है जब दो संरचनाओं का उपयोग अनुकूलन के लिए पारस्परिक विशिष्टता में किया जाता है।

चल बिंदु उदाहरण

टाइप पनिंग के सभी उदाहरणों में संरचनाएं सम्मलित नहीं हैं, जैसा कि पिछले उदाहरण में था। मान लीजिए कि हम यह निर्धारित करना चाहते हैं कि चल बिन्दु संख्या ऋणात्मक है या नहीं। हम लिख सकते हैं:

bool is_negative(float x) {
    return x < 0.0;
}

चूँकि, यह मानते हुए कि चल बिन्दु आधार तुलनाएँ होती हैं,और यह भी मानते हुए कि फ्लोट को आईईईई चल बिन्दु संख्या मानक मानक के अनुसार दर्शाया गया है, और पूर्णांक 32 बिट चौड़े होते हैं, हम केवल पूर्णांक संचालन का उपयोग करके चल बिन्दु संख्या के साइन बिट को निकालने के लिए टाइप पनिंग में संलग्न हो सकते हैं:

bool is_negative(float x) {
    unsigned int *ui = (unsigned int *)&x;
    return *ui & 0x80000000;
}

ध्यान दें कि व्यवहार बिल्कुल वैसा नहीं होगा: x के ऋणात्मक शून्य होने के विशेष स्थितियों में, पहला कार्यान्वयन false परिणाम देता हैजबकि दूसरा कार्यान्वयन trueसाथ ही, पहला कार्यान्वयन वापस आ जाएगाfalseकिसी भी NaN मान के लिए,true किन्तु बाद वाला साइन बिट के साथ NaN मानों के लिए false होता है।

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

चल बिंदु पनिंग के व्यावहारिक उदाहरणों में क्वेक III द्वारा लोकप्रिय तेज़ वर्गमूल, पूर्णांक के रूप में FP तुलना [1] और एक पूर्णांक के रूप में वृद्धि करके निकटतम मूल्यों को ढूँढना (कार्यान्वयन nextafter) सम्मलित है।[2]

भाषा द्वारा

सी और सी ++

चल बिन्दु संख्याों के बिट-प्रस्तुतिकरण के बारे में धारणा के अतिरिक्त , उपरोक्त चल बिंदु टाइप-पनिंग उदाहरण भाषा की बाधाओं का भी उल्लंघन करता है कि कैसे वस्तुओं तक पहुँचा जा सकता है:[3] x टाइप float है किन्तु इसे unsigned int प्रकार की अभिव्यक्ति के माध्यम से पढ़ा जाता है। कई सामान्य प्लेटफार्मों पर, यदि अलग-अलग पॉइंटर्स को डेटा संरचना विधियों से संरेखित किया जाता है, तो पॉइंटर पनिंग का यह उपयोग समस्याएँ उत्पन्न कर सकता हैं। इसके अतिरिक्त, विभिन्न आकारों के पॉइंटर्स एक ही मेमोरी को अलियासिंग कर सकते हैं, जिससे संकलक द्वारा अनचेक की गई समस्याएं उत्पन्न हो सकती हैं। यहां तक ​​कि जब डेटा आकार और सूचक प्रतिनिधित्व मेल खाते हैं, चूँकि , संकलक अनुकूलन करने के लिए गैर-अलियासिंग बाधाओं पर अनुकूलन करने के लिए विश्‍वास कर सकते हैं जो अस्वीकृत एलियासिंग की उपस्थिति में असुरक्षित होता है।

सूचकों का प्रयोग

पॉइंटर्स का उपयोग करके टाइप-पनिंग का एक सरल प्रयास प्राप्त किया जा सकता है: (निम्न चल रहा उदाहरण टाइपfloatके लिए आईईईई-754 बिट-प्रतिनिधित्व करता है।)

bool is_negative(float x) {
   uint32_t ui = *(uint32_t*)&pi;
   return ui & 0x80000000;
}

C मानक के अलियासिंग नियम बताते हैं कि किसी वस्तु का संग्रहित मूल्य केवल एक संगत प्रकार के लवल्यू अभिव्यक्ति द्वारा ही पहुँचा जा सकता है।[4] टाइप float और uint32_t संगत नहीं होता हैं, इसलिए इस कोड का व्यवहार अपरिभाषित करता है। यद्यपि जीसीसी और एलएलवीएम पर यह विशेष कार्यक्रम अपेक्षित रूप से संकलित करता है, जटिल उदाहरण सख्त अलियासिंग द्वारा बनाई गई धारणाओं को कर सकते हैं और अवांछित व्यवहार को जन्म दे सकते हैं। विकल्प -fno-strict-aliasingटाइप-पनिंग के इस रूप का उपयोग करके कोड के सही व्यवहार को सुनिश्चित करेगा, चूँकि अन्य प्रकार के पनिंग का उपयोग करने की अनुशंसा की जाती है।[5]

यूनियनका उपयोग

C में, किन्तु C ++ में नहीं, कभी-कभी unionके माध्यम से टाइप पनिंग करना संभव होता है।

bool is_negative(float x) {
    union {
        unsigned int ui;
        float d;
    } my_union;
    my_union.d = x;
    return my_union.ui & 0x80000000;
}

हाल ही में दूसरे इकाई my_union.d को लिखने के बाद, my_union.ui, तक पहुंचनाC में टाइप-पनिंग का एक अनुमत रूप होता है,[6] बशर्ते कि पढ़ा गया इकाई उस मूल्य से बड़ा न हो जिसका मूल्य निर्धारित किया गया था (अन्यथा पढ़ने में अनिर्दिष्ट व्यवहार होता है[7]) यह वाक्यविन्यास की दृष्टि से मान्य है, किन्तु C++ में इसका व्यवहार अपरिभाषित है, [8] चूँकि, जहाँ केवल अंतिम-लिखित इकाई को ही कोई मूल्य माना जाता है।

टाइप पनिंग के एक अन्य उदाहरण के लिए, किसी सरणी का चरण देखें।

बिट_कास्ट का उपयोग

सी ++ 20 में, std::bit_cast संचालक बिना किसी अपरिभाषित व्यवहार के टाइप पनिंग की अनुमति देता है। यह फलन को कॉन्स्टेक्सपीआर लेबल करने की भी अनुमति देता है।

  static_assert(std::numeric_limits<float>::is_iec559); //(केवल आईईईई 754 पर सक्षम करें)
  auto ui = std�:: bit_cast<std�:: uint32_t> (x);
  return ui & 0x80000000;

पास्कल

एक संस्करण रिकॉर्ड डेटा टाइप को कई प्रकार के डेटा के रूप में मानने की अनुमति देता है, जो इस पर निर्भर करता है कि किस प्रकार का संदर्भ दिया जा रहा है। निम्नलिखित उदाहरण में, पूर्णांक को 16 बिट माना जाता है, जबकि लॉन्गिंट और रियल को 32 माना जाता है, जबकि कैरेक्टर को 8 बिट माना जाता है:

type
    VariantRecord = record
        case RecType : LongInt of
            1: (I : array[1..2] of Integer);  (* not show here: there can be several variables in a variant record's case statement *)
            2: (L : LongInt               );
            3: (R : Real                  );
            4: (C : array[1..4] of Char   );
        end;

var
    V  : VariantRecord;
    K  : Integer;
    LA : LongInt;
    RA : Real;
    Ch : Character;


V.I[1] := 1;
Ch     := V.C[1];  (* this would extract the first byte of V.I *)
V.R    := 8.3;   
LA     := V.L;     (* this would store a Real into an Integer *)

पास्कल में, एक वास्तविक को एक पूर्णांक में कॉपी करने से यह छोटा मूल्य में परिवर्तित हो जाता है। यह विधि चल बिन्दु संख्या के बाइनरी मान को एक लंबे पूर्णांक (32 बिट) के रूप में अनुवादित करेगी, जो समान नहीं होगा और कुछ प्रणाली पर लंबे पूर्णांक मान के साथ असंगत हो सकता है।

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

type
    PA = ^Arec;

    Arec = record
        case RT : LongInt of
            1: (P : PA     );
            2: (L : LongInt);
        end;

var
    PP : PA;
    K  : LongInt;


New(PP);
PP^.P := PP;
WriteLn('Variable PP is located at address ', Hex(PP^.L));

जहाँ पास्कल में एक सूचक के लिए स्मृति आवंटित करने के लिए मानक दिनचर्या है, और हेक्स संभवतः एक पूर्णांक के मान का वर्णन करने वाले हेक्साडेसिमल स्ट्रिंग को प्रिंट करने के लिए एक नियमित है। यह एक सूचक के पते को प्रदर्शित करने की अनुमति देगा, जिसे सामान्य रूप से अनुमति नहीं है। (बिन्दुओ को पढ़ा या लिखा नहीं जा सकता है, केवल असाइन किया जा सकता है।) सूचक के एक पूर्णांक संस्करण के लिए मान निर्दिष्ट करने से प्रणाली मेमोरी में किसी भी स्थान पर जांच या लिखने की अनुमति होती है :

PP^.L := 0;
PP    := PP^.P;  (* PP now points to address 0     *)
K     := PP^.L;  (* K contains the value of word 0 *)
WriteLn('Word 0 of this machine contains ', K);

यह निर्माण प्रोग्राम की जाँच या सुरक्षा उल्लंघन का कारण बन सकता है यदि 0 उस मशीन पर पढ़ने से सुरक्षित है जिस पर प्रोग्राम चल रहा है या संचालन प्रणाली के तहत चल रहा है।

C/C++ से पुनर्व्याख्या कास्ट तकनीक पास्कल में भी काम करती है। यह उपयोगी हो सकता है, जब उदा बाइट स्ट्रीम से डवर्ड्स पढ़ना, और हम उन्हें फ्लोट के रूप में देखना चाहते हैं। यहां एक कार्यशील उदाहरण दिया गया है, जहां हम एक फ्लोट में एक शब्द को दोबारा परिभाषित करते हैं:

type
    pReal = ^Real;

var
    DW : DWord;
    F  : Real;

F := pReal(@DW)^;

C#

C# (और अन्य .NET भाषाओं) में, टाइप प्रणाली के कारण टाइप पनिंग प्राप्त करना थोड़ा कठिन है, किन्तु फिर भी सूचक या संरचना यूनियनों का उपयोग करके इसे किया जा सकता है।

संकेतक

C# केवल तथाकथित मूल प्रकारों के लिए पॉइंटर्स की अनुमति देता है, अर्थात किसी भी अभाज्य प्रकार (को छोड़कर string), एनम, एरे या संरचना जो केवल अन्य मूल प्रकारों से बना होता है। ध्यान दें कि पॉइंटर्स की अनुमति केवल 'असुरक्षित' चिह्नित कोड ब्लॉक में ही होती है।

float pi = 3.14159;
uint piAsRawData = *(uint*)&pi;

यूनियन संरचना

यूनियन संरचना को 'असुरक्षित' कोड की किसी भी धारणा के बिना अनुमति दी जाती है, किन्तु उन्हें एक नए प्रकार की परिभाषा की आवश्यकता होती है।

[StructLayout(LayoutKind.Explicit)]
struct FloatAndUIntUnion
{
    [FieldOffset(0)]
    public float DataAsFloat;

    [FieldOffset(0)]
    public uint DataAsUInt;
}

// ...

FloatAndUIntUnion union;
union.DataAsFloat = 3.14159;
uint piAsRawData = union.DataAsUInt;

रॉ सीआईएल कोड

C# के स्थान पर रॉ सामान्य मध्यवर्ती भाषा का उपयोग किया जा सकता है, क्योंकि इसमें अधिकांश प्रकार की सीमाएँ नहीं हैं। उदाहरण के लिए, यह किसी को सामान्य प्रकार के दो एनम मानों को संयोजित करने की अनुमति देता है:

TEnum a = ...;
TEnum b = ...;
TEnum combined = a | b; // illegal

इसे निम्नलिखित सीआईएल कोड द्वारा गतिरोध उत्पन्न किया जा सकता है:

.method public static hidebysig
    !!TEnum CombineEnums<valuetype .ctor ([mscorlib]System.ValueType) TEnum>(
        !!TEnum a,
        !!TEnum b
    ) cil managed
{
    .maxstack 2

    ldarg.0 
    ldarg.1
    or  // this will not cause an overflow, because a and b have the same type, and therefore the same size.
    ret
}
cpblk e> CIL ऑपकोड कुछ अन्य युक्तियों की अनुमति देता है, जैसे किसी संरचना को बाइट सरणी में बदलना होता है:
.method public static hidebysig
    uint8[] ToByteArray<valuetype .ctor ([mscorlib]System.ValueType) T>(
        !!T& v // 'ref T' in C#
    ) cil managed
{
    .locals init (
        [0] uint8[]
    )

    .maxstack 3

    // create a new byte array with length sizeof(T) and store it in local 0
    sizeof !!T
    newarr uint8
    dup           // keep a copy on the stack for later (1)
    stloc.0

    ldc.i4.0
    ldelema uint8

    // memcpy(local 0, &v, sizeof(T));
    // <the array is still on the stack, see (1)>
    ldarg.0 // this is the *address* of 'v', because its type is '!!T&'
    sizeof !!T
    cpblk

    ldloc.0
    ret
}

संदर्भ

  1. Herf, Michael (December 2001). "मूलांक टोटके". stereopsis : graphics.
  2. "बेवकूफ फ्लोट ट्रिक्स". Random ASCII - tech blog of Bruce Dawson (in English). 24 January 2012.
  3. ISO/IEC 9899 :1999 s6.5/7
  4. "§ 6.5/7" (PDF), ISO/IEC 9899:2018 (in English), 2018, p. 55, archived from the original (PDF) on 2018-12-30, An object shall have its stored value accessed only by an lvalue expression that has one of the following types: [...]
  5. "जीसीसी बग - जीएनयू प्रोजेक्ट". gcc.gnu.org.
  6. "§ 6.5.2.3/3, footnote 97" (PDF), ISO/IEC 9899:2018 (in English), 2018, p. 59, archived from the original (PDF) on 2018-12-30, If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.
  7. "§ J.1/1, bullet 11" (PDF), ISO/IEC 9899:2018 (in English), 2018, p. 403, archived from the original (PDF) on 2018-12-30, The following are unspecified: … The values of bytes that correspond to union members other than the one last stored into (6.2.6.1).
  8. ISO/IEC 14882:2011 Section 9.5


बाहरी संबंध

  • Section of the GCC manual on -fstrict-aliasing, which defeats some type punning
  • Defect Report 257 to the C99 standard, incidentally defining "type punning" in terms of union, and discussing the issues surrounding the implementation-defined behavior of the last example above
  • Defect Report 283 on the use of unions for type punning