खोज…


परिचय

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

कोड जो यूबी को आमंत्रित करता है वह एक विशिष्ट संकलक के साथ एक विशिष्ट प्रणाली पर काम कर सकता है, लेकिन संभवतः किसी अन्य सिस्टम पर, या एक अलग संकलक, संकलक संस्करण या संकलक सेटिंग्स के साथ काम नहीं करेगा।

टिप्पणियों

अपरिभाषित व्यवहार (UB) क्या है?

अपरिभाषित व्यवहार C मानक में प्रयुक्त एक शब्द है। C11 मानक (ISO / IEC 9899: 2011) शब्द अपरिभाषित व्यवहार को परिभाषित करता है

एक गैर-जिम्मेदार या गलत कार्यक्रम निर्माण या गलत डेटा के उपयोग पर, जिसके लिए यह अंतर्राष्ट्रीय मानक कोई आवश्यकता नहीं रखता है

यदि मेरे कोड में UB है तो क्या होगा?

ये वे परिणाम हैं जो मानक के अनुसार अपरिभाषित व्यवहार के कारण हो सकते हैं:

नोट संभव अपरिभाषित व्यवहार, अप्रत्याशित परिणामों के साथ स्थिति की अनदेखी करने से लेकर, अनुवाद या कार्यक्रम निष्पादन के दौरान व्यवहार करने के लिए पर्यावरण की एक प्रलेखित तरीके से विशेषता (नैदानिक संदेश जारी करने के साथ या बिना), अनुवाद या निष्पादन को समाप्त करने के लिए होता है (के साथ) एक नैदानिक संदेश जारी करना)।

निम्न उद्धरण का उपयोग अक्सर वर्णन करने के लिए किया जाता है (कम औपचारिक रूप से हालांकि) अपरिभाषित व्यवहार से होने वाले परिणाम:

"जब कंपाइलर का सामना [एक दिए गए अपरिभाषित निर्माण] से होता है, तो इसके लिए कानूनी है कि आप अपनी नाक से राक्षसों को बाहर निकाल दें" (इसका अर्थ यह है कि कंपाइलर ANSI C मानक का उल्लंघन करते हुए कोड की व्याख्या करने के लिए किसी भी मनमाने ढंग से विचित्र तरीका चुन सकता है)

यूबी क्यों मौजूद है?

यदि यह इतना बुरा है, तो उन्होंने इसे सिर्फ परिभाषित क्यों नहीं किया या इसे क्रियान्वित-परिभाषित नहीं किया?

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

UB को ट्रैक करना कठिन क्यों है?

कम से कम दो कारण हैं कि अपरिभाषित व्यवहार से ऐसे कीड़े पैदा होते हैं जिनका पता लगाना मुश्किल है:

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

नल-पॉइंटर डीफर्सेशन पर विचार करें: कंपाइलर को न्यूलर-पॉइंटर डेरेफेरेंस का निदान करने की आवश्यकता नहीं है, और यहां तक कि रन-टाइम के दौरान कोई पॉइंटर किसी फ़ंक्शन में पारित नहीं किया जा सकता है, या वैश्विक चर में शून्य हो सकता है। और जब नल-पॉइंटर डेरेफेरेंस होता है, तो मानक अनिवार्य नहीं करता है कि प्रोग्राम को क्रैश करने की आवश्यकता है। बल्कि, कार्यक्रम पहले दुर्घटनाग्रस्त हो सकता है, बाद में, या दुर्घटना बिल्कुल नहीं हो सकता है; यह भी व्यवहार कर सकता है जैसे कि नल सूचक एक वैध वस्तु को इंगित करता है, और पूरी तरह से सामान्य रूप से व्यवहार करता है, केवल अन्य परिस्थितियों में दुर्घटनाग्रस्त होने के लिए।

Null-pointer dereference के मामले में, C भाषा जावा या C # जैसी प्रबंधित भाषाओं से भिन्न होती है, जहाँ null-pointer dereference का व्यवहार परिभाषित किया गया है : एक अपवाद को सटीक समय पर ( NullPointerException in Java, NullReferenceException in C #) में फेंक दिया जाता है। , इस प्रकार जावा या सी # से आने वाले लोग गलत तरीके से मान सकते हैं कि ऐसे मामले में, सी प्रोग्राम को डायग्नोस्टिक संदेश जारी करने के साथ या उसके बिना क्रैश होना चाहिए

अतिरिक्त जानकारी

ऐसी कई स्थितियाँ हैं जिन्हें स्पष्ट रूप से प्रतिष्ठित किया जाना चाहिए:

  • स्पष्ट रूप से अपरिभाषित व्यवहार, वह वह जगह है जहाँ C मानक आपको स्पष्ट रूप से बताता है कि आप बंद सीमाएं हैं।
  • स्पष्ट रूप से अपरिभाषित व्यवहार, जहां मानक में कोई पाठ नहीं है जो उस स्थिति के लिए एक व्यवहार की भविष्यवाणी करता है जिसे आप अपने कार्यक्रम में लाए थे।

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

यह भी ध्यान दें कि यदि अपरिभाषित व्यवहार कार्यक्रम में होता है, तो इसका मतलब यह नहीं है कि अपरिभाषित व्यवहार होने वाली बात सिर्फ समस्यापूर्ण है, बल्कि पूरा कार्यक्रम निरर्थक हो जाता है।

इस तरह की चिंताओं के कारण यह महत्वपूर्ण है (क्योंकि कंपाइलर हमेशा हमें यूबी के बारे में चेतावनी नहीं देते हैं) सी में व्यक्ति प्रोग्रामिंग के लिए कम से कम उन चीजों से परिचित होना चाहिए जो अपरिभाषित व्यवहार को ट्रिगर करते हैं।

