c++ - एक बूल में अतिरिक्त बिट्स सेट करना एक ही समय में सही और गलत बनाता है




boolean undefined-behavior (2)

अगर मुझे bool वेरिएबल मिलता है और उसका दूसरा बिट 1 सेट करता है, तो वेरिएबल एक ही समय में सही और गलत का मूल्यांकन करता है। निम्नलिखित कोड को gcc6.3 के साथ -g विकल्प, ( gcc-v6.3.0/Linux/RHEL6.0-2016-x86_64/bin/g++ -g main.cpp -o mytest_d ) के साथ gcc-v6.3.0/Linux/RHEL6.0-2016-x86_64/bin/g++ -g main.cpp -o mytest_d और निष्पादन योग्य चलाएं। आप निम्नलिखित प्राप्त करें।

T एक ही समय में सच्चे और झूठे के बराबर कैसे हो सकता है?

       value   bits 
       -----   ---- 
    T:   1     0001
after bit change
    T:   3     0011
T is true
T is false

यह तब हो सकता है जब आप किसी अन्य भाषा में एक फ़ंक्शन कहते हैं (फोरट्रान कहते हैं) जहां सही और गलत परिभाषा सी ++ से भिन्न होती है। फोरट्रान के लिए यदि कोई बिट 0 नहीं है तो मान सत्य है, यदि सभी बिट शून्य हैं तो मान गलत है।

#include <iostream>
#include <bitset>

using namespace std;

void set_bits_to_1(void* val){
  char *x = static_cast<char *>(val);

  for (int i = 0; i<2; i++ ){
    *x |= (1UL << i);
  }
}

int main(int argc,char *argv[])
{

  bool T = 3;

  cout <<"       value   bits " <<endl;
  cout <<"       -----   ---- " <<endl;
  cout <<"    T:   "<< T <<"     "<< bitset<4>(T)<<endl;

  set_bits_to_1(&T);


  bitset<4> bit_T = bitset<4>(T);
  cout <<"after bit change"<<endl;
  cout <<"    T:   "<< T <<"     "<< bit_T<<endl;

  if (T ){
    cout <<"T is true" <<endl;
  }

  if ( T == false){
    cout <<"T is false" <<endl;
  }


}

///////////////////////////////////// // फोरट्रान फ़ंक्शन जो सी ++ के साथ संगत नहीं है जब इफोर्ट के साथ संकलित किया जाता है।

       logical*1 function return_true()
         implicit none

         return_true = 1;

       end function return_true

यह तब होता है जब आप भाषा और संकलक दोनों के साथ अपने अनुबंध का उल्लंघन करते हैं।

आपने शायद कहीं सुना है कि "शून्य गलत है", और "शून्य शून्य सत्य है"। यह तब होता है जब आप भाषा के मापदंडों से चिपके रहते हैं, सांख्यिकीय रूप से एक int को bool या इसके विपरीत में परिवर्तित करते हैं।

जब आप बिट अभ्यावेदन के साथ गड़बड़ करना शुरू करते हैं तो यह पकड़ में नहीं आता। उस स्थिति में, आप अपना अनुबंध तोड़ते हैं, और (बहुत कम से कम) कार्यान्वयन-परिभाषित व्यवहार के दायरे में प्रवेश करते हैं।

बस ऐसा मत करो।

यह आपके ऊपर नहीं है कि मेमोरी में bool कैसे संग्रहीत किया जाता है। यह संकलक तक है। यदि आप एक bool का मान बदलना चाहते हैं, तो या तो true / false , या एक पूर्णांक असाइन करें और C ++ द्वारा प्रदान किए गए उचित रूपांतरण तंत्र का उपयोग करें।

C ++ मानक वास्तव में एक विशिष्ट कॉल-आउट देता था कि इस तरीके से bool का उपयोग करना कैसे शरारती और बुरा है और बुराई है ( "इस दस्तावेज़ द्वारा वर्णित तरीकों में एक bool मान का उपयोग 'अपरिभाषित' के रूप में किया गया है, जैसे कि मान का परीक्षण करके एकांतहीन स्वचालित वस्तु, इसका व्यवहार करने का कारण हो सकता है जैसे कि यह न तो true और न ही false । " ), हालांकि इसे संपादकीय कारणों से C ++ 20 में हटा दिया गया था।


