यह कुछ प्लेटफार्मों पर पाश से बाहर निकलने के लिए क्यों है और दूसरों पर नहीं?


240

मैंने हाल ही में सी सीखना शुरू किया है और मैं विषय के रूप में सी के साथ एक कक्षा ले रहा हूं। मैं वर्तमान में लूप्स के साथ खेल रहा हूं और मैं कुछ अजीब व्यवहार में चल रहा हूं, जो मुझे नहीं पता कि मैं कैसे समझाऊं।

#include <stdio.h>

int main()
{
  int array[10],i;

  for (i = 0; i <=10 ; i++)
  {
    array[i]=0; /*code should never terminate*/
    printf("test \n");

  }
  printf("%d \n", sizeof(array)/sizeof(int));
  return 0;
}

मेरे लैपटॉप पर Ubuntu 14.04 चल रहा है, यह कोड नहीं टूटता है। यह पूरा करने के लिए चलाता है। मेरे स्कूल के कंप्यूटर पर CentOS 6.6 चल रहा है, यह भी ठीक चलता है। विंडोज 8.1 पर, लूप कभी समाप्त नहीं होता है।

इससे भी अधिक विचित्र यह है कि जब मैं forलूप की स्थिति को संपादित करता हूं : तो i <= 11, कोड केवल मेरे लैपटॉप पर उबंटू चल रहा है। यह कभी भी CentOS और Windows में समाप्त नहीं होता है।

क्या कोई समझा सकता है कि मेमोरी में क्या हो रहा है और एक ही कोड चलाने वाले विभिन्न OS अलग परिणाम क्यों देते हैं?

संपादित करें: मुझे पता है कि लूप सीमा से बाहर चला जाता है। मैं इसे जानबूझकर कर रहा हूं। मैं अभी यह पता नहीं लगा सकता कि विभिन्न ओएस और कंप्यूटरों में व्यवहार कैसे भिन्न हो सकता है।


147
चूंकि आप सरणी को ओवररेंज कर रहे हैं तो अपरिभाषित व्यवहार होता है। अपरिभाषित व्यवहार का मतलब है कि कुछ भी हो सकता है जिसमें यह काम करने के लिए प्रकट होता है। इस प्रकार "कोड को कभी समाप्त नहीं करना चाहिए" एक वैध अपेक्षा नहीं है।
kaylum

37
बिल्कुल, सी। में आपका स्वागत है। आपके सरणी में 10 तत्व हैं - 0 से 9 तक गिने गए
यति99

14
@JonCav आपने कोड को तोड़ दिया। आपको अपरिभाषित व्यवहार मिल रहा है जो कोड टूट गया है।
kaylum

50
ठीक है, पूरा मुद्दा यह है कि अपरिभाषित व्यवहार बिल्कुल यही है। आप मज़बूती से इसका परीक्षण नहीं कर सकते हैं और साबित कर सकते हैं कि कुछ परिभाषित होगा। आपके विंडोज मशीन में क्या चल रहा है, यह है कि चर iअंत के ठीक बाद संग्रहीत किया जाता है array, और आप इसके साथ ओवरराइट कर रहे हैं array[10]=0;। यह एक ही प्लेटफ़ॉर्म पर एक अनुकूलित बिल्ड में मामला नहीं हो सकता है, जो iएक रजिस्टर में संग्रहीत हो सकता है और कभी भी मेमोरी में इसका उल्लेख नहीं कर सकता है।
धान

46
क्योंकि नॉन-प्रेडिक्टिबिलिटी अपरिभाषित व्यवहार की एक मौलिक संपत्ति है। आपको इसे समझने की आवश्यकता है ... बिल्कुल सभी दांव बंद हैं।
धान

जवाबों:


356

मेरे लैपटॉप पर Ubuntu 14.04 चल रहा है, यह कोड नहीं टूटता है यह पूरा होने तक चलता है। मेरे स्कूल के कंप्यूटर पर CentOS 6.6 चल रहा है, यह भी ठीक चलता है। विंडोज 8.1 पर, लूप कभी समाप्त नहीं होता है।

