यह संकेत के उपयोग को अप्रत्याशित बनाता है?


108

मैं वर्तमान में पॉइंटर्स सीख रहा हूँ और मेरे प्रोफेसर ने इस कोड को एक उदाहरण के रूप में प्रदान किया है:

//We cannot predict the behavior of this program!

#include <iostream>
using namespace std;

int main()
{
    char * s = "My String";
    char s2[] = {'a', 'b', 'c', '\0'};

    cout << s2 << endl;

    return 0;
}

उन्होंने टिप्पणी में लिखा कि हम कार्यक्रम के व्यवहार की भविष्यवाणी नहीं कर सकते। क्या वास्तव में यह अप्रत्याशित बनाता है? मैने इसके साथ कुछ भी गलत नहीं देखा।


2
क्या आप सुनिश्चित हैं कि आपने प्रोफेसर के कोड को सही तरीके से पुन: प्रस्तुत किया है? हालांकि यह तर्क देना संभव है कि यह कार्यक्रम "अप्रत्याशित" व्यवहार पैदा कर सकता है, लेकिन ऐसा करने का कोई मतलब नहीं है। और मुझे संदेह है कि कोई भी प्रोफेसर छात्रों को "अप्रत्याशित" बताने के लिए कुछ गूढ़ का उपयोग करेगा।
चींटी

1
ऑर्बिट में @Lightness दौड़: आवश्यक नैदानिक ​​संदेशों को जारी करने के बाद कंपाइलरों को बीमार कोड को "स्वीकार" करने की अनुमति है। लेकिन भाषा विनिर्देश कोड के व्यवहार को परिभाषित नहीं करता है। I s, प्रोग्राम के प्रारंभ में त्रुटि के कारण , यदि कुछ संकलक द्वारा स्वीकार किया जाता है, तो औपचारिक रूप से अप्रत्याशित व्यवहार होता है।
चींटी

2
@ TheParamagneticCroissant: नहीं। आरंभीकरण आधुनिक समय में बीमार है।
लाइट ऑर्बिट

2
@ द पैरामैग्नेटिक क्रोइसैन: जैसा कि मैंने ऊपर कहा था, भाषा को "संकलन करने में विफल" के लिए बीमार कोड की आवश्यकता नहीं है। कंपाइलर को केवल डायग्नोस्टिक जारी करने की आवश्यकता होती है। उसके बाद उन्हें जारी रखने की अनुमति है और "सफलतापूर्वक" कोड संकलित करें। हालांकि, इस तरह के कोड का व्यवहार भाषा की कल्पना से परिभाषित नहीं होता है।
चींटी

2
मुझे यह जानकर अच्छा लगेगा कि आपके प्रोफेसर ने आपको क्या जवाब दिया।
डेनियल डब्ल्यू। क्रॉम्पटन

जवाबों:


125

कार्यक्रम का व्यवहार गैर-मौजूद है, क्योंकि यह बीमार है।

char* s = "My String";

यह गैरकानूनी है। 2011 से पहले, यह 12 साल के लिए पदावनत कर दिया गया था।

सही लाइन है:

const char* s = "My String";

इसके अलावा, कार्यक्रम ठीक है। आपके प्रोफेसर को कम व्हिस्की पीनी चाहिए!


10
-pedantic साथ यह होता है: main.cpp: 6: 16: चेतावनी: आईएसओ सी ++ के लिए एक स्ट्रिंग निरंतर परिवर्तित मना करता है 'चार *' [-Wpedantic]
marcinj

17
@black: नहीं, यह तथ्य कि रूपांतरण अवैध है, कार्यक्रम को बीमार बनाता है। इसे अतीत में हटा दिया गया था । हम अब अतीत में नहीं हैं।
ऑर्बिट

17
(जो मूर्खतापूर्ण है क्योंकि यह 12-वर्षीय पदावनति का उद्देश्य था)
ऑर्बिट्स ऑर्बिट

17
@ब्लैक: एक प्रोग्राम का व्यवहार जो बीमार है, वह "पूरी तरह से परिभाषित" नहीं है।
ऑर्बिट

11
बावजूद, सवाल सी ++ के बारे में है, जीसीसी के कुछ विशेष संस्करण के बारे में नहीं।
ऑर्बिट

81

इसका उत्तर है: यह इस बात पर निर्भर करता है कि आप किस C ++ मानक का विरोध कर रहे हैं। इस पंक्ति के अपवाद के साथ सभी कोड सभी मानकों पर पूरी तरह से सुव्यवस्थित हैं:

char * s = "My String";

