Bu neden bazı platformlarda değil, bazı platformlarda döngü çıkışı için?


240

Son zamanlarda C öğrenmeye başladım ve konu olarak C ile bir ders alıyorum. Şu anda döngülerle oynuyorum ve nasıl açıklayacağımı bilmediğim garip bir davranışla karşılaşıyorum.

#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 çalıştıran dizüstü bilgisayarımda bu kod bozulmuyor. Tamamlanmak için çalışır. Okulumun CentOS 6.6 çalıştıran bilgisayarında da iyi çalışıyor. Windows 8.1'de, döngü asla sona ermez.

Daha da garip olan şey, fordöngünün durumunu şu şekilde düzenlediğimde : i <= 11kodun sadece Ubuntu çalıştıran dizüstü bilgisayarımda sonlanması. CentOS ve Windows'ta hiçbir zaman sona ermez.

Herkes hafızada neler olduğunu ve aynı kodu çalıştıran farklı işletim sistemlerinin neden farklı sonuçlar verdiğini açıklayabilir mi?

EDIT: for döngüsü sınırların dışına gider biliyorum. Bunu kasten yapıyorum. Farklı işletim sistemlerinde ve bilgisayarlarda davranışın nasıl farklı olabileceğini anlayamıyorum.


147
Diziyi geçersiz kıldığınız için tanımlanmamış davranış oluşur. Tanımsız davranış, işe yaraması da dahil olmak üzere her şeyin olabileceği anlamına gelir. Dolayısıyla "kod asla sonlandırılmamalıdır" geçerli bir beklenti değildir.
kaylum

37
Tam olarak, C'ye hoş geldiniz. Diziniz 0 ile 9 arasında numaralandırılmış 10 öğeye sahiptir.
Yetti99

14
@JonCav Kodu kırdın. Kod bozuk tanımsız davranış alıyorsunuz.
kaylum

50
Mesele şu ki, tanımsız davranış tam olarak budur. Güvenilir bir şekilde test edemez ve tanımlanan bir şeyin olacağını kanıtlayamazsınız. Muhtemelen Windows makinenizde iolup biten , değişkenin bitiminden hemen sonra depolanması arrayve üzerine yazmanızdır array[10]=0;. Aynı platformda optimize edilmiş bir yapıda durum böyle olmayabilir; bu, ibir kayıt defterinde saklanabilir ve hiçbir zaman belleğe hiç başvurmaz.
Çeltik

46
Çünkü öngörülemezlik Tanımsız Davranışın temel bir özelliğidir. Bunu anlamalısın ... Kesinlikle tüm bahisler kapalı.
çeltik

Yanıtlar:


356

Ubuntu 14.04 çalıştıran dizüstü bilgisayarımda, bu kod tamamlanmayacak şekilde kırılmaz. Okulumun CentOS 6.6 çalıştıran bilgisayarında da iyi çalışıyor. Windows 8.1'de, döngü asla sona ermez.

Daha garip olan şey, fordöngünün koşulunu şu şekilde düzenlediğimde : i <= 11kod sadece Ubuntu çalıştıran dizüstü bilgisayarımda sona eriyor. CentOS ve Windows hiçbir zaman sona ermez.

Az önce hafızanın durduğunu keşfettiniz. Bununla ilgili daha fazla bilgiyi buradan edinebilirsiniz: “Bellek stomp” nedir?

Ayırdığınızda int array[10],i;, bu değişkenler belleğe gider (özellikle işlevle ilişkilendirilmiş bir bellek bloğu olan yığına ayrılır). array[]ve imuhtemelen bellekte birbirine bitişiktir. Görünüşe göre Windows 8.1'de iyer alıyor array[10]. CentOS, iadresinde bulunmaktadır array[11]. Ve Ubuntu'da, hiçbir yerde değil (belki de array[-1]?).

Bu hata ayıklama deyimlerini kodunuza eklemeyi deneyin. 10 veya 11 yinelemede array[i]şu noktayı işaret ettiğine dikkat etmelisiniz 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
Hey teşekkürler! Bu gerçekten biraz açıkladı. Windows'da, 10'u diziden ayırırsanız, hem CentOS hem de Ubuntu'da -1 olduğunu belirtir. Hata ayıklayıcı kodunuzu yorum yaparsam CentOS kodu çalıştıramaz (askıda kalır), ancak hata ayıklama kodunuzla çalışır. C şu ana kadar çok dil gibi görünüyor X_x
JonCav