जब मैं forलूप की सशर्त को संपादित करता हूं, तो यह अधिक अजीब है : i <= 11कोड केवल मेरे लैपटॉप पर उबंटू चल रहा है। CentOS और Windows कभी समाप्त नहीं होता है।

आपने सिर्फ मेमोरी स्टॉम्पिंग की खोज की है। आप इसके बारे में और अधिक यहाँ पढ़ सकते हैं: "मेमोरी स्टॉम्प" क्या है?

जब आप आवंटित करते हैं int array[10],i;, तो वे चर स्मृति में जाते हैं (विशेष रूप से, वे स्टैक पर आवंटित किए जाते हैं, जो फ़ंक्शन से जुड़ी मेमोरी का एक ब्लॉक है)। array[]और iशायद स्मृति में एक दूसरे से सटे हुए हैं। ऐसा लगता है कि विंडोज 8.1 पर, iस्थित है array[10]। CentOS पर iस्थित है array[11]। और उबंटू पर, यह न तो स्पॉट में है (शायद यह है array[-1]?)।

इन डिबगिंग स्टेटमेंट्स को अपने कोड में जोड़ने का प्रयास करें। आपको ध्यान देना चाहिए कि पुनरावृत्ति 10 या 11 पर, array[i]बिंदुओं पर i

#include <stdio.h>
 
int main() 
{ 
  int array[10],i; 
 
  printf ("array: %p, &i: %p\n", array, &i); 
  printf ("i is offset %d from array\n", &i - array);

  for (i = 0; i <=11 ; i++) 
  { 
    printf ("%d: Writing 0 to address %p\n", i, &array[i]); 
    array[i]=0; /*code should never terminate*/ 
  } 
  return 0; 
} 

6
धन्यवाद! यह वास्तव में काफी समझाया गया है। विंडोज में यह कहा गया है कि अगर मैं 10 सरणी से ऑफसेट करता हूं, जबकि CentOS और Ubuntu दोनों में, यह -1 है। अगर हम आपके डिबगर कोड को कमेंट करते हैं तो weirder क्या है, CentOS कोड को नहीं चला सकता (यह लटका हुआ है), लेकिन आपके डीबगिंग कोड से यह चलता है। C को X_x
JonCav

12
@JonCav "यह लटका हुआ है" हो सकता है अगर लेखन array[10]स्टैक फ्रेम को नष्ट कर देता है, उदाहरण के लिए। डिबगिंग आउटपुट के साथ या बिना कोड के बीच अंतर कैसे हो सकता है? यदि पते की iआवश्यकता नहीं है, तो कंपाइलर ऑप्टिमाइज़ कर सकताi है। एक रजिस्टर में, इस प्रकार स्टैक पर मेमोरी लेआउट बदल रहा है ...
Hagen von Eitzen