यह ध्यान दिया जाना चाहिए कि कुछ उपकरण हैं (जैसे कि पीसी-लिंट जैसे स्थैतिक विश्लेषण उपकरण) जो अपरिभाषित व्यवहार का पता लगाने में सहायता करते हैं, लेकिन फिर से, वे अपरिभाषित व्यवहार की सभी घटनाओं का पता नहीं लगा सकते हैं।

एक अशक्त सूचक को संदर्भित करना

यह एक NULL पॉइंटर को dereferencing का एक उदाहरण है, जिससे अपरिभाषित व्यवहार होता है।

int * pointer = NULL;
int value = *pointer; /* Dereferencing happens here */

एक NULL पॉइंटर की गारंटी सी मानक द्वारा किसी भी पॉइंटर से असमान की तुलना किसी वैध ऑब्जेक्ट से करने के लिए की जाती है, और इसे अपरिभाषित व्यवहार को डीरफ्रेंसिंग कहा जाता है।

किसी भी वस्तु को दो अनुक्रम बिंदुओं के बीच एक से अधिक बार संशोधित करना

int i = 42;
i = i++; /* Assignment changes variable, post-increment as well */
int a = i++ + i--;

इस तरह का कोड अक्सर i के "परिणामी मूल्य" के बारे में अटकलों की ओर जाता है। एक परिणाम को निर्दिष्ट करने के बजाय, हालांकि, सी मानक निर्दिष्ट करते हैं कि इस तरह की अभिव्यक्ति का मूल्यांकन अपरिभाषित व्यवहार पैदा करता है । C2011 से पहले, मानक ने तथाकथित अनुक्रम बिंदुओं के संदर्भ में इन नियमों को औपचारिक रूप दिया:

पिछले और अगले अनुक्रम बिंदु के बीच एक स्केलर ऑब्जेक्ट में एक अभिव्यक्ति के मूल्यांकन द्वारा एक बार में इसका संग्रहीत मूल्य संशोधित किया जाएगा। इसके अलावा, स्टोर किए जाने वाले मूल्य को निर्धारित करने के लिए केवल पूर्व मान पढ़ा जाएगा।

(C99 मानक, खंड 6.5, पैरा 2)

यह योजना थोड़ी बहुत कठिन साबित हुई, जिसके परिणामस्वरूप कुछ अभिव्यक्तियों ने C99 के संबंध में अपरिभाषित व्यवहार प्रदर्शित किया, जो कि संभवतः नहीं करना चाहिए। C2011 अनुक्रम बिंदुओं को बरकरार रखता है, लेकिन अनुक्रमण के आधार पर इस क्षेत्र के लिए एक अधिक बारीक दृष्टिकोण का परिचय देता है और एक रिश्ता जिसे "पहले अनुक्रम" कहा जाता है:

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

(C2011 मानक, खंड 6.5, पैरा 2)

"संबंध से पहले अनुक्रमित" का पूरा विवरण यहाँ वर्णन करने के लिए बहुत लंबा है, लेकिन वे उन्हें दबाने के बजाय अनुक्रम बिंदुओं को पूरक करते हैं, इसलिए उनके पास कुछ मूल्यांकन के लिए व्यवहार को परिभाषित करने का प्रभाव होता है, जिसका व्यवहार पहले अपरिभाषित था। विशेष रूप से, यदि दो मूल्यांकन के बीच एक अनुक्रम बिंदु है, तो अनुक्रम बिंदु से पहले वाला "बाद में" पहले अनुक्रम है।

निम्नलिखित उदाहरण में अच्छी तरह से परिभाषित व्यवहार है:

int i = 42;
i = (i++, i+42); /* The comma-operator creates a sequence point */

निम्न उदाहरण में अपरिभाषित व्यवहार है:

int i = 42;
printf("%d %d\n", i++, i++); /* commas as separator of function arguments are not comma-operators */

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

मूल्य वापसी फ़ंक्शन में अनुपलब्ध रिटर्न स्टेटमेंट

int foo(void) {
  /* do stuff */
  /* no return here */
}

int main(void) {
  /* Trying to use the (not) returned value causes UB */
  int value = foo();
  return 0;
}

जब किसी फ़ंक्शन को मान लौटाने के लिए घोषित किया जाता है तो उसे इसके माध्यम से हर संभव कोड पथ पर ऐसा करना पड़ता है। अपरिभाषित व्यवहार तब होता है जैसे ही कॉलर (जो वापसी मूल्य की उम्मीद कर रहा है) रिटर्न मान 1 का उपयोग करने की कोशिश करता है।

ध्यान दें कि अपरिभाषित व्यवहार केवल तभी होता है जब कॉलर फ़ंक्शन से मान का उपयोग / उपयोग करने का प्रयास करता है। उदाहरण के लिए,

int foo(void) {
  /* do stuff */
  /* no return here */
}

int main(void) {
  /* The value (not) returned from foo() is unused. So, this program
   * doesn't cause *undefined behaviour*. */
  foo();
  return 0;
}
C99

main() फ़ंक्शन इस नियम का एक अपवाद है, जिसमें रिटर्न स्टेटमेंट के बिना इसे समाप्त किया जाना संभव है, क्योंकि 0 मान लिया गया रिटर्न स्वचालित रूप से इस केस 2 में उपयोग किया जाएगा।


1 ( आईएसओ / आईईसी 9899: 201x , 6.9.1 / 12)