12
@JonCav array[10], örneğin yığın çerçevesini yok etmek için yazı yazarken "kilitleniyor" olabilir . Hata ayıklama çıktısı olan veya olmayan kod arasında nasıl bir fark olabilir? Adresi varsa igerekli hiçbir zaman, derleyici olabilir optimize iuzağa. böylece kayıttaki bellek düzenini değiştirir ...
Hagen von Eitzen

2
Asılı olduğunu sanmıyorum, çünkü döngü sayacını bellekten yeniden yüklediği için sonsuz bir döngüde olduğunu düşünüyorum (sadece sıfırlandı array[10]=0. Kodunuzu optimizasyon ile derlediyseniz, bu muhtemelen olmazdı. diğer belleklerle üst üste gelme olasılığını sınırlamak için ne tür bellek erişimlerini sınırlayan diğer adlandırma kuralları. bir dizinin tanımlanamayan davranışıdır.Buna bağlı olarak daima kaçınmak için çok çalışın
Peter Cordes

4
Başka bir alternatif de, optimize edici bir derleyicinin (sorunun orijinal kodunda) gözlemlenebilir bir etkisi olmadığı için diziyi tamamen kaldırmasıdır. Bu nedenle, sonuçta elde edilen kod, bu sabit dizgiyi on bir kez yazdırabilir, ardından sabit boyutu yazdırabilir ve böylece taşmayı tamamen farkedilemez hale getirebilir.
Holger

9
@JonCav Ben genel olarak söyleyebilirim yok tanımsız kod yazmak değil bellek yönetimi hakkında daha fazla bilgi gerekiyor ve bunun yerine basitçe biliyorum özellikle ... Bir dizinin sonunun yazmayın
T. Kiley

98

Hata şu kod parçaları arasında yer alır:

int array[10],i;

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

array[i]=0;

Bu yana array, yalnızca 10 elemana sahiptir, son tekrarında array[10] = 0;bir tampon taşma. Arabellek taşmaları tanımlanmamış DAVRANIŞ , yani sabit diskinizi biçimlendirebilir veya şeytanların burnunuzdan uçmasına neden olabilir.

Tüm yığın değişkenlerinin birbirine bitişik olarak yerleştirilmesi oldukça yaygındır. Yazmanın iolduğu yerde bulunursa , array[10]UB sıfırlanır ive 0böylece sonlandırılmamış döngüye yol açar.

Düzeltmek için döngü koşulunu olarak değiştirin i < 10.


6
Nitpick: Kök (veya eşdeğeri) olarak çalışmadığınız sürece sabit sürücüyü piyasadaki herhangi bir akıllı işletim sisteminde biçimlendiremezsiniz.
Kevin

26
@ Kevin, UB'yi çağırdığınızda, akıl sağlığınızla ilgili herhangi bir iddiadan vazgeçersiniz.
o11c

7
Kodunuzun aklı başında olup olmadığı önemli değil. İşletim sistemi bunu yapmanıza izin vermez.
Kevin

2
@Kevin Sabit diskinizi biçimlendirmeyle ilgili örnek bu durumdan çok önce kaynaklanmıştı. Zamanın unix'leri (C kaynaklı) bile böyle şeyler yapmanıza izin vermekten oldukça mutluydu - ve bugün bile, birçok dağıtım, rm -rf /kök olmasanız bile, her şeyi silmeye mutlu bir şekilde izin verecektir. Tabii ki tüm sürücüyü "biçimlendirmek", ancak yine de tüm verilerinizi yok etmek. Ahh.
Luaan

5
@Kevin ancak tanımlanmamış davranış bir OS güvenlik açığından yararlanabilir ve ardından yeni bir sabit disk sürücüsü yüklemek için kendini yükseltebilir ve ardından sürücüyü fırçalamaya başlayabilir.
cırcır ucube

38

Döngünün son çalıştırılmasında ne yazılır array[10], ancak dizide 0 ile 9 arasında numaralandırılmış yalnızca 10 öğe vardır. C dili belirtimi bunun “tanımlanmamış davranış” olduğunu söyler. Uygulamada bunun anlamı, programınızın inthemen bellekte kalan büyük boyutlu bir belleğe yazmaya çalışacağıdır array. O zaman ne olur, aslında orada yatan şeylere bağlıdır ve bu sadece işletim sistemine değil, derleyiciye, derleyici seçeneklerine (optimizasyon ayarları gibi), işlemci mimarisine, çevre koduna bağlıdır. Örneğin adres alanı rasgeleleştirmesi nedeniyle yürütmeden yürütmeye bile değişebilir (muhtemelen bu oyuncak örneğinde değil, ancak gerçek hayatta gerçekleşir). Bazı olasılıklar:

  • Konum kullanılmadı. Döngü normal olarak sona erer.
  • Konum, 0 değerine sahip olan bir şey için kullanıldı. Döngü normal olarak sona erer.
  • Konum, işlevin dönüş adresini içeriyordu. Döngü normal olarak sona erer, ancak program 0 adresine atlamaya çalıştığı için çöker.
  • Konum değişkeni içerir i. Döngü asla sona ermez, çünkü i0'da yeniden başlar.
  • Konum başka bir değişken içeriyor. Döngü normal olarak sona erer, ancak daha sonra “ilginç” şeyler olur.
  • Konum geçersiz bir bellek adresidir, örneğin arraybir sanal bellek sayfasının hemen sonunda olduğundan ve sonraki sayfa eşlenmediğinden.
  • Şeytanlar burnundan uçar . Neyse ki çoğu bilgisayar gerekli donanıma sahip değildir.

Ne Windows üzerinde gözlenen derleyici değişkeni yerleştirmek için karar olmasıydı i, bellekte dizinin hemen sonra öylesine array[10] = 0için atama sona erdi i. Ubuntu ve CentOS'ta derleyici ioraya yerleştirilmedi. Hemen hemen tüm C uygulamaları, bellekteki yerel değişkenleri bir bellek yığınında gruplandırır ve büyük bir istisnadır: bazı yerel değişkenler tamamen kayıtlara yerleştirilebilir . Değişken yığın üzerinde olsa bile, değişkenlerin sırası derleyici tarafından belirlenir ve yalnızca kaynak dosyadaki sıraya değil, aynı zamanda türlerine de bağlı olabilir (delik bırakacak hizalama kısıtlamalarına bellek israfını önlemek için) , isimlerinde, bir derleyicinin dahili veri yapısında kullanılan bazı karma değerlerde vb.

Derleyicinizin ne yapmaya karar verdiğini öğrenmek istiyorsanız, bunu derleyici kodunu göstermesini söyleyebilirsiniz. Oh, ve montajcıyı deşifre etmeyi öğren (yazmaktan daha kolay). GCC (ve özellikle Unix dünyasındaki diğer derleyiciler) ile -Sikili yerine montajcı kodu üretme seçeneğini iletin. Örneğin, burada döngü için derleyici snippet'i, optimizasyon seçeneği -O0(optimizasyon yok) ile amd64 üzerinde GCC ile derleme ve yorumların manuel olarak eklenmesi:

.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

Burada, değişken iyığının üst kısmının altında 52 bayt, dizi ise yığının üstünden 48 bayt altında başlar. Yani bu derleyici idiziden hemen önce yer alıyor; Üzerine yazmak istiyorum isize yazmaya olduysa array[-1]. Eğer değiştirirseniz array[i]=0için array[9-i]=0, bu belirli derleyici seçenekleri ile bu özel platformda sonsuz döngü elde edersiniz.

Şimdi programınızı derleyelim gcc -O1.

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

Bu daha kısa! Derleyici yalnızca bir yığın konumu ayırmayı reddetmedi i- sadece kayıtta saklandı ebx- ancak arrayöğelerini ayarlamak için bellek ayırmak veya kod oluşturmak için uğraşmadı , çünkü öğelerin hiçbirinin hiç kullanılmaz.

Bu örneği daha iyi anlatmak için, derleyiciye optimize edemediği bir şey sağlayarak dizi atamalarının gerçekleştirildiğinden emin olalım. Bunu yapmanın kolay bir yolu, diziyi başka bir dosyadan kullanmaktır - ayrı derleme nedeniyle, derleyici başka bir dosyada ne olduğunu bilmiyor (bağlantı zamanında optimize edilmedikçe, hangisi gcc -O0yoksa veya gcc -O1etmiyorsa). Aşağıdakileri use_array.ciçeren bir kaynak dosya oluşturun

void use_array(int *array) {}

ve kaynak kodunuzu

#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;
}