सी ++ में एक bool का बिट प्रतिनिधित्व (और यहां तक ​​कि आकार) कार्यान्वयन को परिभाषित किया गया है; आम तौर पर इसे char आकार वाले प्रकार के रूप में लागू किया जाता है जो संभव मानों के रूप में 1 या 0 लेता है।

यदि आप किसी के लिए अनुमतियों से अलग इस मूल्य को निर्धारित करते हैं (इस विशिष्ट मामले में char माध्यम से एक bool को char और इसके बिट प्रतिनिधित्व को संशोधित करके), तो आप भाषा के नियमों को तोड़ रहे हैं, इसलिए कुछ भी हो सकता है। विशेष रूप से, यह स्पष्ट रूप से मानक में निर्दिष्ट है कि एक "टूटी हुई" bool एक ही समय में true और false (या न तो true और न ही false ) दोनों के रूप में व्यवहार कर सकती है:

इस अंतर्राष्ट्रीय मानक द्वारा वर्णित तरीकों के रूप में "अपरिभाषित" के रूप में एक bool मान का उपयोग करना, जैसे कि एक असिंचित स्वचालित वस्तु के मूल्य की जांच करके, यह व्यवहार करने का कारण हो सकता है जैसे कि यह न तो true और न ही false

(सी ++ 11, [बेसिक.फंडमेंटल], नोट 47)

इस विशेष मामले में, आप देख सकते हैं कि इस विचित्र स्थिति में यह कैसे समाप्त हुआ : पहला if संकलित हो जाता है

    movzx   eax, BYTE PTR [rbp-33]
    test    al, al
    je      .L22

जो ई में T (शून्य विस्तार के साथ) लोड करता है, और प्रिंट को छोड़ देता है यदि यह सब शून्य है; अगले के बजाय अगर है

    movzx   eax, BYTE PTR [rbp-33]
    xor     eax, 1
    test    al, al
    je      .L23

परीक्षण if(T == false) में परिवर्तित हो जाता है if(T^1) , जो कि कम बिट पर ही फ़्लिप करता है। यह एक वैध bool लिए ठीक होगा, लेकिन आपके "टूटे हुए" के लिए यह इसे काट नहीं करता है।

ध्यान दें कि यह विचित्र अनुक्रम केवल कम अनुकूलन स्तरों पर उत्पन्न होता है; उच्च स्तर पर यह आम तौर पर एक शून्य / नॉनजेरो चेक को उबालने वाला है, और आपके जैसा एक क्रम एकल परीक्षण / सशर्त शाखा बनने की संभावना है। आपको अन्य संदर्भों में विचित्र व्यवहार मिलेगा, उदाहरण के लिए जब अन्य पूर्णांकों में bool मानों का योग करें:

int foo(bool b, int i) {
    return i + b;
}

becomes

foo(bool, int):
        movzx   edi, dil
        lea     eax, [rdi+rsi]
        ret

जहां dil "0" पर "विश्वसनीय" है।

यदि आपका प्रोग्राम सभी C ++ है, तो समाधान सरल है: इस तरह से bool मानों को न तोड़ें, उनके बिट प्रतिनिधित्व के साथ खिलवाड़ करने से बचें और सब कुछ ठीक हो जाएगा; विशेष रूप से, भले ही आप एक पूर्णांक से एक bool तक असाइन करते हैं संकलक आवश्यक कोड का उत्सर्जन करेगा यह सुनिश्चित करने के लिए कि परिणामस्वरूप मूल्य एक मान्य bool , इसलिए आपका bool T = 3 वास्तव में सुरक्षित है, और T एक true साथ समाप्त हो जाएगा इसकी हिम्मत में।

यदि इसके बजाय आपको अन्य भाषाओं में लिखे गए कोड के साथ हस्तक्षेप करने की आवश्यकता है जो एक ही विचार को साझा नहीं कर सकता है कि एक bool क्या है, बस "सीमा" कोड के लिए bool से बचें, और इसे एक उचित आकार के पूर्णांक के रूप में मार्शल करें। यह सशर्त और सह में काम करेगा। बस ठीक है।

समस्या के फोरट्रान / इंटरऑपरेबिलिटी पक्ष के बारे में अपडेट करें

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