यदि} जो किसी फ़ंक्शन को समाप्त करता है, तो पहुंच जाता है, और फ़ंक्शन कॉल का मान कॉलर द्वारा उपयोग किया जाता है, तो व्यवहार अपरिभाषित है।

2 ( आईएसओ / आईईसी 9899: 201x, 5.1.2.2.3 / 1)

}, जो मुख्य फ़ंक्शन को समाप्त करता है, तक पहुँचने पर 0 का मान मिलता है।

पूर्णांक अतिप्रवाह

C99 और C11 दोनों के प्रति पैरा 6.5 / 5, एक अभिव्यक्ति का मूल्यांकन अपरिभाषित व्यवहार का उत्पादन करता है यदि परिणाम अभिव्यक्ति के प्रकार का एक प्रतिनिधित्व मूल्य नहीं है। अंकगणितीय प्रकारों के लिए, जिसे एक अतिप्रवाह कहा जाता है। Unsigned पूर्णांक अंकगणित अतिप्रवाह नहीं करता है क्योंकि पैरा 6.2.5 / 9 लागू होता है, जिससे कोई भी अहस्ताक्षरित परिणाम होता है, अन्यथा सीमा से बाहर इन-रेंज मान कम हो जाएगा। हालाँकि, हस्ताक्षरित पूर्णांक प्रकारों के लिए कोई अनुरूप प्रावधान नहीं है; ये अपरिभाषित व्यवहार का निर्माण कर सकते हैं और अतिप्रवाह कर सकते हैं। उदाहरण के लिए,

#include <limits.h>      /* to get INT_MAX */

int main(void) {
    int i = INT_MAX + 1; /* Overflow happens here */
    return 0;
}

इस प्रकार के अपरिभाषित व्यवहार के अधिकांश उदाहरणों को पहचानना या भविष्यवाणी करना अधिक कठिन है। ओवरफ़्लो सिद्धांत रूप में किसी भी जोड़, घटाव या गुणा ऑपरेशन पर हस्ताक्षरित पूर्णांकों (सामान्य अंकगणितीय रूपांतरण के अधीन) से उत्पन्न हो सकता है, जहां इसे रोकने के लिए ऑपरेंड के बीच प्रभावी संबंध या संबंध नहीं हैं। उदाहरण के लिए, यह फ़ंक्शन:

int square(int x) {
    return x * x;  /* overflows for some values of x */
}

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

दूसरी ओर, अतिप्रवाह-सुरक्षित हस्ताक्षरित पूर्णांक अंकगणित के इस तुच्छ उदाहरण पर विचार करें:

int zero(int x) {
    return x - x;  /* Cannot overflow */
}

घटाव ऑपरेटर के ऑपरेंड्स के बीच संबंध सुनिश्चित करता है कि घटाव कभी भी अधिक नहीं होता है। या इसे कुछ और व्यावहारिक उदाहरण मानें:

int sizeDelta(FILE *f1, FILE *f2) {
    int count1 = 0;
    int count2 = 0;
    while (fgetc(f1) != EOF) count1++;  /* might overflow */
    while (fgetc(f2) != EOF) count2++;  /* might overflow */

    return count1 - count2; /* provided no UB to this point, will not overflow */
}

जब तक कि काउंटर व्यक्तिगत रूप से अतिप्रवाह नहीं करते हैं, तब तक अंतिम घटाव के संचालन दोनों गैर-नकारात्मक होंगे। किसी भी दो तरह के मूल्यों के बीच सभी मतभेदों के रूप में प्रदर्शनीय हैं int

एक असिंचित चर का उपयोग

int a; 
printf("%d", a);

चर a स्वचालित भंडारण अवधि के साथ एक int । ऊपर दिया गया उदाहरण कोड एक असमान परिवर्तनशील चर के मान को मुद्रित करने का प्रयास कर रहा है ( a आरंभिक नहीं था)। स्वचालित चर जो आरंभिक नहीं हैं, उनमें अनिश्चित मान हैं; इन तक पहुँचने से अपरिभाषित व्यवहार हो सकता है।

नोट: स्थिर या थ्रेड स्थानीय भंडारण के साथ चर , static कीवर्ड के बिना वैश्विक चर सहित, या तो शून्य या उनके प्रारंभिक मूल्य के लिए आरंभिक हैं। इसलिए निम्नलिखित कानूनी है।

static int b;
printf("%d", b);

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

उदाहरण:

#include <stdio.h>

int main(void) {
    int i, counter;
    for(i = 0; i < 10; ++i)
        counter += i;
    printf("%d\n", counter);
    return 0;
}

आउटपुट:

C02QT2UBFVH6-lm:~ gsamaras$ gcc main.c -Wall -o main
main.c:6:9: warning: variable 'counter' is uninitialized when used here [-Wuninitialized]
        counter += i;
        ^~~~~~~
main.c:4:19: note: initialize the variable 'counter' to silence this warning
    int i, counter;
                  ^
                   = 0
1 warning generated.
C02QT2UBFVH6-lm:~ gsamaras$ ./main
32812

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

int main(void)
{
    int *p;
    p++; // Trying to increment an uninitialized pointer.
}

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

अपने जीवनकाल से परे परिवर्तनशील के लिए एक पॉइंफ्रेंसिंग को संदर्भित करना

int* foo(int bar)
{
    int baz = 6;
    baz += bar;
    return &baz; /* (&baz) copied to new memory location outside of foo. */
} /* (1) The lifetime of baz and bar end here as they have automatic storage   
   * duration (local variables), thus the returned pointer is not valid! */

int main (void)
{
    int* p;

    p = foo(5);  /* (2) this expression's behavior is undefined */
    *p = *p - 6; /* (3) Undefined behaviour here */

    return 0;
}