अब, स्ट्रिंग शाब्दिक प्रकार है const char[10]और हम इसे एक नॉन-कास्ट पॉइंटर को इनिशियलाइज़ करने की कोशिश कर रहे हैं। charस्ट्रिंग शाब्दिक के परिवार के अलावा अन्य सभी प्रकारों के लिए , इस तरह की शुरूआत हमेशा अवैध थी। उदाहरण के लिए:

const int arr[] = {1};
int *p = arr; // nope!

हालांकि, स्ट्रिंग सीडल्स के लिए प्री-सी ++ 11 में, /4.2 / 2 में एक अपवाद था:

एक स्ट्रिंग शाब्दिक (2.13.4) है कि एक विस्तृत स्ट्रिंग शाब्दिक प्रकार के "एक rvalue में बदला जा सकता नहीं चार सूचक "; [...]। किसी भी स्थिति में, परिणाम सरणी के पहले तत्व के लिए एक संकेतक है। यह रूपांतरण केवल तब माना जाता है जब एक स्पष्ट उपयुक्त सूचक लक्ष्य प्रकार होता है, न कि तब जब एक अंतराल से एक अंतराल में परिवर्तित होने की सामान्य आवश्यकता होती है। [नोट: यह रूपांतरण पदावनत है । देखें अनुलग्नक डी। ]

इसलिए सी ++ 03 में, कोड पूरी तरह से ठीक है (हालांकि पदावनत), और इसमें स्पष्ट, पूर्वानुमानित व्यवहार है।

C ++ 11 में, वह ब्लॉक मौजूद नहीं है - स्ट्रिंग शाब्दिकों में रूपांतरित होने के लिए ऐसा कोई अपवाद नहीं है char*, और इसलिए कोड केवल int*उदाहरण के रूप में मेरे द्वारा प्रदत्त उदाहरण के रूप में बीमार है । कंपाइलर को डायग्नोस्टिक जारी करने के लिए बाध्य किया जाता है, और आदर्श रूप से इस तरह के मामलों में जो C ++ टाइप सिस्टम के स्पष्ट उल्लंघन हैं, हम एक अच्छे कंपाइलर से इस संबंध में न केवल पुष्टि करने की अपेक्षा करेंगे (जैसे कि चेतावनी जारी करके) एकमुश्त।

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

const int arr[] = {1};
int *p = const_cast<int*>(arr); // OK, technically

इसके साथ, बाकी कार्यक्रम पूरी तरह से ठीक है, जैसा कि आप वास्तव में sफिर कभी नहीं छूते हैं । गैर- पॉइंटर के माध्यम से बनाई गई वस्तु को पढ़ना पूरी तरह से ठीक है। इस तरह के पॉइंटर के माध्यम से निर्मित वस्तु को लिखना अपरिभाषित व्यवहार है:constconstconst

std::cout << *p; // fine, prints 1
*p = 5;          // will compile, but undefined behavior, which
                 // certainly qualifies as "unpredictable"

जैसा कि sआपके कोड में कहीं से भी कोई संशोधन नहीं है , प्रोग्राम C ++ 03 में ठीक है, C ++ 11 में संकलित करने में विफल होना चाहिए, लेकिन वैसे भी - और यह देखते हुए कि संकलक इसे अनुमति देते हैं, इसमें अभी भी कोई अपरिभाषित व्यवहार नहीं है ification । भत्ते के साथ कि कंपाइलर अभी भी [गलत तरीके से] C ++ 03 नियमों की व्याख्या कर रहे हैं, मुझे ऐसा कुछ भी नहीं दिख रहा है जिससे "अप्रत्याशित" व्यवहार हो। sहालांकि लिखें , और सभी दांव बंद हैं। C ++ 03 और C ++ 11 दोनों में।


† हालांकि, फिर से, परिभाषा के अनुसार बीमार-निर्मित कोड से उचित व्यवहार की कोई उम्मीद
नहीं है, सिवाय इसके कि, मैट मैकनब के उत्तर को न देखें


मुझे लगता है कि यहां "अप्रत्याशित" प्रोफेसर द्वारा अभिप्रेत है कि कोई यह अनुमान लगाने के लिए मानक का उपयोग नहीं कर सकता है कि एक कंपाइलर बीमार-गठित कोड (नैदानिक ​​जारी करने से परे) के साथ क्या करेगा। हां, यह इसे C ++ 03 के रूप में मान सकता है, इसका इलाज किया जाना चाहिए, और "नो ट्रू स्कॉट्समैन" के पतन के जोखिम पर) सामान्य ज्ञान हमें कुछ आत्मविश्वास के साथ भविष्यवाणी करने की अनुमति देता है कि यह केवल एक समझदार संकलक-लेखक है कभी भी चुनेंगे कि क्या कोड का संकलन है। फिर, इसे गैर-कास्ट करने से पहले स्ट्रिंग शाब्दिक को उलटने के अर्थ के रूप में माना जा सकता है। मानक C ++ परवाह नहीं करता है।
स्टीव जेसोप