2
मुझे नहीं लगता कि यह लटका हुआ है, मुझे लगता है कि यह एक अनंत लूप में है क्योंकि यह मेमोरी से लूप काउंटर को पुनः लोड कर रहा है (जो कि केवल शून्य हो गया है array[10]=0। यदि आपने अपने कोड को अनुकूलन के साथ संकलित किया है, तो संभवतः ऐसा नहीं होगा। (क्योंकि सी है) अन्य प्रकार के स्मृति नियमों को सीमित करने वाले नियमों को संभावित रूप से अन्य मेमोरी को ओवरलैप करने के लिए ग्रहण किया जाना चाहिए। एक स्थानीय चर के रूप में, जिसे आप कभी भी संबोधित नहीं करते हैं, मुझे लगता है कि एक संकलक को यह मान लेना चाहिए कि कुछ भी उपनाम नहीं है। वैसे भी, अंत लिखना। एक सरणी अपरिभाषित व्यवहार है। इसके आधार पर बचने के लिए हमेशा कठिन प्रयास करें।
पीटर कॉर्ड्स

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

9
@JonCav मैं सामान्य रूप से कहूंगा कि आपको स्मृति प्रबंधन के बारे में अधिक जानने की आवश्यकता नहीं है और इसके बजाय केवल अपरिभाषित कोड लिखना नहीं जानते हैं, विशेष रूप से, किसी सरणी के अंत में न लिखें ...
टी। केली

98

बग कोड के इन टुकड़ों के बीच स्थित है:

int array[10],i;

for (i = 0; i <=10 ; i++)

array[i]=0;

चूंकि arrayकेवल 10 तत्व हैं, अंतिम पुनरावृत्ति array[10] = 0;में एक बफर अतिप्रवाह है। बफ़र ओवरफ्लो UNDEFINED BEHAVIOR हैं , जिसका अर्थ है कि वे आपकी हार्ड ड्राइव को प्रारूपित कर सकते हैं या राक्षसों को आपकी नाक से बाहर निकालने का कारण बन सकते हैं।

यह सभी स्टैक चर के लिए एक दूसरे से सटे होने के लिए काफी आम है। यदि iस्थित है, जहां array[10]लिखता है, तो यूबी फिर से रीसेट iहो जाएगा 0, इस तरह से अनपेक्षित लूप के लिए अग्रणी होगा।

ठीक करने के लिए, लूप की स्थिति को बदलें i < 10


6
नाइटपिक: जब तक आप रूट (या समतुल्य) के रूप में नहीं चलेंगे, तब तक आप वास्तव में बाजार के किसी भी OS पर हार्ड ड्राइव को प्रारूपित नहीं कर सकते।
केविन

26
@ केविन जब आप यूबी का आह्वान करते हैं, तो आप पवित्रता का कोई भी दावा छोड़ देते हैं।
o11c

7
इससे कोई फर्क नहीं पड़ता कि आपका कोड समझदार है या नहीं। OS आपको ऐसा नहीं करने देगा।
केविन

2
@ केविन आपको हार्ड ड्राइव तैयार करने के उदाहरण से पहले मामला सामने आया था। उस समय के यूनिक्स भी (जहां सी की उत्पत्ति हुई थी) आपको इस तरह की चीजें करने की अनुमति देने में काफी खुश थे - और आज भी, बहुत से डिस्ट्रोस खुशी से आपको rm -rf /रूट नहीं होने पर भी सब कुछ हटाने की शुरुआत करने की अनुमति देंगे , नहीं "स्वरूपण" पाठ्यक्रम की पूरी ड्राइव, लेकिन अभी भी आपके सभी डेटा को नष्ट कर रहा है। आउच।
लुआं

5
@ केविन लेकिन अपरिभाषित व्यवहार एक ओएस भेद्यता का फायदा उठा सकता है और फिर एक नए हार्ड-डिस्क ड्राइवर को स्थापित करने के लिए खुद को ऊपर उठाता है और फिर ड्राइव को स्क्रब करना शुरू कर देता है।
शाफ़्ट फ्राक

38

लूप के अंतिम रन में क्या होना चाहिए, आप लिखते हैं array[10], लेकिन सरणी में केवल 10 तत्व हैं, 0 के माध्यम से 0 गिने। सी भाषा विनिर्देश का कहना है कि यह "अपरिभाषित व्यवहार" है। व्यवहार में इसका मतलब यह है कि आपका कार्यक्रम intस्मृति के तुरंत बाद निहित स्मृति के टुकड़े को लिखने का प्रयास करेगा array। तब क्या होता है, इस पर निर्भर करता है, वास्तव में, वहां झूठ बोलते हैं, और यह न केवल ऑपरेटिंग सिस्टम पर निर्भर करता है, बल्कि संकलक पर, कंपाइलर विकल्प (जैसे अनुकूलन सेटिंग्स) पर, प्रोसेसर आर्किटेक्चर पर, आसपास के कोड पर भी निर्भर करता है , आदि यह निष्पादन से लेकर निष्पादन तक भी भिन्न हो सकते हैं, जैसे पता स्थान यादृच्छिकरण के कारण (शायद यह खिलौना उदाहरण पर नहीं है, लेकिन वास्तविक जीवन में ऐसा होता है)। कुछ संभावनाओं में शामिल हैं:

  • स्थान का उपयोग नहीं किया गया था। पाश सामान्य रूप से समाप्त हो जाता है।
  • स्थान का उपयोग उस चीज के लिए किया गया था जिसका मान 0 हुआ। लूप सामान्य रूप से समाप्त हो गया।
  • स्थान में फ़ंक्शन का पता पता था। लूप सामान्य रूप से समाप्त हो जाता है, लेकिन फिर प्रोग्राम क्रैश हो जाता है क्योंकि यह पता 0 पर कूदने की कोशिश करता है।
  • स्थान चर है i। लूप कभी समाप्त नहीं होता है क्योंकि i0 पर पुनरारंभ होता है।
  • स्थान में कुछ अन्य चर हैं। लूप सामान्य रूप से समाप्त हो जाता है, लेकिन तब "दिलचस्प" चीजें होती हैं।
  • स्थान एक अमान्य मेमोरी एड्रेस है, उदाहरण के लिए क्योंकि arrayवर्चुअल मेमोरी पेज के अंत में सही है और अगला पेज मैप नहीं किया गया है।
  • आपके नाक से दानव उड़ जाते हैं । सौभाग्य से अधिकांश कंप्यूटरों में अपेक्षित हार्डवेयर की कमी होती है।

आपने विंडोज पर जो देखा, वह यह था कि संकलक ने चर iको मेमोरी में सरणी के तुरंत बाद रखने का निर्णय लिया , इसलिए उसे array[10] = 0असाइन करना समाप्त कर दिया i। उबंटू और सेंटोस पर, कंपाइलर iवहां नहीं लगा। लगभग सभी सी कार्यान्वयन स्मृति में, मेमोरी स्टैक पर , एक प्रमुख अपवाद के साथ समूह स्थानीय चर करते हैं: कुछ स्थानीय चर पूरी तरह से रजिस्टरों में रखे जा सकते हैं । भले ही चर स्टैक पर है, चर का क्रम संकलक द्वारा निर्धारित किया जाता है, और यह न केवल स्रोत फ़ाइल में आदेश पर निर्भर हो सकता है, बल्कि उनके प्रकारों पर भी (मेमोरी को संरेखित करने के लिए बर्बादी से बचने के लिए जो छेद छोड़ देगा) उनके नाम पर, संकलक के आंतरिक डेटा संरचना आदि में उपयोग किए गए कुछ हैश मूल्य पर।

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

.L3:
    movl    -52(%rbp), %eax           ; load i to register eax
    cltq
    movl    $0, -48(%rbp,%rax,4)      ; set array[i] to 0
    movl    $.LC0, %edi
    call    puts                      ; printf of a constant string was optimized to puts
    addl    $1, -52(%rbp)             ; add 1 to i
.L2:
    cmpl    $10, -52(%rbp)            ; compare i to 10
    jle     .L3

यहाँ चर iके शीर्ष के नीचे चर 52 बाइट्स है, जबकि व्यूह ढेर के शीर्ष से 48 बाइट्स शुरू होता है। तो यह संकलक iसरणी से ठीक पहले रखा गया होता है ; iयदि आप लिखने के लिए हुआ है तो आप ओवरराइट करेंगे array[-1]। यदि आप बदलते array[i]=0हैं array[9-i]=0, तो आपको इन विशेष संकलक विकल्पों के साथ इस विशेष मंच पर एक अनंत लूप मिलेगा।

अब आप अपने कार्यक्रम को संकलित करें gcc -O1

    movl    $11, %ebx
.L3:
    movl    $.LC0, %edi
    call    puts
    subl    $1, %ebx
    jne     .L3

वह छोटा है! संकलक ने न केवल इसके लिए एक स्टैक स्थान आवंटित करने से मना कर दिया है i- यह केवल कभी रजिस्टर में संग्रहीत है ebx- लेकिन arrayइसके तत्वों को सेट करने के लिए या किसी भी मेमोरी को आवंटित करने के लिए परेशान नहीं किया है , क्योंकि यह ध्यान दिया गया है कि कोई भी तत्व नहीं है कभी उपयोग किया जाता है।

इस उदाहरण को और अधिक बताने के लिए, आइए यह सुनिश्चित करें कि संकलक को कुछ ऐसा प्रदान करने के लिए सरणी असाइनमेंट किया जाता है जो इसे अनुकूलित करने में सक्षम नहीं है। ऐसा करने का एक आसान तरीका है कि किसी अन्य फ़ाइल से सरणी का उपयोग करना - अलग संकलन के कारण, कंपाइलर को यह नहीं पता होता है कि किसी अन्य फ़ाइल में क्या होता है (जब तक कि यह लिंक समय पर अनुकूलन नहीं करता है , जो gcc -O0या gcc -O1नहीं करता है)। एक स्रोत फ़ाइल use_array.cयुक्त बनाएँ

void use_array(int *array) {}

और अपने स्रोत कोड को बदल दें

#include <stdio.h>
void use_array(int *array);

int main()
{
  int array[10],i;

  for (i = 0; i <=10 ; i++)
  {
    array[i]=0; /*code should never terminate*/
    printf("test \n");

  }
  printf("%zd \n", sizeof(array)/sizeof(int));
  use_array(array);
  return 0;
}

संकलन

gcc -c use_array.c
gcc -O1 -S -o with_use_array1.c with_use_array.c use_array.o

इस बार कोड कोड इस तरह दिखता है:

    movq    %rsp, %rbx
    leaq    44(%rsp), %rbp
.L3:
    movl    $0, (%rbx)
    movl    $.LC0, %edi
    call    puts
    addq    $4, %rbx
    cmpq    %rbp, %rbx
    jne     .L3

अब सरणी ढेर पर है, ऊपर से 44 बाइट्स। किस बारे में i? यह कहीं भी दिखाई नहीं देता है! लेकिन लूप काउंटर को रजिस्टर में रखा जाता है rbx। यह ठीक नहीं है i, लेकिन का पता है array[i]। संकलक ने फैसला किया है कि चूंकि मूल्य का iसीधे उपयोग नहीं किया गया था, इसलिए यह गणना करने के लिए अंकगणितीय प्रदर्शन करने का कोई मतलब नहीं था कि लूप के प्रत्येक रन के दौरान 0 को कहां संग्रहीत किया जाए। इसके बजाय वह पता लूप वेरिएबल है, और सीमाओं को निर्धारित करने के लिए अंकगणितीय संकलन समय पर आंशिक रूप से किया गया (11 पुनरावृत्तियों को 4 बाइट्स प्रति सरणी तत्व 44 प्राप्त करने के लिए) और आंशिक रूप से रन टाइम पर लेकिन एक बार और सभी के लिए लूप शुरू होने से पहले ( प्रारंभिक मूल्य प्राप्त करने के लिए एक घटाव प्रदर्शन)।

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


25

जावा के विपरीत, C सरणी सीमा की जांच नहीं करता है, अर्थात, ऐसा नहीं है ArrayIndexOutOfBoundsException, यह सुनिश्चित करने का कार्य है कि सरणी इंडेक्स मान्य है, प्रोग्रामर पर छोड़ दिया गया है। इस उद्देश्य को करने से अपरिभाषित व्यवहार होता है, कुछ भी हो सकता है।


एक सरणी के लिए:

int array[10]

अनुक्रमित रेंज में ही मान्य हैं 0करने के लिए 9। हालाँकि, आप इसके लिए प्रयास कर रहे हैं:

for (i = 0; i <=10 ; i++)

array[10]यहां पहुंचें , स्थिति को बदल देंi < 10


6
उद्देश्य पर ऐसा न करना भी अपरिभाषित व्यवहार की ओर जाता है - संकलक नहीं बता सकता है! ;-)
टोबी स्पाइट