Şununla derleyin:

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

Bu kez montajcı kodu şöyle görünür:

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

Şimdi dizi yığında, üstten 44 bayt. Ne olmuş i? Hiçbir yerde görünmüyor! Ancak döngü sayacı kayıtta tutulur rbx. Tam olarak ideğil, adresi array[i]. Derleyici, değerinin ihiçbir zaman doğrudan kullanılmadığından, döngünün her çalışması sırasında 0'ın nerede depolanacağını hesaplamak için aritmetik gerçekleştirmenin bir anlamı olmadığına karar verdi . Bunun yerine bu adres döngü değişkendir ve sınırları belirlemek için aritmetik kısmen derleme zamanında gerçekleştirildi (44 elde etmek için 11 yinelemeyi dizi öğesi başına 4 bayt ile çarpın) ve kısmen çalışma zamanında ancak döngü başlamadan önce bir kez ve hepsi için ( başlangıç ​​değerini almak için bir çıkarma işlemi yapın).

Bu çok basit örnekte bile, derleyici seçeneklerini değiştirmenin (optimizasyonu aç) veya küçük bir şeyi değiştirmeyi ( array[i]to array[9-i]) veya hatta görünüşte ilgisiz bir şeyi değiştirmeyi (çağrıyı ekleyerek use_array) yürütülebilir programın oluşturduğu şeyde nasıl önemli bir fark yaratabildiğini gördük. derleyici tarafından yapar. Derleyici optimizasyonları, tanımlanmamış davranışları çağıran programlarda sezgisel görünmeyebilir . Bu yüzden tanımsız davranış tamamen tanımsız bırakılır. Gerçek dünya programlarında, parçalardan çok az saptığınız zaman, deneyimli programcılar için bile kodun ne yaptığı ve ne yapılması gerektiği arasındaki ilişkiyi anlamak çok zor olabilir.