कुछ संकलक मदद से इसे इंगित करते हैं। उदाहरण के लिए, gcc साथ चेतावनी देता है:

warning: function returns address of local variable [-Wreturn-local-addr]

और clang ने चेतावनी दी:

warning: address of stack memory associated with local variable 'baz' returned 
[-Wreturn-stack-address]

उपरोक्त कोड के लिए। लेकिन कंपाइलर जटिल कोड में मदद करने में सक्षम नहीं हो सकते हैं।

(1) वैरिएबल घोषित static संदर्भ में रिटर्निंग को परिभाषित व्यवहार कहा जाता है, क्योंकि वर्तमान स्कोप को छोड़ने के बाद वैरिएबल नष्ट नहीं होता है।

(2) आईएसओ / आईईसी 9899: 2011 6.2.4 "2 के अनुसार, "एक पॉइंटर का मूल्य अनिश्चित हो जाता है जब ऑब्जेक्ट अपने जीवनकाल के अंत तक पहुंचने के लिए इंगित करता है।"

(3) फ़ंक्शन foo द्वारा लौटाए गए सूचक को अपरिभाषित करना अपरिभाषित व्यवहार है क्योंकि यह जिस संदर्भ को संदर्भित करता है वह अनिश्चित मान रखता है।

शून्य से विभाजन

int x = 0;
int y = 5 / x;  /* integer division */

या

double x = 0.0;
double y = 5.0 / x;  /* floating point division */

या

int x = 0;
int y = 5 % x;  /* modulo operation */

प्रत्येक उदाहरण में दूसरी पंक्ति के लिए, जहां दूसरे ऑपरेंड (x) का मान शून्य है, व्यवहार अपरिभाषित है।

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

आवंटित चंक से परे मेमोरी तक पहुंच

n तत्वों से युक्त स्मृति के एक टुकड़े के लिए एक संकेतक केवल श्रेणीबद्ध हो सकता है यदि यह सीमा memory और memory + (n - 1) । उस सीमा के बाहर एक पॉइंफ्रेंसिंग को अपरिभाषित करने से अपरिभाषित व्यवहार होता है। एक उदाहरण के रूप में, निम्नलिखित कोड पर विचार करें:

int array[3];
int *beyond_array = array + 3;
*beyond_array = 0; /* Accesses memory that has not been allocated. */

तीसरी पंक्ति एक सरणी में 4 तत्व को एक्सेस करती है जो केवल 3 तत्वों को लंबा करती है, जिसके कारण अपरिभाषित व्यवहार होता है। इसी तरह, निम्नलिखित कोड के टुकड़े में दूसरी पंक्ति के व्यवहार को भी अच्छी तरह से परिभाषित नहीं किया गया है:

int array[3];
array[3] = 0;

ध्यान दें कि किसी सरणी के अंतिम तत्व को इंगित करना अपरिभाषित व्यवहार नहीं है ( beyond_array = array + 3 को यहां अच्छी तरह से परिभाषित किया गया है), लेकिन यह *beyond_array है ( *beyond_array अपरिभाषित व्यवहार है)। इस नियम ने भी (जैसे के माध्यम से बनाया बफर के रूप में गतिशील रूप से आबंटित स्मृति के लिए रखती है malloc )।

ओवरलैपिंग मेमोरी को कॉपी करना

मानक लाइब्रेरी फ़ंक्शंस की एक विस्तृत विविधता बाइट अनुक्रमों को एक मेमोरी क्षेत्र से दूसरे में कॉपी करने वाले उनके प्रभावों के बीच है। स्रोत और गंतव्य क्षेत्र ओवरलैप होने पर इनमें से अधिकांश कार्यों में अपरिभाषित व्यवहार होता है।

उदाहरण के लिए, यह ...

#include <string.h> /* for memcpy() */

char str[19] = "This is an example";
memcpy(str + 7, str, 10);

... 10 बाइट्स की प्रतिलिपि बनाने का प्रयास जहां स्रोत और गंतव्य मेमोरी क्षेत्र तीन बाइट्स द्वारा ओवरलैप करते हैं। कल्पना करने के लिए:

               overlapping area
               |
               _ _
              |   |
              v   v
T h i s   i s   a n   e x a m p l e \0
^             ^
|             |
|             destination
|
source

ओवरलैप के कारण, परिणामी व्यवहार अपरिभाषित है।

इस तरह की सीमा के साथ मानक पुस्तकालय कार्यों के बीच memcpy() , strcpy() , strcat() , sprintf() , और sscanf() । मानक इन और कई अन्य कार्यों के बारे में कहता है:

यदि ओवरलैप करने वाली वस्तुओं के बीच नकल होती है, तो व्यवहार अपरिभाषित है।

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

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

एक असंबद्ध वस्तु को पढ़ना जो स्मृति द्वारा समर्थित नहीं है

C11

यदि ऑब्जेक्ट 1 है, तो ऑब्जेक्ट को पढ़ना अपरिभाषित व्यवहार का कारण होगा:

  • अप्रारंभीकृत
  • स्वचालित भंडारण अवधि के साथ परिभाषित किया गया
  • इसका पता कभी नहीं लगा

नीचे दिए गए उदाहरण में चर उन सभी स्थितियों को संतुष्ट करता है:

void Function( void )
{
    int a;
    int b = a;
} 

