C++
अनिर्दिष्ट व्यवहार
खोज…
टिप्पणियों
यदि एक निर्माण का व्यवहार अनिर्दिष्ट है, तो मानक व्यवहार पर कुछ बाधाओं को रखता है, लेकिन कार्यान्वयन के लिए कुछ स्वतंत्रता छोड़ देता है, जो किसी दिए गए स्थिति में क्या होता है, यह दस्तावेज करने की आवश्यकता नहीं है। यह विपरीत कार्यान्वयन से परिभाषित व्यवहार , जिसमें कार्यान्वयन दस्तावेज़ क्या होता है के लिए आवश्यक है, और अपरिभाषित व्यवहार है, जिसमें कुछ भी हो सकता।
टीयू में ग्लोबल्स के आरंभ का आदेश
जबकि अनुवाद इकाई के अंदर, वैश्विक चर के आरंभ का आदेश निर्दिष्ट किया गया है, अनुवाद इकाइयों पर आरंभ का क्रम अनिर्दिष्ट है।
तो निम्न फ़ाइलों के साथ कार्यक्रम
foo.cpp
#include <iostream> int dummyFoo = ((std::cout << "foo"), 0);
bar.cpp
#include <iostream> int dummyBar = ((std::cout << "bar"), 0);
main.cpp
int main() {}
उत्पादन के रूप में उत्पादन कर सकते हैं:
foobar
या
barfoo
इससे स्टैटिक इनिशियलाइज़ेशन ऑर्डर फासको को बढ़ावा मिल सकता है।
एक आउट-ऑफ-रेंज रेंज का मान
यदि एक स्कॉप्ड एनम को एक अभिन्न प्रकार में बदल दिया जाता है जो अपने मूल्य को रखने के लिए बहुत छोटा है, तो परिणामस्वरूप मूल्य अनिर्दिष्ट है। उदाहरण:
enum class E {
X = 1,
Y = 1000,
};
// assume 1000 does not fit into a char
char c1 = static_cast<char>(E::X); // c1 is 1
char c2 = static_cast<char>(E::Y); // c2 has an unspecified value
इसके अलावा, यदि एक पूर्णांक को एक एनम में बदल दिया जाता है और पूर्णांक का मूल्य एनम के मूल्यों की सीमा के बाहर है, तो परिणामी मान अनिर्दिष्ट है। उदाहरण:
enum Color {
RED = 1,
GREEN = 2,
BLUE = 3,
};
Color c = static_cast<Color>(4);
हालाँकि, अगले उदाहरण में, व्यवहार अनिर्दिष्ट नहीं है , क्योंकि स्रोत मान enum की सीमा के भीतर है, हालांकि यह सभी enumerators के लिए असमान है:
enum Scale {
ONE = 1,
TWO = 2,
FOUR = 4,
};
Scale s = static_cast<Scale>(3);
यहाँ s
का मान 3 होगा, और ONE
, TWO
और FOUR
लिए असमान होगा।
फर्जी शून्य से स्थिर डाली * मूल्य
यदि एक void*
मान को ऑब्जेक्ट प्रकार, T*
लिए एक पॉइंटर में बदल दिया जाता है, लेकिन T
लिए ठीक से संरेखित नहीं किया जाता है, तो परिणामी सूचक मान अनिर्दिष्ट है। उदाहरण:
// Suppose that alignof(int) is 4
int x = 42;
void* p1 = &x;
// Do some pointer arithmetic...
void* p2 = static_cast<char*>(p1) + 2;
int* p3 = static_cast<int*>(p2);
का मूल्य p3
क्योंकि अनिर्दिष्ट है p2
प्रकार का ऑब्जेक्ट को इंगित नहीं कर सकता int
; इसका मान ठीक से संरेखित पता नहीं है।
कुछ पुन: व्याख्या_का रूपांतरण के परिणाम
एक का परिणाम reinterpret_cast
दूसरे करने के लिए एक से दूसरे समारोह सूचक प्रकार, या एक समारोह संदर्भ प्रकार से, अनिर्दिष्ट है। उदाहरण:
int f();
auto fp = reinterpret_cast<int(*)(int)>(&f); // fp has unspecified value
एक का परिणाम reinterpret_cast
दूसरे करने के लिए एक से दूसरे वस्तु सूचक प्रकार, या एक वस्तु संदर्भ प्रकार से, अनिर्दिष्ट है। उदाहरण:
int x = 42;
char* p = reinterpret_cast<char*>(&x); // p has unspecified value
हालांकि, अधिकांश संकलक के साथ, यह static_cast<char*>(static_cast<void*>(&x))
बराबर था, इसलिए परिणामस्वरूप सूचक p
ने x
के पहले बाइट को इंगित किया। यह C ++ 11 में मानक व्यवहार किया गया था। अधिक विवरण के लिए टाइप करें पाइनेटिंग रूपांतरण देखें।
कुछ सूचक तुलनाओं का परिणाम
यदि दो बिंदुओं की तुलना <
, >
, <=
, या >=
, तो परिणाम निम्न मामलों में अनिर्दिष्ट है:
संकेत विभिन्न सरणियों में इंगित करते हैं। (एक गैर-सरणी ऑब्जेक्ट को आकार 1. की एक सरणी माना जाता है)
int x; int y; const bool b1 = &x < &y; // unspecified int a[10]; const bool b2 = &a[0] < &a[1]; // true const bool b3 = &a[0] < &x; // unspecified const bool b4 = (a + 9) < (a + 10); // true // note: a+10 points past the end of the array
संकेत एक ही वस्तु में इंगित करते हैं, लेकिन विभिन्न अभिगम नियंत्रण वाले सदस्यों के लिए।
class A { public: int x; int y; bool f1() { return &x < &y; } // true; x comes before y bool f2() { return &x < &z; } // unspecified private: int z; };
अंतरिक्ष एक संदर्भ द्वारा कब्जा कर लिया
एक संदर्भ एक वस्तु नहीं है, और एक वस्तु के विपरीत, स्मृति के कुछ सन्निहित बाइट्स पर कब्जा करने की गारंटी नहीं है। मानक इसे अनिर्दिष्ट छोड़ देता है कि क्या किसी संदर्भ में किसी भी भंडारण की आवश्यकता है। भाषा की कई विशेषताएं यह दर्शाती हैं कि किसी भी स्टोरेज को जिस तरह से संदर्भित किया जा सकता है, उस पर कुछ हद तक जांच करना असंभव है।
- यदि
sizeof
को संदर्भ में लागू किया जाता है, तो यह संदर्भित प्रकार का आकार लौटाता है, जिससे इस बारे में कोई जानकारी नहीं मिलती है कि संदर्भ किसी भंडारण पर है या नहीं। - संदर्भों की सारणी अवैध हैं, इसलिए किसी संदर्भ के आकार को निर्धारित करने के लिए सरणियों के एक काल्पनिक संदर्भ के दो लगातार तत्वों के पते की जांच करना संभव नहीं है।
- यदि किसी संदर्भ का पता लिया जाता है, तो परिणाम संदर्भ का पता होता है, इसलिए हम संदर्भ के लिए एक संकेतक नहीं प्राप्त कर सकते हैं।
- यदि किसी वर्ग में एक संदर्भ सदस्य होता है, तो इस तरह के वर्ग के मानक-लेआउट वर्ग नहीं होने के कारण
offsetof
उपज का उपयोग करके उस सदस्य के पते को निकालने का प्रयास किया जाता है। - यदि किसी वर्ग में एक संदर्भ सदस्य है, तो कक्षा अब मानक लेआउट नहीं है, इसलिए संदर्भ परिणामों को अपरिभाषित या अनिर्दिष्ट व्यवहार में संग्रहीत करने के लिए उपयोग किए गए किसी भी डेटा तक पहुंचने का प्रयास करता है।
व्यवहार में, कुछ मामलों में एक संदर्भ चर को एक संकेतक चर के समान लागू किया जा सकता है और इसलिए एक संकेतक के रूप में भंडारण की समान मात्रा पर कब्जा कर सकता है, जबकि अन्य मामलों में एक संदर्भ में कोई स्थान नहीं हो सकता है क्योंकि इसे अनुकूलित किया जा सकता है। उदाहरण के लिए:
void f() {
int x;
int& r = x;
// do something with r
}
संकलक x
लिए बस एक उपनाम के रूप में r
इलाज करने के लिए स्वतंत्र है और x
साथ फ़ंक्शन f
के बाकी हिस्सों में r
की सभी घटनाओं को प्रतिस्थापित करता है, और r
को धारण करने के लिए किसी भी भंडारण को आवंटित नहीं करता है।
फ़ंक्शन तर्कों का मूल्यांकन क्रम
यदि किसी फ़ंक्शन में कई तर्क होते हैं, तो यह अनिर्दिष्ट है कि वे किस क्रम में मूल्यांकन किए जाते हैं। निम्नलिखित कोड x = 1, y = 2
या x = 2, y = 1
प्रिंट कर सकता है x = 2, y = 1
लेकिन यह अनिर्दिष्ट है।
int f(int x, int y) {
printf("x = %d, y = %d\n", x, y);
}
int get_val() {
static int x = 0;
return ++x;
}
int main() {
f(get_val(), get_val());
}
C ++ 17 में, फ़ंक्शन तर्कों के मूल्यांकन का क्रम अनिर्दिष्ट है।
हालाँकि, प्रत्येक फ़ंक्शन तर्क का पूरी तरह से मूल्यांकन किया जाता है, और किसी भी फ़ंक्शन तर्क के होने से पहले कॉलिंग ऑब्जेक्ट का मूल्यांकन किया जाता है।
struct from_int {
from_int(int x) { std::cout << "from_int (" << x << ")\n"; }
};
int make_int(int x){ std::cout << "make_int (" << x << ")\n"; return x; }
void foo(from_int a, from_int b) {
}
void bar(from_int a, from_int b) {
}
auto which_func(bool b){
std::cout << b?"foo":"bar" << "\n";
return b?foo:bar;
}
int main(int argc, char const*const* argv) {
which_func( true )( make_int(1), make_int(2) );
}
यह अवश्य प्रिंट करें:
bar
make_int(1)
from_int(1)
make_int(2)
from_int(2)
या
bar
make_int(2)
from_int(2)
make_int(1)
from_int(1)
यह प्रिंट नहीं हो सकती bar
में से किसी के बाद make
या from
की, और यह प्रिंट नहीं हो सकती:
bar
make_int(2)
make_int(1)
from_int(2)
from_int(1)
या इसी के समान। C ++ 17 प्रिंटिंग bar
पहले make_int
s कानूनी था, जैसा कि किसी भी from_int
s करने से पहले दोनों make_int
को कर रहे थे।
अधिकांश मानक पुस्तकालय वर्गों के राज्य से स्थानांतरित
सभी मानक पुस्तकालय कंटेनरों को एक वैध लेकिन अनिर्दिष्ट स्थिति में छोड़ दिया जाता है। उदाहरण के लिए, निम्नलिखित कोड में, v2
में मूव के बाद {1, 2, 3, 4}
, लेकिन v1
खाली होने की गारंटी नहीं है।
int main() {
std::vector<int> v1{1, 2, 3, 4};
std::vector<int> v2 = std::move(v1);
}
कुछ वर्गों में एक सटीक परिभाषित राज्य है। सबसे महत्वपूर्ण मामला यह है कि std::unique_ptr<T>
, जिसे ले जाने के बाद अशक्त होने की गारंटी है।