1
चेतावनी के रूप में अपनी त्रुटियों को डालने के लिए बस एक मैक्रो का उपयोग करें: #define UNINTENDED_MISTAKE (EXP) प्रिंटफ ("चेतावनी:" #EXP "गलती \ n");
lkraider

1
मेरा मतलब है, यदि आप उद्देश्य पर गलती कर रहे हैं तो आप इसे भी पहचान सकते हैं और इसे सुरक्षित व्यवहार से बचने के लिए सुरक्षित कर सकते हैं; डी
लैक्राइडर

19

आपके पास सीमा उल्लंघन है, और गैर-समाप्ति वाले प्लेटफार्मों पर, मेरा मानना ​​है कि आप अनजाने iमें लूप के अंत में शून्य पर सेट कर रहे हैं , ताकि यह फिर से शुरू हो जाए।

array[10]अमान्य है; इसमें 10 तत्व शामिल हैं, के array[0]माध्यम से array[9], और array[10]11 वां है। आपके लूप को पहले बंद करने के लिए लिखा जाना चाहिए 10, इस प्रकार है:

for (i = 0; i < 10; i++)

जहां array[10]भूमि कार्यान्वयन-परिभाषित है, और मनोरंजक रूप से, आपके दो प्लेटफार्मों पर, यह भूमि पर है i, जो उन प्लेटफार्मों को स्पष्ट रूप से सीधे बाद में बाहर ले जाते हैं arrayiशून्य पर सेट है और लूप हमेशा के लिए जारी रहता है। आपके अन्य प्लेटफार्मों के लिए, iपहले स्थित हो सकता है array, या arrayइसके बाद कुछ पैडिंग हो सकती है।


