सभी सामान्य अपरिभाषित व्यवहार क्या हैं जिनके बारे में C ++ प्रोग्रामर को पता होना चाहिए? [बन्द है]


201

सभी सामान्य अपरिभाषित व्यवहार क्या हैं जिनके बारे में C ++ प्रोग्रामर को पता होना चाहिए?

कहो, जैसे:

a[i] = i++;


3
क्या आपको यकीन है। यह अच्छी तरह से परिभाषित लग रहा है।
मार्टिन यॉर्क

17
6.2.2 मूल्यांकन आदेश [expr.evaluation] C ++ प्रोग्रामिंग भाषा में ऐसा कहते हैं। मेरे पास कोई अन्य संदर्भ नहीं है
यशराज

4
वह सही है .. बस C ++ प्रोग्रामिंग लैंग्वेज में 6.2.2 देखा गया और यह कहता है कि v [i] = i ++ अपरिभाषित है
dancavallaro

4
मैं कल्पना करूँगा क्योंकि कॉमाइलर v [i] की मेमोरी लोकेशन की गणना करने से पहले या बाद में i ++ निष्पादित करता है। यकीन है, मैं हमेशा वहाँ सौंपा जा रहा हूँ। लेकिन यह संचालन के आदेश के आधार पर v [i] या v [i + 1] दोनों में लिख सकता है
Evan Teran

2
C ++ प्रोग्रामिंग लैंग्वेज कहती है कि "अभिव्यक्ति के भीतर सबएक्सप्रेस के संचालन का क्रम अपरिभाषित है। विशेष रूप से, आप यह नहीं मान सकते कि अभिव्यक्ति का मूल्यांकन बाएं से दाएं किया गया है।"
दनकवलारो १५'०१aro को aro:

जवाबों:


233

सूचक

  • एक NULLसूचक को संदर्भित करना
  • आकार शून्य के "नए" आवंटन द्वारा लौटाए गए एक सूचक को हटा देना
  • उन वस्तुओं की ओर संकेत करना जिनका जीवनकाल समाप्त हो गया है (उदाहरण के लिए, आवंटित वस्तुओं या हटाए गए ऑब्जेक्ट को ढेर करें)
  • एक पॉइंटर को Derefericing जो निश्चित रूप से अभी तक शुरू नहीं हुआ है
  • सूचक अंकगणितीय प्रदर्शन करना जो किसी सरणी की सीमाओं (या तो ऊपर या नीचे) के बाहर परिणाम देता है।
  • एक सरणी के अंत से परे एक स्थान पर सूचक को हटा देना।
  • असंगत प्रकार की वस्तुओं की ओर संकेत करते हुए
  • memcpyओवरलैपिंग बफ़र्स की प्रतिलिपि का उपयोग करना

बफर ओवरफ्लो हो गया

  • किसी वस्तु या सरणी को किसी ऑफसेट पर पढ़ना या लिखना नकारात्मक है, या उस ऑब्जेक्ट के आकार से परे (स्टैक / हीप ओवरफ्लो)

इंटेगर ओवरफ्लो

  • पूर्णांक अतिप्रवाह
  • एक अभिव्यक्ति का मूल्यांकन जो गणितीय रूप से परिभाषित नहीं है
  • ऋणात्मक राशि द्वारा वाम-स्थानांतरण मान (ऋणात्मक मात्राओं द्वारा सही बदलाव कार्यान्वयन को परिभाषित करते हैं)
  • संख्या में बिट्स की संख्या के बराबर या उससे अधिक राशि द्वारा मूल्यों को शिफ्ट करना (जैसे int64_t i = 1; i <<= 72अपरिभाषित है)

प्रकार, कास्ट और कास्ट

  • एक संख्यात्मक मान को ऐसे मूल्य में बदलना जो लक्ष्य प्रकार (या तो सीधे या static_cast के माध्यम से) का प्रतिनिधित्व नहीं कर सकता है
  • निश्चित रूप से असाइन किए जाने से पहले एक स्वचालित चर का उपयोग करना (जैसे, int i; i++; cout << i;)
  • अन्य की तुलना में प्रकार की किसी भी वस्तु के मूल्य का उपयोग करना volatileयाsig_atomic_tसिग्नल की प्राप्ति पर
  • अपने जीवनकाल के दौरान एक स्ट्रिंग शाब्दिक या किसी अन्य कास्ट ऑब्जेक्ट को संशोधित करने का प्रयास
  • प्रीप्रोसेसिंग के दौरान एक विस्तृत स्ट्रिंग शाब्दिक के साथ एक संकीर्ण को समेटना