1 (से उद्धृत: आईएसओ: IEC 9899: 201X 6.3.2.1 अंतराल, सरणियाँ, और फ़ंक्शन पदनाम 2)
यदि लवल्यू स्वचालित भंडारण अवधि की एक वस्तु को डिजाइन करता है जिसे रजिस्टर स्टोरेज क्लास के साथ घोषित किया जा सकता है (इसका पता कभी नहीं लिया गया था), और उस वस्तु को अनइंस्टाल्यूट किया गया है (प्रारंभिक के साथ घोषित नहीं किया गया है और इसका उपयोग करने से पहले कोई असाइनमेंट नहीं किया गया है ), व्यवहार अपरिभाषित है।

डेटा की दौड़

C11

C11 ने निष्पादन के कई थ्रेड्स के लिए समर्थन पेश किया, जो डेटा रेस की संभावना को बढ़ाता है। एक कार्यक्रम एक डेटा दौड़ अगर यह में एक वस्तु दो अलग अलग सूत्र, जहां पहुंच कम से कम एक गैर परमाणु है, कम से कम एक संशोधित वस्तु से 1 पहुँचा जा सकता है शामिल है, और कार्यक्रम अर्थ विज्ञान सुनिश्चित करना है कि दो पहुंच ओवरलैप नहीं कर सकते असफल अस्थायी। 2 अच्छी तरह से ध्यान दें कि इसमें शामिल होने की वास्तविक संगति डेटा दौड़ के लिए एक शर्त नहीं है; डेटा दौड़ स्मृति के विभिन्न थ्रेड्स के विचारों में विसंगतियों (अनुमत) से उत्पन्न होने वाले मुद्दों की एक व्यापक श्रेणी को कवर करती है।

इस उदाहरण पर विचार करें:

#include <threads.h>

int a = 0;

int Function( void* ignore )
{
    a = 1;

    return 0;
}

int main( void )
{
    thrd_t id;
    thrd_create( &id , Function , NULL );

    int b = a;

    thrd_join( id , NULL );
}

मुख्य थ्रेड एक नया थ्रेड रनिंग Function शुरू करने के लिए thrd_create कहता है। दूसरा धागा a को संशोधित करता a , और मुख्य धागा a पढ़ता a । दोनों में से कोई भी परमाणु नहीं है, और दो धागे व्यक्तिगत रूप से या संयुक्त रूप से यह सुनिश्चित करने के लिए कुछ भी नहीं करते हैं कि वे ओवरलैप नहीं करते हैं, इसलिए एक डेटा रेस है।

इस कार्यक्रम के बीच डेटा दौड़ से बच सकते हैं

  • मुख्य थ्रेड के अपने पढ़ने प्रदर्शन कर सकता है a अन्य धागा शुरू करने से पहले;
  • मुख्य थ्रेड के अपने पढ़ने प्रदर्शन कर सकता है a के माध्यम से सुनिश्चित करने के बाद thrd_join कि अन्य समाप्त कर दिया है;
  • धागे म्यूटेक्स के माध्यम से अपनी पहुंच को सिंक्रनाइज़ कर सकते हैं, हर एक उस म्यूटेक्स को लॉक करने से पहले a और उसके बाद अनलॉक कर सकता है।

म्युटेक्स विकल्प दर्शाता है के रूप में, एक डेटा दौड़ से परहेज है, जो बच्चे धागा को संशोधित करने के रूप में संचालन, की एक विशेष क्रम सुनिश्चित की आवश्यकता नहीं है a से पहले मुख्य थ्रेड इसे पढ़ता है; यह पर्याप्त है (एक डेटा दौड़ से बचने के लिए) यह सुनिश्चित करने के लिए कि किसी दिए गए निष्पादन के लिए, एक पहुंच दूसरे से पहले होगी।


1 किसी वस्तु को संशोधित करना या पढ़ना।

2 (आईएसओ से उद्धृत: आईईसी 9889: 201x, खंड 5.1.2.4 "बहु-थ्रेडेड निष्पादन और डेटा दौड़")
एक कार्यक्रम के निष्पादन में एक डेटा दौड़ होती है यदि इसमें विभिन्न थ्रेड्स में दो परस्पर विरोधी क्रियाएं होती हैं, जिनमें से कम से कम एक परमाणु नहीं है, और न ही दूसरे से पहले होता है। ऐसी किसी भी डेटा दौड़ के परिणाम अपरिभाषित व्यवहार में होते हैं।

पॉइंटर का मूल्य पढ़ें जिसे मुक्त किया गया था

यहां तक कि सिर्फ एक पॉइंटर के मूल्य को पढ़ना जो मुक्त किया गया था (यानी पॉइंटर को रोकने की कोशिश किए बिना) अपरिभाषित व्यवहार (यूबी) है, जैसे

char *p = malloc(5);
free(p);
if (p == NULL) /* NOTE: even without dereferencing, this may have UB */
{

}

आईएसओ / आईईसी 9899 का उद्धरण : 2011 , खंड 6.2.4 I2:

[…] एक पॉइंटर का मूल्य अनिश्चित हो जाता है जब वह वस्तु अपने जीवनकाल के अंत तक (या सिर्फ अतीत) को इंगित करती है।

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

स्ट्रिंग शाब्दिक संशोधित करें

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

char *p = "hello world";
p[0] = 'H'; // Undefined behavior

हालांकि, के एक परिवर्तनशील सरणी को संशोधित करने के char सीधे, या एक सूचक के माध्यम से स्वाभाविक रूप से व्यवहार अपरिभाषित नहीं है, भले ही उसके प्रारंभकर्ता एक शाब्दिक स्ट्रिंग है। निम्नलिखित ठीक है:

char a[] = "hello, world";
char *p = a;

a[0] = 'H';
p[7] = 'W';