मुझे नहीं लगता कि वैलग्रिंड इसे पकड़ सकता है क्योंकि यह अभी भी एक वैध स्थान है, लेकिन आसन कर सकता है।
o11c

13

आप घोषित करते हैं int array[10]कि arrayइसमें इंडेक्स 0है 9(कुल 10पूर्णांक तत्व इसे पकड़ सकते हैं)। लेकिन निम्नलिखित लूप,

for (i = 0; i <=10 ; i++)

लूप 0टू टाइम का 10अर्थ है 11इसलिए जब i = 10यह बफर को ओवरफ्लो करेगा और अनिर्धारित व्यवहार का कारण होगा ।

तो यह प्रयास करें:

for (i = 0; i < 10 ; i++)

या,

for (i = 0; i <= 9 ; i++)

7

यह पहले से अपरिभाषित है array[10], और पहले से वर्णित अनुसार अपरिभाषित व्यवहार देता है । इसके बारे में कुछ इस तरह सोचें:

मेरी किराने की गाड़ी में 10 आइटम हैं। वो हैं:

0: अनाज का एक डिब्बा
1: ब्रेड
2: दूध
3: पाई
4: अंडे
5: केक
6: ए 2 लीटर सोडा
7: सलाद
8: बर्गर
9: आइसक्रीम