समारोह और टेम्पलेट

  • मान-वापसी फ़ंक्शन से कोई मान नहीं लौटा रहा है (सीधे या कोशिश-ब्लॉक से बहकर)
  • एक ही इकाई (वर्ग, टेम्पलेट, गणना, इनलाइन फ़ंक्शन, स्थिर सदस्य फ़ंक्शन, आदि) के लिए कई अलग-अलग परिभाषाएं।
  • टेम्पलेट्स की तात्कालिकता में अनंत पुनरावृत्ति
  • फ़ंक्शन को विभिन्न मापदंडों या लिंकेज का उपयोग करके पैरामीटर को कॉल करना और लिंकेज है कि फ़ंक्शन का उपयोग करके परिभाषित किया गया है।

OOP

  • स्थैतिक भंडारण अवधि के साथ ऑब्जेक्ट्स के कैस्केडिंग विनाश
  • आंशिक रूप से अतिव्यापी वस्तुओं को असाइन करने का परिणाम
  • अपने स्टैटिक ऑब्जेक्ट्स के आरंभीकरण के दौरान किसी फ़ंक्शन को पुन: दर्ज करना
  • वर्चुअल फ़ंक्शन कॉल किसी ऑब्जेक्ट के शुद्ध वर्चुअल फ़ंक्शंस को उसके निर्माता या विध्वंसक से करता है
  • जिन वस्तुओं का निर्माण नहीं किया गया है या जो पहले ही नष्ट हो चुकी हैं, उनके गैर-अस्थिर सदस्यों का उल्लेख

स्रोत फ़ाइल और प्रीप्रोसेसिंग

  • एक गैर-रिक्त स्रोत फ़ाइल जो किसी नई पंक्ति के साथ समाप्त नहीं होती है, या बैकस्लैश के साथ समाप्त होती है (C ++ 11 से पहले)
  • एक वर्ण द्वारा पीछा किया जाने वाला एक बैकस्लैश जो किसी वर्ण या स्ट्रिंग स्थिरांक में निर्दिष्ट एस्केप कोड का हिस्सा नहीं है (यह C ++ 11 में कार्यान्वयन-परिभाषित है)।
  • कार्यान्वयन सीमाएँ (नेस्टेड ब्लॉक की संख्या, एक कार्यक्रम में कार्यों की संख्या, उपलब्ध स्टैक स्पेस ...)
  • प्रीप्रोसेसर न्यूमेरिक वैल्यूज़ जिन्हें a द्वारा दर्शाया नहीं जा सकता है long int
  • एक फ़ंक्शन की तरह बाईं ओर मैक्रो परिभाषा में प्रीप्रोसेसिंग निर्देश
  • गतिशील रूप से परिभाषित टोकन को #ifअभिव्यक्ति में उत्पन्न करना

वर्गीकृत किया जाना है

  • स्थैतिक भंडारण अवधि के साथ एक कार्यक्रम के विनाश के दौरान बाहर निकलना

हम ... NaN (x / 0) और Infinity (0/0) IEE 754 द्वारा कवर किए गए थे, अगर C ++ को बाद में डिजाइन किया गया था, तो यह x / 0 को अपरिभाषित के रूप में क्यों रिकॉर्ड करता है?
new123456

पुन: "एक बैकस्लैश के बाद एक ऐसा चरित्र जो एक चरित्र या स्ट्रिंग स्थिरांक में निर्दिष्ट एस्केप कोड का हिस्सा नहीं है।" कि C89 (§3.1.3.4) और C ++ 03 (जो C89 को शामिल करता है) में UB है, लेकिन C99 में नहीं। C99 का कहना है कि "परिणाम एक टोकन नहीं है और एक नैदानिक ​​की आवश्यकता है" (that6.4.4.4)। संभवतः C ++ 0x (जिसमें C89 शामिल है) समान होगा।
एडम रोसेनफील्ड

1
C99 मानक में परिशिष्ट J.2 में अपरिभाषित व्यवहारों की एक सूची है। इस सूची को C ++ के अनुकूल बनाने में कुछ काम लगेगा। आपको C99 क्लॉज़ के बजाय सही C ++ क्लॉज़ के संदर्भों को बदलना होगा, कुछ भी अप्रासंगिक हटा दें, और यह भी जाँच लें कि क्या वे सभी चीज़ें C ++ के साथ-साथ C में भी अपरिभाषित हैं या नहीं, लेकिन यह एक शुरुआत प्रदान करता है।
स्टीव जेसोप