ऐसा इसलिए है क्योंकि प्रत्येक बार सरणी आरंभीकृत होने पर स्ट्रिंग शाब्दिक रूप से प्रभावी ढंग से कॉपी की जाती है (जब एक बार स्थिर अवधि वाले वेरिएबल्स के लिए, प्रत्येक बार सरणी ऑटोमैटिक या थ्रेड अवधि वाले वेरिएबल्स के लिए बनाई जाती है - आवंटित अवधि वाले वेरिएबल्स को आरंभीकृत नहीं किया जाता है), और सरणी सामग्री को संशोधित करना ठीक है।

दो बार मेमोरी खाली करना

मेमोरी को दो बार मुक्त करना अपरिभाषित व्यवहार है, जैसे

int * x = malloc(sizeof(int));
*x = 9;
free(x);
free(x);

मानक से उद्धरण (7.20.3.2। C99 का मुफ्त कार्य):

अन्यथा, यदि तर्क कॉलोक, मॉलोक, या रियललोक फ़ंक्शन द्वारा लौटाए गए सूचक से मेल नहीं खाता है, या यदि स्पेस को फ्री या रियललोक के लिए कॉल द्वारा डील किया गया है, तो व्यवहार अपरिभाषित है।

प्रिंटफ़ में गलत प्रारूप निर्दिष्ट का उपयोग करना

पहले तर्क में एक गलत प्रारूप विनिर्देशक का उपयोग करके printf को अपरिभाषित व्यवहार को आमंत्रित करता है। उदाहरण के लिए, नीचे दिया गया कोड अपरिभाषित व्यवहार करता है:

long z = 'B';
printf("%c\n", z);

यहाँ एक और उदाहरण है

printf("%f\n",0);

कोड की ऊपर की रेखा अपरिभाषित व्यवहार है। %f दोगुनी उम्मीद करता है। हालाँकि 0 प्रकार की int

नोट अपने संकलक आमतौर पर मदद कर सकते हैं कि आप इस तरह के मामलों से बचने, यदि आप (संकलन के दौरान उचित झंडे पर बारी -Wformat में clang और gcc )। पिछले उदाहरण से:

warning: format specifies type 'double' but the argument has type
      'int' [-Wformat]
    printf("%f\n",0);
            ~~    ^
            %d

सूचक प्रकारों के बीच रूपांतरण गलत तरीके से संरेखित परिणाम उत्पन्न करता है

गलत सूचक संरेखण के कारण निम्न अपरिभाषित व्यवहार हो सकता है:

 char *memory_block = calloc(sizeof(uint32_t) + 1, 1);
 uint32_t *intptr = (uint32_t*)(memory_block + 1);  /* possible undefined behavior */
 uint32_t mvalue = *intptr;

जैसा कि सूचक परिवर्तित किया जाता है अपरिभाषित व्यवहार होता है। सी 11 के अनुसार, यदि दो पॉइंटर प्रकारों के बीच रूपांतरण एक परिणाम उत्पन्न करता है जो गलत तरीके से गठबंधन किया जाता है (6.3.2.3), तो व्यवहार अपरिभाषित है । यहां उदाहरण के लिए uint32_t को 2 या 4 के संरेखण की आवश्यकता हो सकती है।

दूसरी ओर calloc को एक पॉइंटर को वापस करने की आवश्यकता होती है जो किसी भी ऑब्जेक्ट प्रकार के लिए उपयुक्त रूप से गठबंधन किया जाता है; इस प्रकार memory_block अपने प्रारंभिक भाग में एक uint32_t सम्‍मिलित करने के लिए ठीक से संरेखित है। फिर, एक प्रणाली पर जहां uint32_t को 2 या 4 के संरेखण की आवश्यकता होती है, memory_block + 1 एक विषम पता होगा और इस प्रकार ठीक से संरेखित नहीं होगा।

निरीक्षण करें कि C मानक अनुरोध जो पहले से ही कास्ट ऑपरेशन अपरिभाषित है। यह इसलिए लगाया गया है क्योंकि जिन प्लेटफार्मों पर पते खंडित हैं, बाइट एड्रेस memory_block + 1 का पूर्णांक सूचक के रूप में उचित प्रतिनिधित्व भी नहीं हो सकता है।

संरेखण आवश्यकताओं के लिए किसी भी चिंता के बिना अन्य प्रकारों को इंगित करने के लिए कास्टिंग char * कभी-कभी गलत तरीके से फ़ाइल हेडर या नेटवर्क पैकेट जैसी पैक संरचनाओं को डिकोड करने के लिए उपयोग किया जाता है।

आप memcpy का उपयोग करके गलत संकेत सूचक रूपांतरण से उत्पन्न होने वाले अपरिभाषित व्यवहार से बच सकते हैं:

memcpy(&mvalue, memory_block + 1, sizeof mvalue);

यहाँ कोई पॉइंटर रूपांतरण uint32_t* होता है और बाइट्स को एक-एक करके कॉपी किया जाता है।

हमारे उदाहरण के लिए यह प्रतिलिपि आपरेशन केवल की मान्य मान की ओर जाता है mvalue है क्योंकि:

  • हमने calloc उपयोग किया, इसलिए बाइट्स को ठीक से आरंभीकृत किया जाता है। हमारे मामले में सभी बाइट्स का मूल्य 0 , लेकिन कोई अन्य उचित आरंभीकरण होगा।
  • uint32_t एक सटीक चौड़ाई प्रकार है और इसमें कोई पैडिंग बिट्स नहीं है
  • कोई भी मनमाना बिट पैटर्न किसी भी अहस्ताक्षरित प्रकार के लिए एक वैध प्रतिनिधित्व है।