25

Java'nın aksine, C, dizi sınır kontrolü yapmaz, yani, ArrayIndexOutOfBoundsExceptiondizi dizininin geçerli olduğundan emin olma işi programcıya bırakılır. Bunu bilerek yapmak tanımsız davranışa yol açar, her şey olabilir.


Bir dizi için:

int array[10]

endeksleri aralığında geçerlidir 0etmek 9. Ancak, şunları yapmaya çalışıyorsunuz:

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

array[10]buradan erişin , koşulu şu şekilde değiştirin:i < 10


6
Bilerek yapılmaması da tanımsız davranışlara yol açar - derleyici bunu söyleyemez! ;-)
Toby Speight

1
Hatalarınızı uyarı olarak göndermek için bir makro kullanın: #define UNINTENDED_MISTAKE (EXP) printf ("Uyarı:" #EXP "hata \ n");
lkraider

1
Yani, bilerek bir hata yapıyorsanız, bunu böyle tanımlayabilir ve tanımlanmamış davranıştan kaçınmayı güvenli hale getirebilirsiniz; D
lkraider

19

Bir sınır ihlali var ve sonlandırılmayan platformlarda, idöngünün sonunda yanlışlıkla sıfır olarak ayarladığınıza inanıyorum , böylece tekrar başlıyor.

array[10]geçersizdir; 10 elementleri içerir array[0]yoluyla array[9]ve array[10]11'inci olduğunu. Döngünüz daha önce durmak için şu şekilde yazılmalıdır 10:

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

Nerede array[10]topraklar uygulama tanımlı olduğunu ve eğlendirici, senin platformlarının ikisine, bu topraklarda ibu platformlar görünüşte hemen sonra düzenlemek üzere, array. isıfıra ayarlanır ve döngü sonsuza kadar devam eder. Diğer platformlarınız için, idaha önce yerleştirilmiş olabilir arrayveya arraydaha sonra bazı dolgulara sahip olabilir.


Valgrind'in hala geçerli bir yer olduğu için bunu yakalayabileceğini sanmıyorum, ama ASAN yapabilir.
o11c

13

Sen beyan int array[10]araçlar arraydizini vardır 0için 9(toplam 10tutabileceği tamsayı elemanları). Ancak aşağıdaki döngü,

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

şekilde döngü 0için 10araçlar 11zaman. Bu nedenle i = 10, arabellek taşacağı ve Tanımsız Davranışa neden olacağı zaman .

Bu yüzden şunu deneyin:

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

veya,

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

7

Adresinde tanımlanmamıştır array[10]ve daha önce tanımlandığı gibi tanımlanmamış davranış verir . Bunun hakkında böyle düşün:

Alışveriş sepetimde 10 ürün var. Onlar:

0: Bir kutu mısır gevreği
1: Ekmek
2: Süt
3: Pasta
4: Yumurta
5: Kek
6: 2 litre soda
7: Salata
8: Hamburger
9: Dondurma