2
@SteveJessop मैं वह व्याख्या नहीं खरीदता। यह न तो अनिर्धारित व्यवहार है और न ही अशिक्षित कोड की श्रेणी है जिसका मानक लेबल की आवश्यकता है क्योंकि कोई निदान आवश्यक नहीं है। यह एक सरल प्रकार की प्रणाली का उल्लंघन है जो बहुत पूर्वानुमानित होना चाहिए (C ++ 03 पर सामान्य चीजें करता है और C5 03 पर संकलित करने में विफल रहता है)। आप वास्तव में संकलक कीड़े (या कलात्मक लाइसेंस) का उपयोग करने के लिए यह सुझाव नहीं दे सकते हैं कि कोड अप्रत्याशित है - अन्यथा सभी कोड अनधिकृत रूप से अप्रत्याशित होंगे।
बैरी

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

1
नहीं, यह नहीं है। मानक बीमार कार्यक्रमों के व्यवहार को परिभाषित नहीं करता है।
स्टीव जेसप

1
@ सुपरकैट: यह एक उचित बिंदु है, लेकिन मुझे विश्वास नहीं है कि यह मुख्य कारण है। मुझे लगता है कि मुख्य कारण मानक गैर-गठित कार्यक्रमों के व्यवहार को निर्दिष्ट नहीं करता है, इसलिए यह है कि संकलक भाषा को सिंटैक्स जोड़कर एक्सटेंशन का समर्थन कर सकते हैं जो अच्छी तरह से गठित नहीं है (जैसे कि ऑब्जेक्टिव सी करता है)। एक असफल संकलन के बाद सफाई से बाहर कुल हॉर्लिक्स बनाने के लिए कार्यान्वयन की अनुमति देना सिर्फ एक बोनस है :-)
स्टीव जेसप

20

अन्य उत्तरों ने कवर किया है कि यह प्रोग्राम C ++ 11 में एक const charएरे को असाइन करने के कारण बीमार है char *

हालाँकि कार्यक्रम C ++ 11 से पहले भी बीमार था।

operator<<भार के में हैं <ostream>iostreamशामिल करने की आवश्यकता ostreamC ++ 11 में जोड़ी गई थी।

ऐतिहासिक रूप से, ज्यादातर कार्यान्वयन iostreamमें ostreamवैसे भी शामिल थे , शायद कार्यान्वयन में आसानी के लिए या शायद बेहतर क्यूओआई प्रदान करने के लिए।

लेकिन यह iostreamकेवल अतिभारों ostreamको परिभाषित किए बिना वर्ग को परिभाषित करने के लिए अनुरूप होगा operator<<


13

इस कार्यक्रम के साथ जो थोड़ी बहुत गलत बात मुझे दिखाई देती है, वह यह है कि आप एक स्ट्रिंग पॉरल को एक म्यूटेबल charपॉइंटर को सौंपने वाले नहीं हैं , हालांकि इसे अक्सर कंपाइलर एक्सटेंशन के रूप में स्वीकार किया जाता है।

अन्यथा, यह कार्यक्रम मेरे लिए अच्छी तरह से परिभाषित है:

  • पैरामीटर के रूप में पारित होने पर चरित्र एरे कैसे चरित्र बिंदु बनते हैं, यह निर्धारित करता है (जैसे कि cout << s2) अच्छी तरह से परिभाषित किया गया है।
  • सरणी शून्य-समाप्त है, जो (या ) के operator<<साथ के लिए एक शर्त है ।char*const char*
  • #include <iostream>शामिल है <ostream>, जो बदले में परिभाषित करता है operator<<(ostream&, const char*), इसलिए सब कुछ जगह में दिखाई देता है।

12

उपरोक्त कारणों से आप कंपाइलर के व्यवहार का अनुमान नहीं लगा सकते। (यह संकलन करने में विफल होना चाहिए , लेकिन नहीं हो सकता है।)

यदि संकलन सफल होता है, तो व्यवहार अच्छी तरह से परिभाषित है। आप निश्चित रूप से कार्यक्रम के व्यवहार की भविष्यवाणी कर सकते हैं।

यदि यह संकलन करने में विफल रहता है, तो कोई कार्यक्रम नहीं है। संकलित भाषा में, प्रोग्राम निष्पादन योग्य है, स्रोत कोड नहीं। यदि आपके पास एक निष्पादन योग्य नहीं है, तो आपके पास एक कार्यक्रम नहीं है, और आप उस चीज़ के व्यवहार के बारे में बात नहीं कर सकते हैं जो मौजूद नहीं है।

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


10

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

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

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

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