cart[10]अपरिभाषित है, और कुछ संकलक में सीमा से बाहर अपवाद दे सकता है। लेकिन, बहुत स्पष्ट नहीं है। स्पष्ट 11 वाँ आइटम कार्ट में वास्तव में नहीं है 11 वाँ आइटम इंगित कर रहा है, जिसे मैं कॉल करने जा रहा हूं, एक "पॉलीटर्जिस्ट आइटम।" यह कभी अस्तित्व में नहीं था, लेकिन यह वहां था।

क्यों कुछ compilers देने iके एक सूचकांक array[10]या array[11]या यहाँ तक कि array[-1]अपने प्रारंभ / घोषणा बयान की वजह से है। कुछ संकलक इसकी व्याख्या करते हैं:

  • "के 10 ब्लॉकों का आवंटन intके लिए रों array[10]और एक अन्य intब्लॉक। यह आसान बनाने के लिए, उन्हें एक दूसरे के ठीक बगल डाल दिया।"
  • पहले की तरह ही, लेकिन इसे एक जगह या दो दूर स्थानांतरित करें, ताकि array[10]यह इंगित न करे i
  • पहले की तरह ही है, लेकिन आवंटन iपर array[-1](क्योंकि एक सरणी के एक सूचकांक या नहीं कर सकते हैं, नहीं, नकारात्मक होना चाहिए), या एक पूरी तरह से अलग स्थान पर यह आवंटन क्योंकि ओएस इसे संभाल कर सकते हैं, और यह है सुरक्षित।

कुछ संकलक चाहते हैं कि चीजें जल्दी जाएं, और कुछ संकलक सुरक्षा पसंद करते हैं। यह सब संदर्भ के बारे में है। अगर मैं प्राचीन BREW OS (एक बेसिक फोन का OS) के लिए एक ऐप विकसित कर रहा था, उदाहरण के लिए, यह सुरक्षा के बारे में परवाह नहीं करेगा। अगर मैं एक iPhone 6 के लिए विकसित कर रहा था, तो यह तेजी से चल सकता है चाहे कोई भी हो, इसलिए मुझे सुरक्षा पर जोर देना होगा। (गंभीरता से, क्या आपने Apple के ऐप स्टोर दिशानिर्देशों को पढ़ा है, या स्विफ्ट और स्विफ्ट 2.0 के विकास पर पढ़ा है?)