1
@ new123456 - सभी फ्लोटिंग पॉइंट यूनिट IEE754 संगत नहीं हैं। यदि C ++ को IEE754 अनुपालन की आवश्यकता होती है, तो संकलकों को उस मामले को जांचने और संभालने की आवश्यकता होगी जहां आरएचएस एक स्पष्ट जांच के माध्यम से शून्य है। व्यवहार को अपरिभाषित बनाकर, कंपाइलर उस ओवरहेड को यह कहकर टाल सकता है कि "यदि आप एक गैर IEE754 FPU का उपयोग करते हैं, तो आपको IEEE754 FPU व्यवहार नहीं मिलेगा"।
सिक्योरिटीमैट

1
"एक अभिव्यक्ति का मूल्यांकन जिसका परिणाम संबंधित प्रकारों की सीमा में नहीं है" .... पूर्णांक ओवरफ्लो को UNSIGNED अभिन्न प्रकारों के लिए अच्छी तरह से परिभाषित किया गया है, बस उन पर हस्ताक्षर नहीं किए गए हैं।
nacitar sevaht

31

फ़ंक्शन मापदंडों का मूल्यांकन करने वाला क्रम अनिर्दिष्ट व्यवहार है । (यह आपके प्रोग्राम को क्रैश नहीं करेगा, विस्फोट नहीं करेगा या पिज्जा ... अपरिभाषित व्यवहार के विपरीत )।

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


यह:

// The simple obvious one.
callFunc(getA(),getB());

इसके बराबर हो सकता है:

int a = getA();
int b = getB();
callFunc(a,b);

या यह:

int b = getB();
int a = getA();
callFunc(a,b);

यह या तो हो सकता है; यह संकलक तक है। परिणाम दुष्प्रभाव के आधार पर मायने रख सकता है।


23
आदेश अनिर्दिष्ट है, अपरिभाषित नहीं है।
रॉब केनेडी

1
मैं इस एक से नफरत करता हूं :) मैंने इन मामलों में से एक पर नज़र रखने के बाद एक दिन का काम खो दिया ... वैसे भी मेरे सबक सीखा और सौभाग्य से फिर से गिर नहीं गया
रॉबर्ट गोल्ड

2
@ रोब: मैं यहाँ अर्थ में बदलाव के बारे में आपसे बहस करूँगा, लेकिन मुझे पता है कि इन दो शब्दों की सटीक परिभाषा पर मानक समिति बहुत चुस्त है। तो मैं बस इसे बदल दूंगा :-)
मार्टिन यॉर्क

2
मैं इस पर भाग्यशाली हो गया। मुझे इसका काट तब मिला जब मैं कॉलेज में था और एक प्रोफेसर था जिसने इस पर एक नज़र डाली और लगभग 5 सेकंड में मुझे अपनी समस्या बताई। कोई यह नहीं बताता कि मैंने डिबगिंग को कितना समय बर्बाद किया होगा।
छिपकली का बिल

27

संकलक एक अभिव्यक्ति के मूल्यांकन भागों को फिर से आदेश देने के लिए स्वतंत्र है (अर्थ अपरिवर्तित है)।

मूल प्रश्न से:

a[i] = i++;

// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)

// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:

int rhs  = i++;
int lhs& = a[i];
lhs = rhs;

// or
int lhs& = a[i];
int rhs  = i++;
lhs = rhs;

डबल चेकिंग लॉकिंग। और एक आसान गलती।

A* a = new A("plop");

// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'

// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.

// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
    Lock   lock(mutex);
    if (a == null)
    {
        a = new A("Plop");  // (Point A).
    }
}
a->doStuff();

// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
//           Remember (c) has been done thus 'a' is not NULL.
//           But the memory has not been initialized.
//           Thread 2 now executes doStuff() on an uninitialized variable.

// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
    Lock   lock(mutex);
    if (a == null)
    {
        A* tmp = new A("Plop");  // (Point A).
        a = tmp;
    }
}
a->doStuff();

// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.

अनुक्रम बिंदु से क्या मतलब है?
यसराज


1
ओह ... यह बुरा है, खासकर जब से मैंने देखा है कि जावा में सटीक संरचना की सिफारिश की गई है
टॉम

ध्यान दें कि कुछ संकलक इस स्थिति में व्यवहार को परिभाषित करते हैं। वीसी ++ 2005+ में, उदाहरण के लिए, यदि कोई अस्थिर है, तो निर्देश की पुनरावृत्ति को रोकने के लिए आवश्यक मेमोरी बारियर स्थापित किए जाते हैं ताकि डबल-चेकिंग लॉकिंग कार्य हो सके।
ग्रहण