cart[10]tanımlanmamıştır ve bazı derleyicilerde sınırların dışında bir istisna verebilir. Ama görünüşe göre çok fazla değil. Görünen 11. öğe, aslında alışveriş sepetinde olmayan bir öğedir . 11. madde, ne diyeceğimi "poltergeist madde" ye işaret ediyor. Asla yoktu, ama oradaydı.

Bazı derleyiciler vermek neden ibir dizin array[10]veya array[11]hatta array[-1]çünkü başlatma / bildirim deyiminin olduğunu. Bazı derleyiciler bunu şöyle yorumlamaktadır:

  • "10 blok ints array[10]ve başka bir intblok atayın . Bunu kolaylaştırmak için onları yan yana koyun."
  • Önceki ile aynı, ancak bir veya iki boşluk uzağa taşıyın, bu işaret array[10]etmiyor i.
  • Öncekiyle aynı işlemi yapın, ancak iat array[-1](bir dizinin bir dizini negatif olamaz veya olmamalıdır) veya tamamen farklı bir noktada atayın, çünkü işletim sistemi bunu işleyebilir ve daha güvenlidir.

Bazı derleyiciler işlerin daha hızlı gitmesini isterken bazı derleyiciler güvenliği tercih eder. Her şey bağlamla ilgili. Örneğin, eski BREW OS (bir temel telefonun işletim sistemi) için bir uygulama geliştiriyor olsaydım, güvenlik umursamazdı. Bir iPhone 6 için geliştiriyor olsaydım, ne olursa olsun hızlı çalışabilirdi, bu yüzden güvenliğe önem vermeliydim. (Cidden, Apple'ın App Store Yönergeleri'ni veya Swift ve Swift 2.0'ın geliştirilmesini okudunuz mu?)


Not: Listeyi "0, 1, 2, 3, 4, 5, 6, 7, 8, 9" olacak şekilde yazdım, ancak SO'nun İşaretleme dili sipariş listemin konumlarını düzeltti.
DDPWNAGE

6

10 boyutlu bir dizi oluşturduğunuz için, döngü koşulu aşağıdaki gibi olmalıdır:

int array[10],i;

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

Şu anda, atanmamış konuma kullanarak bellekten erişmeye çalışıyorsunuz array[10]ve tanımlanmamış davranışa neden oluyor . Tanımsız davranış, programınızın belirsiz bir şekilde davranacağı anlamına gelir, böylece her yürütmede farklı çıktılar verebilir.


5

C derleyicisi geleneksel olarak sınırları kontrol etmez. İşleminize "ait olmayan" bir konuma başvurmanız durumunda bir segmentasyon hatası alabilirsiniz. Bununla birlikte, yerel değişkenler yığına tahsis edilir ve belleğin tahsis edilme şekline bağlı olarak, dizinin ( array[10]) hemen ötesindeki alan işlemin bellek segmentine ait olabilir. Böylece, hiçbir segmentasyon hatası tuzağı atılmaz ve deneyimlediğiniz şey budur. Diğerlerinin de belirttiği gibi, bu C'deki tanımlanmamış bir davranıştır ve kodunuz düzensiz olarak kabul edilebilir. C öğrendiğiniz için, kodunuzdaki sınırları kontrol etme alışkanlığına girmeniz daha iyi olur.


4

a[10]Aslında üzerine yazma yazma girişimi için belleğin ortaya konma olasılığının ötesinde, ibir optimizasyon derleyicisinin döngü testine, ikod ilk kez erişilmeden ondan daha büyük bir değerle ulaşılamayacağını belirlemesi de mümkündür . var olmayan dizi öğesi a[10].

Bu öğeye erişme girişimi tanımlanmamış bir davranış olacağından, derleyicinin programın bu noktadan sonra ne yapabileceği konusunda hiçbir yükümlülüğü yoktur. Daha spesifik olarak, derleyicinin ondan büyük olabileceği her durumda döngü dizinini kontrol etmek için kod üretme zorunluluğu olmayacağından, onu kontrol etmek için kod üretme zorunluluğu olmayacaktır; bunun yerine <=10testin her zaman doğru çıkacağını varsayabilir . Kod a[10]yazmak yerine okuyacak olsa bile bunun doğru olacağını unutmayın .


3

Geçmişi yinelediğinizde i==9, aslında dizinin ötesinde bulunan 'dizi öğelerine' sıfır atarsınız , böylece diğer bazı verilerin üzerine yazılırsınız. Büyük olasılıkla i, daha sonra bulunan değişkenin üzerine yazarsınız a[]. Bu şekilde , ideğişkeni sıfıra sıfırlar ve böylece döngüyü yeniden başlatırsınız.

Döngüde yazdırdıysanız bunu kendiniz keşfedebilirsiniz i:

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

sadece yerine

      printf("test \n");

Tabii ki bu sonuç büyük ölçüde değişkenleriniz için bellek tahsisine bağlıdır, bu da bir derleyiciye ve ayarlarına bağlıdır, bu yüzden genellikle Tanımlanmamış Davranıştır - bu yüzden farklı makinelerde veya farklı işletim sistemlerinde veya farklı derleyicilerde sonuçlar farklı olabilir.


0

hata [10] porsiyon dizisi içindedir w / c ayrıca i adresidir (int dizi [10], i;). dizi [10] 0 olarak ayarlandığında, i 0 w / c olur, tüm döngüyü sıfırlar ve sonsuz döngüye neden olur. [10] dizisi 0-10 arasında ise sonsuz döngü olacaktır. doğru döngü (i = 0; i <10; i ++) {...} int dizi [10], i; (i = 0; i <= 10; i ++) dizi [i] = 0;


0

Ben yukarıda bulmak dint bir şey önerecektir:

[İ] = 20 dizisi atamayı deneyin;

Sanırım bu her yerde kodu sonlandırmak .. (i <= 10 veya ll tutmak verilen)

Bu durumda, burada belirtilen cevapların zaten doğru olduğuna kesin olarak karar verebilirsiniz [hafıza için bir tanesinin hafızasıyla ilgili cevap]


-9

Burada yanlış olan iki şey var. İnt i aslında yığında görüldüğü gibi bir dizi öğesi, dizi [10] 'dur. Dizine eklemenin gerçekten dizi [10] = 0 yapmasına izin verdiğiniz için, döngü dizini, i, hiçbir zaman 10'u aşmayacaktır for(i=0; i<10; i+=1).

i ++, K&R'nin dediği gibi 'kötü stil'. Bu, i'yi 1 değil, i boyutuna göre arttırır. İ ++ işaretçi matematik için ve i + = 1 cebir için. Bu derleyiciye bağlı olsa da, taşınabilirlik için iyi bir kural değildir.


5
-1 Tamamen yanlış. Değişken iNOTan dizi elemanı olup a[10]bu yığının üzerine koymak için bir derleyici zorunluluğu ya da öneri yoktur, sonra a[] - bu da bir dizi önce yer, veya ek alanı ile ayrılabilir. Hatta ana belleğin dışında, örneğin bir CPU kaydında da tahsis edilebilir. ++Tamsayılar için değil, işaretçiler için de doğru değildir. Tamamen yanlış 'i ++ i i boyutuna göre artıyor' - dil tanımındaki operatör açıklamasını okuyun!
CiaPan

bu yüzden bazı platformlarda çalışıyor, bazılarında çalışmıyor. neden pencerelerde sonsuza kadar döngülediğinin tek mantıklı açıklamasıdır. I ++ ile ilgili olarak tamsayı değil gösterici matematiktir. Kutsal Yazıları okuyun ... 'C programlama dili'. Kernigan ve Ritche tarafından isterseniz, imzalı bir kopyam var ve 1981'den beri c programlıyoruz.
SkipBerne

1
Kaynak kodunu OP ile okuyun ve değişken bildirimini bulun i- bu inttürdür. Bir tamsayıdır , bir işaretçi değildir; , için bir dizin olarak kullanılan bir tam sayı array.
CiaPan

1
Yaptım ve işte bu yüzden benim gibi yorum yaptım. belki derleyici yığın denetimleri içermez ve bu durumda I = 10 aslında bazı derlemeler, dizi endeksi ve yığın bölge sınırları içinde başvuruyor zaman yığın başvuru olarak önemli olmayacağını fark etmelisiniz. derleyiciler aptal tamir edemez. derleme, bunun göründüğü gibi bir düzeltme yapabilir, ancak c programlama dilinin saf bir yorumu bu kuralı desteklemez ve OP'nin dediği gibi taşınabilir olmayan sonuçlarla sonuçlanır.
SkipBerne

@SkipBerne: Daha olumsuz puanlarla "ödüllendirilmeden" önce yanıtınızı silmeyi düşünün.
Peter VARGA
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.