नोट: मैंने सूची टाइप की है इसलिए यह "0, 1, 2, 3, 4, 5, 6, 7, 8, 9" जाती है, लेकिन SO के मार्कअप भाषा ने मेरी ऑर्डर की गई सूची के पदों को निर्धारित किया।
DDPWNAGE

6

जब से आपने आकार 10 की एक सरणी बनाई है, लूप की स्थिति निम्नानुसार होनी चाहिए:

int array[10],i;

for (i = 0; i <10 ; i++)
{

वर्तमान में आप उपयोग कर रहे मेमोरी से अनसाइन किए गए स्थान को एक्सेस करने का प्रयास कर रहे हैं array[10]और यह अपरिभाषित व्यवहार का कारण बन रहा है । अपरिभाषित व्यवहार का मतलब है कि आपका कार्यक्रम अनिर्धारित फैशन का व्यवहार करेगा, इसलिए यह प्रत्येक निष्पादन में विभिन्न आउटपुट दे सकता है।


5

खैर, सी संकलक पारंपरिक रूप से सीमा की जांच नहीं करता है। यदि आप अपनी प्रक्रिया के लिए "संबंधित" नहीं होते हैं, तो आप एक विभाजन दोष प्राप्त कर सकते हैं। हालांकि, स्थानीय चर को स्टैक पर आवंटित किया जाता है और जिस तरह से मेमोरी आवंटित की जाती है, उसके आधार पर सरणी ( array[10]) से परे का क्षेत्र प्रक्रिया के मेमोरी सेगमेंट से संबंधित हो सकता है। इस प्रकार, कोई भी विभाजन दोष नहीं फेंका जाता है और यही आप अनुभव करते हैं। जैसा कि अन्य ने बताया है, यह C में अपरिभाषित व्यवहार है और आपके कोड को अनियमित माना जा सकता है। चूंकि आप C सीख रहे हैं, आप अपने कोड में सीमा के लिए जाँच करने की आदत से बेहतर हैं।


4

इस संभावना से परे कि स्मृति रखी जा सकती है ताकि a[10]वास्तव में ओवरराइट करने के लिए लिखने का प्रयास किया iजा सके, यह भी संभव होगा कि एक अनुकूलन कंपाइलर यह निर्धारित कर सके कि लूप टेस्ट iदस से अधिक के मूल्य के साथ नहीं पहुंच सकता है, बिना कोड के पहले एक्सेस किया जा सकता है। गैर-मौजूद सरणी तत्व a[10]

चूंकि उस तत्व तक पहुंचने का प्रयास अपरिभाषित व्यवहार होगा, इसलिए कंपाइलर के पास इस बात के संबंध में कोई बाध्यता नहीं होगी कि प्रोग्राम उस बिंदु पर क्या कर सकता है। अधिक विशेष रूप से, चूंकि संकलक के पास किसी भी मामले में लूप इंडेक्स की जांच करने के लिए कोड उत्पन्न करने की कोई बाध्यता नहीं होगी, जहां यह दस से अधिक हो सकता है, यह कोड को उत्पन्न करने के लिए कोई दायित्व नहीं होगा; इसके बजाय यह मान सकते हैं कि <=10परीक्षण हमेशा सही होगा। ध्यान दें कि यह तब भी सही होगा जब कोड a[10]लिखने के बजाय इसे पढ़ेगा ।


3

जब आप अतीत को पुनरावृत्त करते हैं तो आप i==9शून्य को 'एरे आइटम' के रूप में असाइन करते हैं जो वास्तव में एरे के अतीत में स्थित होते हैं , इसलिए आप कुछ अन्य डेटा को अधिलेखित कर देते हैं। शायद आप iचर को ओवरराइट करते हैं, जो बाद में स्थित है a[]। इस तरह आप बस चर को शून्य पर रीसेट करते हैंi और इस तरह लूप को पुनरारंभ करते हैं।

यदि आप iलूप में छपे हैं तो आप खुद को खोज सकते हैं :

      printf("test i=%d\n", i);

इसके बजाय बस

      printf("test \n");

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


0

त्रुटि अंश सरणी में है [10] w / c का पता i (int array [10], i;) भी है। जब सरणी [10] को 0 पर सेट किया जाता है, तो मैं 0 w / c होगा, जो पूरे लूप को रीसेट करता है और अनंत लूप का कारण बनता है। अनंत लूप होगा यदि सरणी [10] 0-10 के बीच है। सही लूप के लिए होना चाहिए (i = 0; i <10; i ++) {...} int array [10], i; के लिए (i = 0; i <= 10; i ++) सरणी [i] = 0;


0

मैं कुछ सुझाव दूंगा जो मुझे ऊपर मिले।

सरणी प्रदान करने का प्रयास करें [i] = 20;

मुझे लगता है कि यह कोड को हर जगह समाप्त कर देना चाहिए .. (आप मुझे रखते हैं <= 10 या ll)

यदि यह चलता है तो आप दृढ़ता से यह तय कर सकते हैं कि यहां दिए गए उत्तर पहले से ही सही हैं [स्मृति से संबंधित उत्तर पूर्व के लिए स्टंपिंग]।


-9

यहां दो चीजें गलत हैं। इंट मैं वास्तव में एक सरणी तत्व, सरणी [10] है, जैसा कि स्टैक पर देखा गया है। क्योंकि आपने अनुक्रमणिका को वास्तव में सरणी बनाने की अनुमति दी है [१०] = ०, लूप इंडेक्स, यानी, १० से अधिक कभी नहीं for(i=0; i<10; i+=1)

i ++ है, जैसा कि K & R इसे कहते हैं, 'खराब शैली'। यह मुझे i के आकार से बढ़ाता है, न कि 1. i ++ सूचक गणित के लिए और i + = 1 बीजगणित के लिए है। जबकि यह संकलक पर निर्भर करता है, यह पोर्टेबिलिटी के लिए एक अच्छा सम्मेलन नहीं है।


5
-1 पूरी तरह से गलत। चर iNOTan सरणी तत्व है a[10], वहाँ कोई दायित्व नहीं है या एक संकलक तुरंत ढेर पर डाल करने के लिए के लिए भी सुझाव है के बाद a[] - यह रूप में अच्छी तरह सरणी से पहले स्थित हो सकता है, या कुछ अतिरिक्त स्थान के साथ अलग कर दिया। इसे मुख्य मेमोरी के बाहर भी आवंटित किया जा सकता है, उदाहरण के लिए सीपीयू रजिस्टर में। यह भी असत्य है कि ++सूचक के लिए है और पूर्णांक के लिए नहीं है। पूरी तरह से गलत है 'i ++ मैं i के आकार से बढ़ा है' - भाषा की परिभाषा में ऑपरेटर विवरण पढ़ें!
सियापन

यही कारण है कि यह कुछ प्लेटफार्मों पर काम करता है और अन्य नहीं। यह केवल तार्किक व्याख्या है कि क्यों यह खिड़कियों पर हमेशा के लिए बंद हो जाता है। I ++ के संबंध में यह सूचक गणित है पूर्णांक नहीं। शास्त्र पढ़ें ... 'सी प्रोग्रामिंग भाषा'। केर्निगन और Ritche द्वारा, अगर आप चाहते हैं कि मेरे पास एक ऑटोग्राफ की हुई कॉपी हो और 1981 से c में प्रोग्रामिंग कर रहे हों।
SkipBerne

1
ओपी द्वारा स्रोत कोड पढ़ें और चर की घोषणा खोजें i- यह intप्रकार का है। यह एक पूर्णांक है , एक सूचक नहीं है; एक पूर्णांक, के लिए एक सूचकांक के रूप में इस्तेमाल किया array
सियापन

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

@SkipBerne: अधिक नकारात्मक बिंदुओं के साथ "सम्मानित" किए जाने से पहले अपना उत्तर हटाने के लिए विचार करें।
पीटर वार्गा जूल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.