मार्टिन यॉर्क: <i> // (सी) के बाद (ए) और (बी) </ i> होने की गारंटी है? उस विशेष उदाहरण में केवल एक ही परिदृश्य जहाँ यह बात हो सकती है अगर 'i' हार्डवेयर रजिस्टर में एक अस्थिर वैरिएबल मैप किया गया था, और [i] ('i' का पुराना मान) इसे अलियास किया गया था, लेकिन क्या कोई है गारंटी है कि वेतन वृद्धि एक अनुक्रम बिंदु से पहले होगी?
सुपरकैट

5

मेरा पसंदीदा "टेम्पलेट्स की तात्कालिकता में अनंत पुनरावृत्ति" है क्योंकि मेरा मानना ​​है कि यह एकमात्र ऐसा स्थान है जहां संकलन समय पर अपरिभाषित व्यवहार होता है।


यह पहले हो गया, लेकिन मैं नहीं देखता कि कैसे इसका अपरिभाषित होना। यह काफी स्पष्ट है कि आपके विचार में एक अनंत पुनरावृत्ति कर रहा है।
रॉबर्ट गोल्ड

समस्या यह है कि संकलक आपके कोड की जांच नहीं कर सकता है और यह ठीक से तय कर सकता है कि यह अनंत पुनरावृत्ति से पीड़ित होगा या नहीं। यह रुकने की समस्या का एक उदाहरण है। देखें: stackoverflow.com/questions/235984/…
डैनियल ईयरविकेर

हाँ, इसकी निश्चित रूप से एक समस्या है
रॉबर्ट गोल्ड ने

यह बहुत कम स्मृति के कारण स्वैपिंग के कारण मेरे सिस्टम को क्रैश कर गया।
जोहान्स स्काउब -

2
पूर्वप्रक्रमक स्थिरांक जो एक इंट में फिट नहीं है वह भी संकलन समय है।
जोशुआ

5

Ness का constउपयोग करने के बाद स्ट्रिपिंग के बाद एक स्थिर को निरुपित करना const_cast<>:

const int i = 10; 
int *p =  const_cast<int*>( &i );
*p = 1234; //Undefined

5

अपरिभाषित व्यवहार के अलावा , समान रूप से गंदा कार्यान्वयन-परिभाषित व्यवहार भी है

अपरिभाषित व्यवहार तब होता है जब कोई प्रोग्राम कुछ ऐसा करता है जिसका परिणाम मानक द्वारा निर्दिष्ट नहीं होता है।

कार्यान्वयन-परिभाषित व्यवहार एक कार्यक्रम द्वारा एक क्रिया है जिसका परिणाम मानक द्वारा परिभाषित नहीं किया गया है, लेकिन जिसे दस्तावेज़ में लागू करना आवश्यक है। एक उदाहरण स्टैक ओवरफ्लो प्रश्न से "मल्टीबाइट चरित्र शाब्दिक" है, क्या कोई सी संकलक है जो इसे संकलित करने में विफल रहता है?

कार्यान्वयन-परिभाषित व्यवहार आपको केवल तभी काटता है जब आप पोर्ट करना शुरू करते हैं (लेकिन कंपाइलर के नए संस्करण में अपग्रेड करना भी पोर्टिंग है!)।


4

चर केवल एक बार एक अभिव्यक्ति (तकनीकी रूप से एक बार अनुक्रम बिंदुओं के बीच) में अपडेट किए जा सकते हैं।

int i =1;
i = ++i;

// Undefined. Assignment to 'i' twice in the same expression.

दरअसल कम से कम एक बार दो अनुक्रम अंक के बीच।
प्रसून सौरव

2
@Prasoon: मुझे लगता है आप का मतलब है: अधिक से अधिक एक बार दो अनुक्रम अंक के बीच। :-)
नवाज

3

विभिन्न पर्यावरणीय सीमाओं की एक बुनियादी समझ। पूरी सूची सी विनिर्देश के खंड 5.2.4.1 में है। यहाँ कुछ है;

  • एक फंक्शन डे। नेशन में 127 पैरामीटर
  • एक फ़ंक्शन कॉल में 127 तर्क
  • एक मैक्रो डे। नेशन में 127 पैरामीटर
  • एक मैक्रो इनवोकेशन में 127 तर्क
  • एक तार्किक स्रोत लाइन में 4095 अक्षर
  • एक चरित्र स्ट्रिंग शाब्दिक या चौड़ी स्ट्रिंग शाब्दिक में 4095 वर्ण (संघनन के बाद)
  • एक वस्तु में 65535 बाइट (केवल एक होस्ट किए गए वातावरण में)
  • #Included esting लेस के लिए 15nesting स्तर
  • 1023 केस लेबल एक स्विच स्टेटमेंट के लिए (किसी भी स्विच किए गए स्टेटमेंट को छोड़कर)