सूचक का जोड़ या घटाव ठीक से बंधे नहीं

निम्न कोड में अपरिभाषित व्यवहार है:

char buffer[6] = "hello";
char *ptr1 = buffer - 1;  /* undefined behavior */
char *ptr2 = buffer + 5;  /* OK, pointing to the '\0' inside the array */
char *ptr3 = buffer + 6;  /* OK, pointing to just beyond */
char *ptr4 = buffer + 7;  /* undefined behavior */

C11 के अनुसार, यदि पॉइंटर के जोड़ या घटाव में, या उससे परे, एक सरणी ऑब्जेक्ट और पूर्णांक प्रकार एक परिणाम उत्पन्न करता है जो इंगित नहीं करता है, या बस परे, एक ही सरणी ऑब्जेक्ट, व्यवहार अपरिभाषित है (6.5.6) )।

इसके अतिरिक्त यह स्वाभाविक रूप से अपरिभाषित व्यवहार है एक सूचक को स्थगित करने के लिए जो केवल सरणी से परे इंगित करता है:

char buffer[6] = "hello";
char *ptr3 = buffer + 6;  /* OK, pointing to just beyond */
char value = *ptr3;       /* undefined behavior */

पॉइंटर का उपयोग करके एक कास्ट चर को संशोधित करना

int main (void)
{
    const int foo_readonly = 10;
    int *foo_ptr;

    foo_ptr = (int *)&foo_readonly; /* (1) This casts away the const qualifier */
    *foo_ptr = 20; /* This is undefined behavior */

    return 0;
}

आईएसओ / आईईसी 9899: 201x, खंड 6.7.3 EC2:

यदि गैर-कास्ट-योग्य प्रकार के साथ एक अंतराल के उपयोग के माध्यम से एक कास्ट-योग्य प्रकार के साथ परिभाषित ऑब्जेक्ट को संशोधित करने का प्रयास किया जाता है, तो व्यवहार अपरिभाषित होता है। [...]


(1) जीसीसी में यह निम्नलिखित चेतावनी फेंक सकता है: warning: assignment discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]

% S रूपांतरण प्रिंट करने के लिए एक अशक्त सूचक पास करना

printf %s रूपांतरण में कहा गया है कि संबंधित वर्ण वर्ण प्रकार के सरणी के प्रारंभिक तत्व के लिए एक संकेतक है । अशक्त सूचक वर्ण प्रकार के किसी भी सरणी के प्रारंभिक तत्व को इंगित नहीं करता है, और इस प्रकार निम्नलिखित का व्यवहार अपरिभाषित है:

char *foo = NULL;
printf("%s", foo); /* undefined behavior */

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

(null)

ऊपर दिए गए कोड के लिए। हालाँकि, प्रारूप स्ट्रिंग में एक नई पंक्ति जोड़ें (बस) और आपको एक दुर्घटना मिलेगी:

char *foo = 0;
printf("%s\n", foo); /* undefined behavior */

इस स्थिति में, ऐसा होता है क्योंकि जीसीसी में एक अनुकूलन होता है जो printf("%s\n", argument); के लिए एक कॉल में puts साथ puts(argument) , और puts Glibc में अशक्त संकेत संभाल नहीं करता है। यह सब व्यवहार मानक अनुरूप है।

ध्यान दें कि शून्य पॉइंटर एक खाली स्ट्रिंग से अलग है। तो, निम्नलिखित मान्य है और इसका कोई अपरिभाषित व्यवहार नहीं है। यह सिर्फ एक नई प्रिंट करेगा:

char *foo = "";
printf("%s\n", foo);

पहचानकर्ताओं के असंगत जुड़ाव

extern int var;
static int var; /* Undefined behaviour */

C11, 116.2.2, 7 कहते हैं:

यदि, एक अनुवाद इकाई के भीतर, एक ही पहचान with एर आंतरिक और बाहरी दोनों प्रकार के जुड़ाव के साथ दिखाई देती है, तो व्यवहार एकतरफा है।

ध्यान दें कि यदि किसी पहचानकर्ता की पूर्व घोषणा दिखाई देती है तो उसके पास पूर्व घोषणा का लिंकेज होगा। C11, 116.2.2, 4 इसे अनुमति देता है:

एक पहचान के लिए storage एर को स्टोरेज-क्लास के साथ घोषित किया गया है tern एर एक ऐसे दायरे में एक्सटर्नल है जिसमें उस पहचान की एक पूर्व घोषणा दिखाई दे रही है, 31) यदि पूर्व घोषणा का नमूना आंतरिक या बाहरी लिंकेज है, तो पहचान की the एर बाद के घोषणा पर लिंक के समान है। पूर्व घोषणा पर लिंकेज विशिष्ट at एड। यदि कोई पूर्व घोषणा दिखाई नहीं दे रही है, या यदि पूर्व घोषणा में कोई लिंक नहीं है, तो पहचान age एर में बाहरी लिंकेज है।

/* 1. This is NOT undefined */
static int var;
extern int var; 


/* 2. This is NOT undefined */
static int var;
static int var; 

/* 3. This is NOT undefined */
extern int var;
extern int var; 

एक इनपुट स्ट्रीम पर fflush का उपयोग करना

पोसिक्स और सी मानकों में स्पष्ट रूप से कहा गया है कि इनपुट स्ट्रीम पर fflush का उपयोग करना अपरिभाषित व्यवहार है। fflush केवल आउटपुट स्ट्रीम के लिए परिभाषित किया गया है।

#include <stdio.h>