सबसे पहले, इस तरह की भाषा अंतर-सामानता भाषा मानकों का हिस्सा नहीं है, लेकिन मंच एबीआई की है। जैसा कि हम लिनक्स x86-64 के बारे में बात कर रहे हैं, प्रासंगिक दस्तावेज सिस्टम वी x86-64 एबीआई है

सबसे पहले, कहीं भी निर्दिष्ट नहीं किया गया है कि C _Bool प्रकार (जिसे 3.1.2 नोट पर C ++ bool के समान माना जाता है) में फोरट्रान LOGICAL साथ किसी भी प्रकार की संगतता है; विशेष रूप से, 9.2.2 टेबल 9.2 पर निर्दिष्ट है कि "सादे" LOGICAL को signed int में मैप किया गया है। TYPE*N प्रकार के बारे में यह कहता है कि

" TYPE*N " संकेतन निर्दिष्ट करता है कि टाइप या टाइप TYPE कुल सदस्य भंडारण के N बाइट्स पर कब्जा कर लेंगे।

(Ibid।)

LOGICAL*1 लिए कोई समान प्रकार स्पष्ट रूप से निर्दिष्ट नहीं है, और यह समझ में आता है: यह मानक भी नहीं है; यदि आप फोरट्रान 95 कंप्लेंट मोड में एक LOGICAL*1 युक्त फोरट्रान प्रोग्राम को संकलित करने का प्रयास करते हैं, तो आपको इफोर्ट द्वारा दोनों के बारे में चेतावनी मिलती है।

./example.f90(2): warning #6916: Fortran 95 does not allow this length specification.   [1]

    logical*1, intent(in) :: x

------------^

और gfort द्वारा

./example.f90:2:13:
     logical*1, intent(in) :: x
             1
Error: GNU Extension: Nonstandard type declaration LOGICAL*1 at (1)

इसलिए पानी पहले से ही पिघला हुआ है; इसलिए, ऊपर दिए गए दो नियमों को मिलाकर, मैं सुरक्षित होने के लिए signed char लिए जाऊंगा।

हालाँकि : ABI भी निर्दिष्ट करता है:

प्रकार LOGICAL लिए मान .TRUE. 1 और .FALSE. रूप में लागू किया गया .FALSE. 0 के रूप में लागू किया गया।

इसलिए, यदि आपके पास एक प्रोग्राम है जो एक LOGICAL मूल्य में 1 और 0 के अलावा कुछ भी संग्रहीत करता है, तो आप पहले से ही फोरट्रान की ओर से बाहर हैं ! तुम कहो:

एक फोरट्रान logical*1 में bool समान प्रतिनिधित्व है, लेकिन फोरट्रान में अगर बिट्स 00000011 हैं तो यह C ++ में true , अपरिभाषित है।

यह अंतिम कथन सत्य नहीं है, फोरट्रान मानक प्रतिनिधित्व-अज्ञेयवादी है, और एबीआई स्पष्ट रूप से इसके विपरीत कहता है। वास्तव में आप इसे LOGICAL तुलना के लिए दु : ख के उत्पादन की जाँच करके आसानी से देख सकते हैं:

integer function logical_compare(x, y)
    logical, intent(in) :: x
    logical, intent(in) :: y
    if (x .eqv. y) then
        logical_compare = 12
    else
        logical_compare = 24
    end if
end function logical_compare

हो जाता है

logical_compare_:
        mov     eax, DWORD PTR [rsi]
        mov     edx, 24
        cmp     DWORD PTR [rdi], eax
        mov     eax, 12
        cmovne  eax, edx
        ret

आप ध्यान देंगे कि दोनों मूल्यों के बीच एक सीधा cmp , पहले उन्हें सामान्य किए बिना ( ifort विपरीत, इस संबंध में अधिक रूढ़िवादी है)।

और भी दिलचस्प: एबीआई क्या कहता है, भले ही डिफ़ॉल्ट रूप से आईफॉर्ट लॉजिक के लिए गैरमानक प्रतिनिधित्व का उपयोग करता है; यह -fpscomp logicals स्विच स्विच प्रलेखन के बारे में बताया गया है, जो LOGICAL और क्रॉस-लैंग्वेज संगतता के बारे में कुछ दिलचस्प विवरण भी निर्दिष्ट करता है:

निर्दिष्ट करता है कि गैर-शून्य मान वाले पूर्णांक को सत्य माना जाता है, शून्य मान वाले पूर्णांकों को गलत माना जाता है। शाब्दिक स्थिर ।TRUE। 1 का पूर्णांक मान है, और शाब्दिक स्थिरांक .FALSE है। 0. का पूर्णांक मान है। यह प्रतिनिधित्व संस्करण 8.0 से पहले इंटेल फोरट्रान रिलीज और फोरट्रान पावरक्राफ्ट द्वारा उपयोग किया जाता है।

डिफ़ॉल्ट रूप से fpscomp nologicals , जो यह निर्दिष्ट करते हैं कि विषम पूर्णांक मान (कम बिट) को सत्य माना जाता है और यहां तक ​​कि पूर्णांक मान (कम बिट शून्य) को गलत माना जाता है।

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

(महत्व दिया)

अब, एक LOGICAL का आंतरिक प्रतिनिधित्व आम तौर पर एक समस्या नहीं होनी चाहिए, जैसा कि, मैं क्या इकट्ठा करता हूं, अगर आप "नियमों द्वारा" खेलते हैं और भाषा की सीमाओं को पार नहीं करते हैं, तो आप नोटिस नहीं करेंगे। मानक आज्ञाकारी कार्यक्रम के लिए INTEGER और LOGICAL बीच कोई "सीधा रूपांतरण" नहीं है; जिस तरह से मैं देख रहा हूं कि आप एक INTEGER को INTEGER में TRANSFER , TRANSFER लगता है, जो आंतरिक रूप से नॉन-पोर्टेबल है और कोई वास्तविक गारंटी नहीं देता है, या नॉन-स्टैंडर्ड INTEGER <-> असाइनमेंट पर LOGICAL रूपांतरण।

उत्तरार्द्ध को हमेशा गैर- .TRUE. -> .TRUE. में परिणाम देने के लिए दुर्भाग्य से प्रलेखित किया जाता है .TRUE. , शून्य -> .FALSE. , और आप देख सकते हैं कि सभी मामलों में कोड ऐसा करने के लिए उत्पन्न होता है (भले ही यह विरासत प्रतिनिधित्व के साथ इफोर्ट के मामले में जटिल कोड हो), इसलिए आप इस तरह से एक LOGICAL में एक मनमाना पूर्णांक को झटका नहीं दे सकते।

logical*1 function integer_to_logical(x)
    integer, intent(in) :: x
    integer_to_logical = x
    return
end function integer_to_logical
integer_to_logical_:
        mov     eax, DWORD PTR [rdi]
        test    eax, eax
        setne   al
        ret

एक LOGICAL*1 लिए रिवर्स रूपांतरण एक सीधा पूर्णांक शून्य-विस्तार (gfort) है, इसलिए, ऊपर दिए गए दस्तावेज़ में अनुबंध का सम्मान करने के लिए, यह स्पष्ट रूप से LOGICAL मान 0 या 1 होने की उम्मीद कर रहा है।

लेकिन सामान्य तौर पर, इन रूपांतरणों के लिए स्थिति थोड़ी गड़बड़ है , इसलिए मैं उनसे दूर रहूंगा।

इसलिए, लंबी कहानी छोटी: INTEGER डेटा को LOGICAL वैल्यू में डालने से बचें, क्योंकि यह फोरट्रान में भी खराब है, और यह सुनिश्चित करें कि बूलियन्स के लिए एबीआई-कंप्लीट प्रतिनिधित्व पाने के लिए सही कंपाइलर फ्लैग का उपयोग करें, और C / C ++ के लिए इंटरऑपरेबिलिटी ठीक होनी चाहिए। । लेकिन अतिरिक्त सुरक्षित होने के लिए, मैं बस सी + + पर सादे char उपयोग करूँगा।

अंत में, मैं प्रलेखन से क्या इकट्ठा करता हूं, अगर इफोर्ट में बूलियन्स सहित सी के साथ इंटरऑपरेबिलिटी के लिए कुछ अंतर्निहित समर्थन है; आप इसका लाभ उठाने का प्रयास कर सकते हैं।





abi