मैं वास्तव में एक स्विच स्टेटमेंट के लिए 1023 केस लेबल की सीमा पर थोड़ा आश्चर्यचकित था, मैं समझ सकता हूं कि उत्पन्न कोड / लेक्स / पार्सर्स के लिए काफी सहजता से पार किया जा रहा है।

यदि ये सीमाएं पार हो गई हैं, तो आपके पास अपरिभाषित व्यवहार (क्रैश, सुरक्षा दोष, आदि ...) है।

सही है, मुझे पता है कि यह सी विनिर्देश से है, लेकिन सी ++ इन बुनियादी समर्थन को साझा करता है।


9
यदि आप इन सीमाओं को मारते हैं, तो आपको अपरिभाषित व्यवहार की तुलना में अधिक समस्याएं हैं।
new123456

आप एक वस्तु में 65535 बाइट्स से अधिक कर सकते हैं, जैसे कि STD :: वेक्टर
डेमी

2

memcpyअतिव्यापी मेमोरी क्षेत्रों के बीच प्रतिलिपि का उपयोग करना । उदाहरण के लिए:

char a[256] = {};
memcpy(a, a, sizeof(a));

व्यवहार C मानक के अनुसार अपरिभाषित है, जिसे C ++ 03 मानक द्वारा निर्धारित किया गया है।

7.21.2.1 यादगार समारोह

सार

1 / #include void * memcpy (शून्य * प्रतिबंधित s1, const void * प्रतिबंधित s2, size_n n);

विवरण

2 / memcpy फ़ंक्शन s1 द्वारा इंगित ऑब्जेक्ट में s2 द्वारा इंगित ऑब्जेक्ट से n वर्णों की प्रतिलिपि बनाता है। यदि ओवरलैप करने वाली वस्तुओं के बीच नकल होती है, तो व्यवहार अपरिभाषित है। रिटर्न 3 मेमॉपी फ़ंक्शन s1 का मान लौटाता है।

7.21.2.2 मेमोव फ़ंक्शन

सार

1 #include void * memmove (void * s1, const void * s2, size_t n);

विवरण

2 मेमोव फ़ंक्शन एन 1 द्वारा इंगित ऑब्जेक्ट में s2 द्वारा बताई गई वस्तु से n वर्णों की प्रतिलिपि बनाता है। नकल करना ऐसा लगता है मानो s2 द्वारा बताई गई वस्तु से n वर्ण पहले n वर्णों के एक अस्थायी सरणी में कॉपी किए गए हैं जो s1 और s2 द्वारा इंगित की गई वस्तुओं को ओवरलैप नहीं करते हैं, और फिर अस्थायी सरणी से n वर्णों की प्रतिलिपि बनाई जाती है s1 द्वारा इंगित की गई वस्तु। रिटर्न

3 मेमोव फ़ंक्शन s1 का मान लौटाता है।


2

एकमात्र प्रकार जिसके लिए C ++ एक आकार की गारंटी देता है char। और आकार 1. अन्य सभी प्रकारों का आकार प्लेटफॉर्म पर निर्भर है।


नहीं है कि क्या है <cstdint> के लिए? यह uint16_6 et cetera जैसे प्रकारों को परिभाषित करता है।
जैस्पर बेकर्स

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

cstdint अभी भी वर्तमान c ++ मानक का हिस्सा नहीं है। वर्तमान में पोर्टेबल समाधान के लिए बढ़ावा / stdint.hpp देखें।
इवान टेरान

यह अपरिभाषित व्यवहार नहीं है। मानक कहता है कि अनुरूप मंच आकार को परिभाषित करता है, बजाय मानक उन्हें परिभाषित करने के।
डैनियल ईयरविकर

1
@JaredPar: यह एक जटिल पोस्ट है जिसमें बहुत सारे वार्तालाप हैं, इसलिए मैंने इसे यहाँ पूरा किया । लब्बोलुआब यह है: "5. बाइनरी में -2147483647 और +2147483647 का प्रतिनिधित्व करने के लिए, आपको 32 बिट्स की आवश्यकता है।"
जॉन डिबलिंग

2

एक अलग संकलन इकाइयों में नेमस्पेस स्तर की वस्तुओं को प्रारंभिककरण के लिए कभी भी एक-दूसरे पर निर्भर नहीं होना चाहिए, क्योंकि उनका प्रारंभिक क्रम अनिर्धारित है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.