int main()
{
    int i;
    char input[4096];

    scanf("%i", &i);
    fflush(stdin); // <-- undefined behavior
    gets(input);

    return 0;
}

इनपुट स्ट्रीम से अपठित वर्णों को छोड़ने का कोई मानक तरीका नहीं है। दूसरी ओर, कुछ कार्यान्वयन का उपयोग करता है fflush स्पष्ट करने के लिए stdin बफर। Microsoft एक इनपुट स्ट्रीम पर fflush के व्यवहार को परिभाषित करता है: यदि इनपुट के लिए स्ट्रीम खुला है, तो fflush बफर की सामग्री को साफ़ करता है। POSIX.1-2008 के अनुसार, जब तक इनपुट फ़ाइल की तलाश नहीं की fflush है, तब तक fflush का व्यवहार अपरिभाषित होता है।

कई और अधिक विवरणों के लिए fflush(stdin) का उपयोग करना देखें।

नकारात्मक गणना या प्रकार की चौड़ाई से परे बिट शिफ्टिंग

यदि शिफ्ट काउंट मान एक ऋणात्मक मान है तो बाएं शिफ्ट और दाएं शिफ्ट संचालन दोनों अपरिभाषित हैं 1 :

int x = 5 << -3; /* undefined */
int x = 5 >> -3; /* undefined */

यदि बाईं ओर एक नकारात्मक मूल्य पर प्रदर्शन किया जाता है, तो यह अपरिभाषित है:

int x = -5 << 3; /* undefined */

यदि बाईं पारी को सकारात्मक मूल्य पर किया जाता है और गणितीय मूल्य का परिणाम प्रकार में प्रतिनिधित्व करने योग्य नहीं है , तो यह अपरिभाषित है: 1

/* Assuming an int is 32-bits wide, the value '5 * 2^72' doesn't fit 
 * in an int. So, this is undefined. */
       
int x = 5 << 72;

ध्यान दें कि एक नकारात्मक मान (.eg -5 >> 3 ) पर सही बदलाव अपरिभाषित नहीं है लेकिन कार्यान्वयन-परिभाषित है


1 कोटिंग आईएसओ / आईईसी 9899: 201x, खंड 6.5.7:

यदि सही ऑपरेंड का मान ऋणात्मक है या पदोन्नत किए गए बाएं ऑपरेंड की चौड़ाई के बराबर या उससे अधिक है, तो व्यवहार अपरिभाषित है।

Getenv, strerror और setlocale फ़ंक्शन द्वारा लौटे स्ट्रिंग को संशोधित करना

मानक कार्यों getenv() , strerror() और setlocale() द्वारा लौटाए गए तारों को संशोधित करना अपरिभाषित है। इसलिए, कार्यान्वयन इन तारों के लिए स्थैतिक भंडारण का उपयोग कर सकते हैं।

गेटेनव () फ़ंक्शन, C11, 27.22.4.7, 4 , कहते हैं:

Getenv फ़ंक्शन मिलान सूची सदस्य के साथ संबद्ध स्ट्रिंग को पॉइंटर लौटाता है। इंगित की गई स्ट्रिंग को कार्यक्रम द्वारा संशोधित नहीं किया जा सकता है, लेकिन बाद में कॉल करने के लिए इसे फिर से लिखा जा सकता है।

स्ट्रिंगर () फ़ंक्शन, C11, 37.23.6.3, 4 कहते हैं:

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

सेटलोकाले () फ़ंक्शन, C11, .17.11.1.1, 8 कहते हैं:

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

इसी तरह localeconv() फ़ंक्शन एक संकेतक को struct lconv जिसे संशोधित नहीं किया जाएगा।

स्थानीयकरण () फ़ंक्शन, C11, .27.11.2.1, 8 कहते हैं:

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

'_Noreturn` या `noreturn` फ़ंक्शन स्पेसियर के साथ घोषित किए गए फ़ंक्शन से वापस लौटना

C11

समारोह विनिर्देश _Noreturn C11 में पेश किया गया था। हैडर <stdnoreturn.h> एक मैक्रो प्रदान करता है noreturn जो करने के लिए फैलता है _Noreturn । इसलिए <stdnoreturn.h> से _Noreturn या noreturn का उपयोग करना ठीक और समतुल्य है।

एक फ़ंक्शन जिसे _Noreturn (या noreturn ) के साथ घोषित किया गया है, उसके कॉलर पर वापस जाने की अनुमति नहीं है। इस तरह के एक समारोह में अपनी फोन करने वाले के लिए वापसी करता है, व्यवहार अपरिभाषित है।

निम्नलिखित उदाहरण में, func() को noreturn स्पेसिफायर के साथ घोषित किया गया है लेकिन यह अपने कॉलर पर लौटता है।

#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>

noreturn void func(void);

void func(void)
{
    printf("In func()...\n");
} /* Undefined behavior as func() returns */

int main(void)
{
    func();
    return 0;
}

उपरोक्त कार्यक्रम के लिए gcc और clang चेतावनी देते हैं:

$ gcc test.c
test.c: In function ‘func’:
test.c:9:1: warning: ‘noreturn’ function does return
 }
 ^
$ clang test.c
test.c:9:1: warning: function declared 'noreturn' should not return [-Winvalid-noreturn]
}
^

noreturn का उपयोग कर एक उदाहरण जिसमें अच्छी तरह से परिभाषित व्यवहार है:

#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>

noreturn void my_exit(void);

/* calls exit() and doesn't return to its caller. */
void my_exit(void)
{
    printf("Exiting...\n");
    exit(0);
}

int main(void)
{
    my_exit();
    return 0;
}


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow