Mengapa ini untuk loop keluar pada beberapa platform dan bukan pada yang lain?

240

Saya baru-baru ini mulai belajar C dan saya mengambil kelas dengan C sebagai subjek. Saat ini saya bermain-main dengan loop dan saya menjalankan beberapa perilaku aneh yang saya tidak tahu bagaimana menjelaskannya.

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

Di laptop saya yang menjalankan Ubuntu 14.04, kode ini tidak rusak. Ini berjalan sampai selesai. Di komputer sekolah saya yang menjalankan CentOS 6.6, itu juga berjalan dengan baik. Pada Windows 8.1, loop tidak pernah berakhir.

Yang lebih aneh adalah ketika saya mengedit kondisi forloop ke:, i <= 11kode hanya berakhir pada laptop saya yang menjalankan Ubuntu. Itu tidak pernah berakhir di CentOS dan Windows.

Adakah yang bisa menjelaskan apa yang terjadi di memori dan mengapa berbagai OS menjalankan kode yang sama memberikan hasil yang berbeda?

EDIT: Saya tahu loop for keluar dari batas. Saya melakukannya dengan sengaja. Saya tidak tahu bagaimana perilakunya bisa berbeda di berbagai OS dan komputer.

JonCav
sumber
147
Karena Anda men-overrray array maka perilaku yang tidak terdefinisi terjadi. Perilaku yang tidak terdefinisi berarti segala sesuatu dapat terjadi termasuk itu terlihat berfungsi. Dengan demikian "kode tidak boleh berakhir" bukan harapan yang valid.
kaylum
37
Tepat, selamat datang di C. Array Anda memiliki 10 elemen - bernomor 0 hingga 9.
Yetti99
14
@ JonCav Anda telah memecahkan kode. Anda mendapatkan perilaku tidak terdefinisi yang merupakan kode rusak.
kaylum
50
Yah, intinya adalah bahwa perilaku yang tidak terdefinisi persis seperti itu. Anda tidak dapat mengujinya dengan andal dan membuktikan sesuatu yang pasti akan terjadi. Apa yang mungkin terjadi di mesin Windows Anda, adalah bahwa variabel idisimpan tepat setelah akhir array, dan Anda menimpanya array[10]=0;. Ini mungkin bukan kasus di build yang dioptimalkan pada platform yang sama, yang dapat disimpan idalam register dan tidak pernah merujuknya dalam memori sama sekali.
padi
46
Karena tidak dapat diprediksi adalah sifat mendasar dari Perilaku Tidak Terdefinisi. Anda perlu memahami ini ... Benar-benar semua taruhan dibatalkan.
padi

Jawaban:

356

Di laptop saya yang menjalankan Ubuntu 14.04, kode ini tidak berhenti berjalan hingga selesai. Di komputer sekolah saya yang menjalankan CentOS 6.6, itu juga berjalan dengan baik. Pada Windows 8.1, loop tidak pernah berakhir.

Apa yang lebih aneh adalah ketika saya mengedit persyaratan forloop ke:, i <= 11kode hanya berakhir pada laptop saya yang menjalankan Ubuntu. CentOS dan Windows tidak pernah berakhir.

Anda baru saja menemukan memori menghentak. Anda dapat membaca lebih lanjut tentang ini di sini: Apa itu "memori stomp"?

Ketika Anda mengalokasikan int array[10],i;, variabel-variabel tersebut masuk ke memori (khususnya, mereka dialokasikan pada tumpukan, yang merupakan blok memori yang terkait dengan fungsi). array[]dan imungkin berdekatan satu sama lain dalam memori. Tampaknya pada Windows 8.1, iterletak di array[10]. Di CentOS, iterletak di array[11]. Dan di Ubuntu, tidak ada di tempat (mungkin diarray[-1] ?).

Coba tambahkan pernyataan debug ini ke kode Anda. Anda harus memperhatikan bahwa pada iterasi 10 atau 11, array[i]menunjuk pada 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; 
} 
QuestionC
sumber
6
Hai, terima kasih! Itu benar-benar menjelaskan sedikit. Di Windows menyatakan bahwa saya jika mengimbangi 10 dari array, sementara di CentOS dan Ubuntu, itu adalah -1. Yang lebih aneh adalah jika saya mengomentari kode debugger Anda, CentOS tidak dapat menjalankan kode (hang), tetapi dengan kode debug Anda itu berjalan. C tampaknya menjadi bahasa yang sangat jauh X_x
JonCav
12
@JonCav "it hang" dapat terjadi jika menulis untuk array[10]menghancurkan frame stack, misalnya. Bagaimana mungkin ada perbedaan antara kode dengan atau tanpa hasil debug? Jika alamat itidak pernah dibutuhkan, kompiler dapat mengoptimalkannya i. menjadi register, sehingga mengubah tata letak memori pada stack ...
Hagen von Eitzen
2
Saya tidak berpikir itu tergantung, saya pikir itu dalam infinite loop karena itu memuat ulang penghitung loop dari memori (yang baru saja memusatkan perhatian array[10]=0. Jika Anda mengkompilasi kode Anda dengan optimasi, ini mungkin tidak akan terjadi. (Karena C memiliki aliasing aturan yang membatasi jenis akses memori yang harus diasumsikan berpotensi tumpang tindih memori lainnya. Sebagai variabel lokal yang Anda tidak pernah mengambil alamat, saya pikir seorang kompiler harus dapat mengasumsikan bahwa tidak ada yang alias itu. Lagi pula, tuliskan akhirnya array adalah perilaku yang tidak terdefinisi. Selalu berusaha keras untuk menghindari bergantung
Peter Cordes
4
Alternatif lain adalah kompilator yang mengoptimalkan menghapus array sepenuhnya, karena tidak memiliki efek yang dapat diamati (dalam kode asli pertanyaan). Oleh karena itu, kode yang dihasilkan hanya bisa mencetak string konstan itu sebelas kali, diikuti dengan mencetak ukuran konstan dan dengan demikian membuat overflow sama sekali tidak terlihat.
Holger
9
@JonCav Saya akan mengatakan secara umum Anda tidak perlu tahu lebih banyak tentang manajemen memori dan alih-alih hanya tahu untuk tidak menulis kode yang tidak terdefinisi, khususnya, jangan menulis melewati akhir array ...
T. Kiley
98

Bug ini terletak di antara potongan kode ini:

int array[10],i;

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

array[i]=0;

Karena arrayhanya memiliki 10 elemen, dalam iterasi terakhir array[10] = 0;adalah buffer overflow. Buffer overflows adalah PERILAKU YANG TIDAK DIKENAL , yang berarti mereka mungkin memformat hard drive Anda atau menyebabkan setan terbang keluar dari hidung Anda.

Hal ini cukup umum untuk semua variabel stack diletakkan berdekatan satu sama lain. Jika iterletak di mana array[10]menulis, maka UB akan mengatur ulang ike 0, sehingga mengarah ke loop yang tidak ditentukan.

Untuk memperbaikinya, ubah kondisi loop menjadi i < 10.

o11c
sumber
6
Nitpick: Anda sebenarnya tidak dapat memformat hard drive pada OS waras di pasar kecuali Anda menjalankan sebagai root (atau yang setara).
Kevin
26
@Kevin saat Anda memanggil UB, Anda menyerahkan klaim apa pun kepada kewarasan.
o11c
7
Tidak masalah apakah kode Anda waras. OS tidak akan membiarkan Anda melakukan itu.
Kevin
2
@Kevin Contoh dengan memformat hard drive Anda berasal jauh sebelum itu terjadi. Bahkan unix waktu (di mana C berasal) cukup senang memungkinkan Anda untuk melakukan hal-hal seperti itu - dan bahkan hari ini, banyak distro dengan senang hati akan memungkinkan Anda untuk mulai menghapus semuanya dengan rm -rf /bahkan ketika Anda tidak root, tidak "Memformat" seluruh drive tentu saja, tetapi masih menghancurkan semua data Anda. Aduh.
Luaan
5
@Kevin tetapi perilaku yang tidak terdefinisi dapat mengeksploitasi kerentanan OS dan kemudian meninggikan dirinya untuk menginstal driver hard-disk baru dan kemudian mulai menggosok drive.
ratchet freak
38

Dalam apa yang seharusnya menjadi putaran terakhir dari loop, Anda menulis array[10], tetapi hanya ada 10 elemen dalam array, diberi nomor 0 hingga 9. Spesifikasi bahasa C mengatakan bahwa ini adalah "perilaku tidak terdefinisi". Apa artinya ini dalam praktik adalah bahwa program Anda akan mencoba untuk menulis ke intbagian memori yang berukuran yang terletak segera setelah arraydalam memori. Apa yang terjadi kemudian tergantung pada apa yang sebenarnya, terletak di sana, dan ini tidak hanya tergantung pada sistem operasi tetapi lebih pada kompiler, pada opsi kompiler (seperti pengaturan optimisasi), pada arsitektur prosesor, pada kode di sekitarnya. , dll. Bahkan dapat bervariasi dari eksekusi ke eksekusi, misalnya karena mengalamatkan ruang pengalamatan (mungkin tidak pada contoh mainan ini, tetapi hal itu terjadi dalam kehidupan nyata). Beberapa kemungkinan termasuk:

  • Lokasi tidak digunakan. Loop berakhir secara normal.
  • Lokasi itu digunakan untuk sesuatu yang kebetulan memiliki nilai 0. Loop berakhir secara normal.
  • Lokasi berisi alamat pengembalian fungsi. Loop berakhir secara normal, tetapi kemudian program crash karena mencoba untuk melompat ke alamat 0.
  • Lokasi berisi variabel i. Loop tidak pernah berakhir karena irestart pada 0.
  • Lokasi berisi beberapa variabel lain. Loop berakhir secara normal, tetapi hal-hal "menarik" terjadi.
  • Lokasi adalah alamat memori yang tidak valid, misalnya karena arraytepat di akhir halaman memori virtual dan halaman berikutnya tidak dipetakan.
  • Setan terbang keluar dari hidung Anda . Untungnya kebanyakan komputer tidak memiliki perangkat keras yang diperlukan.

Apa yang Anda amati pada Windows adalah bahwa kompiler memutuskan untuk menempatkan variabel isegera setelah array dalam memori, sehingga array[10] = 0akhirnya ditugaskan i. Di Ubuntu dan CentOS, kompiler tidak ditempatkan di isana. Hampir semua implementasi C melakukan pengelompokan variabel lokal dalam memori, pada tumpukan memori , dengan satu pengecualian utama: beberapa variabel lokal dapat ditempatkan sepenuhnya dalam register . Bahkan jika variabel ada di stack, urutan variabel ditentukan oleh kompiler, dan itu mungkin tidak hanya bergantung pada urutan dalam file sumber tetapi juga pada tipenya (untuk menghindari pemborosan memori untuk menyelaraskan batasan yang akan meninggalkan lubang) , pada nama mereka, pada beberapa nilai hash yang digunakan dalam struktur data internal kompiler, dll.

Jika Anda ingin mengetahui apa yang diputuskan oleh kompiler Anda, Anda dapat memerintahkannya untuk menunjukkan kepada Anda kode assembler. Oh, dan belajar menguraikan assembler (lebih mudah daripada menulisnya). Dengan GCC (dan beberapa kompiler lain, terutama di dunia Unix), berikan opsi -Suntuk menghasilkan kode assembler alih-alih biner. Misalnya, inilah cuplikan assembler untuk loop dari kompilasi dengan GCC di amd64 dengan opsi pengoptimalan -O0(tanpa optimasi), dengan komentar ditambahkan secara manual:

.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

Di sini variabelnya iadalah 52 byte di bawah bagian atas stack, sedangkan array dimulai 48 byte di bawah bagian atas stack. Jadi kompiler ini kebetulan telah ditempatkan itepat sebelum array; Anda akan menimpa ijika Anda kebetulan menulis array[-1]. Jika Anda mengubah array[i]=0ke array[9-i]=0, Anda akan mendapatkan sebuah loop tak terbatas pada platform tertentu dengan opsi compiler tertentu.

Sekarang mari kita kompilasi program Anda dengan gcc -O1.

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

Itu lebih pendek! Kompiler tidak hanya menolak untuk mengalokasikan lokasi stack untuk i- itu hanya pernah disimpan dalam register ebx- tetapi juga tidak mau repot untuk mengalokasikan memori apa pun array, atau untuk menghasilkan kode untuk mengatur elemen-elemennya, karena ia menyadari bahwa tidak ada elemen yang pernah digunakan.

Untuk membuat contoh ini lebih jitu, mari pastikan bahwa tugas array dilakukan dengan menyediakan sesuatu yang tidak dapat dioptimalkan oleh kompiler. Cara mudah untuk melakukannya adalah dengan menggunakan array dari file lain - karena kompilasi terpisah, kompiler tidak tahu apa yang terjadi pada file lain (kecuali itu dioptimalkan pada waktu tautan, yang gcc -O0atau gcc -O1tidak). Buat file sumber yang use_array.cberisi

void use_array(int *array) {}

dan ubah kode sumber Anda menjadi

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

Kompilasi dengan

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

Kali ini kode assembler terlihat seperti ini:

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

Sekarang array ada di stack, 44 byte dari atas. Bagaimana dengan i? Itu tidak muncul di mana pun! Tetapi penghitung loop disimpan dalam register rbx. Ini tidak persis i, tetapi alamat array[i]. Compiler telah memutuskan bahwa karena nilai itidak pernah digunakan secara langsung, tidak ada gunanya melakukan aritmatika untuk menghitung di mana menyimpan 0 selama setiap putaran dijalankan. Alih-alih alamat itu adalah variabel loop, dan aritmatika untuk menentukan batas-batas dilakukan sebagian pada waktu kompilasi (kalikan 11 iterasi dengan 4 byte per elemen array untuk mendapatkan 44) dan sebagian saat run time tetapi sekali dan untuk semua sebelum loop dimulai ( lakukan pengurangan untuk mendapatkan nilai awal).

Bahkan pada contoh yang sangat sederhana ini, kita telah melihat bagaimana mengubah opsi kompiler (menghidupkan optimasi) atau mengubah sesuatu yang kecil ( array[i]menjadi array[9-i]) atau bahkan mengubah sesuatu yang tampaknya tidak berhubungan (menambahkan panggilan ke use_array) dapat membuat perbedaan yang signifikan terhadap apa yang dihasilkan oleh program yang dapat dieksekusi yang dihasilkan oleh kompiler tidak. Optimalisasi kompiler dapat melakukan banyak hal yang mungkin tampak tidak intuitif pada program yang menjalankan perilaku tidak terdefinisi . Itu sebabnya perilaku yang tidak terdefinisi dibiarkan sepenuhnya tidak terdefinisi. Ketika Anda menyimpang sedikit dari trek, dalam program dunia nyata, akan sangat sulit untuk memahami hubungan antara apa yang dilakukan kode dan apa yang seharusnya dilakukan, bahkan untuk programmer yang berpengalaman.

Gilles 'SO- berhenti menjadi jahat'
sumber
25

Tidak seperti Java, C tidak melakukan pemeriksaan batas array, yaitu, tidak ada ArrayIndexOutOfBoundsException, tugas memastikan indeks array valid diserahkan kepada programmer. Melakukan ini dengan sengaja menyebabkan perilaku yang tidak terdefinisi, apa pun bisa terjadi.


Untuk sebuah array:

int array[10]

indeks hanya valid dalam kisaran 0hingga 9. Namun, Anda mencoba untuk:

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

akses di array[10]sini, ubah kondisinya menjadii < 10

Yu Hao
sumber
6
Melakukannya secara tidak sengaja juga mengarah pada perilaku yang tidak terdefinisi - kompiler tidak tahu! ;-)
Toby Speight
1
Cukup gunakan makro untuk menampilkan kesalahan Anda sebagai peringatan: #define UNINTENDED_MISTAKE (EXP) printf ("Peringatan:" #EXP "kesalahan \ n");
lkraider
1
Maksud saya, jika Anda melakukan kesalahan dengan sengaja, Anda sebaiknya mengidentifikasinya dan membuatnya aman untuk menghindari perilaku yang tidak terdefinisi; D
lkraider
19

Anda memiliki pelanggaran batas, dan pada platform yang tidak berhenti, saya yakin Anda secara tidak sengaja melakukan pengaturan i ke nol pada akhir loop, sehingga mulai lagi.

array[10]tidak valid; ini berisi 10 elemen, array[0]melalui array[9], dan array[10]ke-11. Pengulangan Anda harus ditulis untuk berhenti sebelum 10 , sebagai berikut:

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

Di mana array[10]lahan ditentukan oleh implementasi, dan secara mengejutkan, pada dua platform Anda, itu akan mendarat i, di mana platform tersebut tampaknya diletakkan langsung setelahnya array. idiatur ke nol dan loop berlanjut selamanya. Untuk platform Anda yang lain, idapat ditemukan sebelumnya array, atau arraymungkin memiliki bantalan setelahnya.

Derek T. Jones
sumber
Saya tidak berpikir valgrind dapat menangkap ini karena masih merupakan lokasi yang valid, tetapi ASAN bisa.
o11c
13

Anda menyatakan int array[10]sarana arraymemiliki indeks 0ke 9( 10elemen integer total yang dapat ditampungnya). Namun loop berikut,

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

akan loop 0untuk 10sarana 11waktu. Karenanya ketika i = 10itu akan meluap buffer dan menyebabkan Perilaku tidak terdefinisi .

Jadi coba ini:

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

atau,

for (i = 0; i <= 9 ; i++)
rakeb.mazharul
sumber
7

Itu tidak terdefinisi pada array[10], dan memberikan perilaku yang tidak terdefinisi seperti yang dijelaskan sebelumnya. Pikirkan itu seperti ini:

Saya memiliki 10 item di troli belanjaan saya. Mereka:

0: Sekotak sereal
1: Roti
2: Susu
3: Pie
4: Telur
5: Kue
6: A 2 liter soda
7: Salad
8: Burger
9: Es krim

cart[10]tidak terdefinisi, dan dapat memberikan pengecualian di luar batas pada beberapa kompiler. Tapi, ternyata banyak yang tidak. Item ke-11 yang terlihat jelas adalah item yang tidak ada di troli. Item ke-11 menunjuk, apa yang akan saya sebut, "item poltergeist." Itu tidak pernah ada, tapi itu ada di sana.

Mengapa beberapa kompiler memberikan iindeks array[10]atau array[11]atau bahkan array[-1]karena pernyataan inisialisasi / deklarasi Anda. Beberapa kompiler menafsirkan ini sebagai:

  • "Alokasikan 10 blok ints untuk array[10]dan satu lagi intblok. Agar lebih mudah, letakkan di sebelah satu sama lain."
  • Sama seperti sebelumnya, tetapi pindahkan satu atau dua spasi, sehingga array[10]tidak menunjuk ke sana i.
  • Lakukan hal yang sama seperti sebelumnya, tetapi alokasikan idi array[-1](karena indeks array tidak dapat, atau tidak boleh, menjadi negatif), atau mengalokasikannya di tempat yang sama sekali berbeda karena OS dapat menanganinya, dan lebih aman.

Beberapa kompiler menginginkan segalanya berjalan lebih cepat, dan beberapa kompiler lebih menyukai keamanan. Ini semua tentang konteksnya. Jika saya mengembangkan aplikasi untuk OS BREW kuno (OS dari telepon dasar), misalnya, itu tidak akan peduli dengan keamanan. Jika saya sedang mengembangkan untuk iPhone 6, maka itu bisa berjalan dengan cepat tidak peduli apa, jadi saya perlu penekanan pada keamanan. (Serius, sudahkah Anda membaca Panduan App Store Apple, atau membaca tentang pengembangan Swift dan Swift 2.0?)

DDPWNAGE
sumber
Catatan: Saya mengetik daftar jadi "0, 1, 2, 3, 4, 5, 6, 7, 8, 9", tetapi bahasa Markup SO memperbaiki posisi daftar yang saya pesan.
DDPWNAGE
6

Karena Anda membuat array ukuran 10, untuk kondisi loop harus sebagai berikut:

int array[10],i;

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

Saat ini Anda mencoba mengakses lokasi yang tidak ditetapkan dari memori menggunakan array[10]dan itu menyebabkan perilaku yang tidak ditentukan . Perilaku tidak terdefinisi berarti program Anda akan berperilaku dengan cara yang tidak ditentukan, sehingga dapat memberikan output yang berbeda dalam setiap eksekusi.

Steephen
sumber
5

Nah, kompiler C secara tradisional tidak memeriksa batasan. Anda bisa mendapatkan kesalahan segmentasi jika Anda merujuk ke lokasi yang bukan "milik" proses Anda. Namun, variabel lokal dialokasikan pada stack dan tergantung pada cara memori dialokasikan, area tepat di luar array ( array[10]) mungkin milik segmen memori proses. Dengan demikian, tidak ada perangkap segmentasi kesalahan yang dilemparkan dan itulah yang tampaknya Anda alami. Seperti yang ditunjukkan orang lain, ini adalah perilaku yang tidak terdefinisi dalam C dan kode Anda mungkin dianggap tidak menentu. Karena Anda belajar C, Anda lebih baik membiasakan diri memeriksa batas-batas dalam kode Anda.

unxnut
sumber
4

Di luar kemungkinan memori dapat diletakkan sehingga upaya menulis untuk a[10]benar - benar menimpa i, mungkin juga kompilator pengoptimal mungkin menentukan bahwa tes loop tidak dapat dicapai dengan nilai ilebih dari sepuluh tanpa kode setelah pertama kali mengakses elemen array tidak ada a[10].

Karena upaya untuk mengakses elemen itu akan menjadi perilaku yang tidak terdefinisi, kompiler tidak akan memiliki kewajiban sehubungan dengan apa yang mungkin dilakukan program setelah titik itu. Lebih khusus, karena kompiler tidak memiliki kewajiban untuk menghasilkan kode untuk memeriksa indeks loop dalam hal apa pun di mana itu mungkin lebih besar dari sepuluh, itu tidak memiliki kewajiban untuk menghasilkan kode untuk memeriksanya sama sekali; alih-alih bisa berasumsi bahwa <=10tes akan selalu menghasilkan yang benar. Perhatikan bahwa ini akan benar bahkan jika kode akan membaca a[10]daripada menulisnya.

supercat
sumber
3

Ketika Anda beralih melewati i==9Anda menetapkan nol ke 'item array' yang sebenarnya terletak melewati array , sehingga Anda menimpa beberapa data lainnya. Kemungkinan besar Anda menimpa ivariabel, yang terletak setelah a[]. Dengan begitu Anda cukup mereset ivariabel ke nol dan dengan demikian restart loop.

Anda dapat menemukan sendiri jika Anda mencetak idi loop:

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

bukannya adil

      printf("test \n");

Tentu saja hasil yang sangat tergantung pada alokasi memori untuk variabel Anda, yang pada gilirannya tergantung pada kompiler dan pengaturannya, sehingga umumnya Perilaku tidak terdefinisi - itu sebabnya hasil pada mesin yang berbeda atau sistem operasi yang berbeda atau pada kompiler yang berbeda mungkin berbeda.

CiaPan
sumber
0

kesalahannya adalah dalam array sebagian [10] w / c juga alamat dari i (int array [10], i;). ketika array [10] diatur ke 0 maka i akan menjadi 0 w / c me-reset seluruh loop dan menyebabkan infinite loop. akan ada infinite loop jika array [10] adalah antara 0-10. loop yang benar seharusnya untuk (i = 0; i <10; i ++) {...} int array [10], i; untuk array (i = 0; i <= 10; i ++) [i] = 0;

Jonelle H. Castaneda
sumber
0

Saya akan menyarankan sesuatu yang saya tidak temukan di atas:

Coba tetapkan array [i] = 20;

Saya kira ini harus mengakhiri kode di mana-mana .. (mengingat Anda tetap saya <= 10 atau ll)

Jika ini berjalan, Anda dapat dengan tegas memutuskan bahwa jawaban yang ditentukan di sini sudah benar [jawaban yang terkait dengan memori menginjak satu untuk mantan.]

Hujan api
sumber
-9

Ada dua hal yang salah di sini. Int i sebenarnya adalah elemen array, array [10], seperti yang terlihat pada stack. Karena Anda telah memungkinkan pengindeksan untuk benar-benar membuat array [10] = 0, indeks loop, i, tidak akan pernah melebihi 10. Buatlah for(i=0; i<10; i+=1).

i ++ adalah, as K&R menyebutnya, 'gaya buruk'. Ini bertambah i dengan ukuran i, bukan 1. i ++ untuk pointer matematika dan i + = 1 untuk aljabar. Meskipun ini tergantung pada kompiler, itu bukan konvensi yang baik untuk portabilitas.

SkipBerne
sumber
5
-1 Benar-benar salah. Variabel iBUKAN elemen array a[10], tidak ada kewajiban atau bahkan saran bagi kompiler untuk meletakkannya di stack segera setelahnya a[] - ia juga dapat ditemukan sebelum array, atau dipisahkan dengan beberapa ruang tambahan. Bahkan dapat dialokasikan di luar memori utama, misalnya dalam register CPU. Itu juga tidak benar ++untuk pointer dan bukan untuk integer. Benar-benar salah adalah 'i ++ menambah saya dengan ukuran i' - baca deskripsi operator dalam definisi bahasa!
CiaPan
itulah sebabnya ia bekerja pada beberapa platform dan bukan yang lain. itu adalah satu-satunya penjelasan yang masuk akal mengapa itu berulang selamanya di windows. berkaitan dengan I ++ itu adalah pointer matematika bukan bilangan bulat. baca Kitab Suci ... 'bahasa pemrograman C'. oleh Kernigan dan Ritche, jika Anda mau, saya memiliki salinan bertanda tangan, dan telah diprogram dalam c sejak 1981.
SkipBerne
1
Baca kode sumber dengan OP dan temukan deklarasi variabel i- ini intbertipe. Ini adalah bilangan bulat , bukan pointer; integer, digunakan sebagai indeks untuk array,.
CiaPan
1
Saya melakukannya dan itulah sebabnya saya berkomentar seperti saya. mungkin Anda harus menyadari bahwa kecuali kompilator menyertakan pemeriksaan tumpukan dan dalam hal ini tidak masalah sebagai referensi tumpukan ketika I = 10 sebenarnya akan menjadi referensi, dalam beberapa kompilasi, indeks array dan yang berada dalam batas-batas wilayah tumpukan. kompiler tidak bisa memperbaiki yang bodoh. kompilasi mungkin membuat perbaikan seperti yang terlihat ini, tetapi interpretasi murni dari bahasa pemrograman c tidak akan mendukung konvensi ini dan akan seperti yang dikatakan OP menghasilkan hasil yang tidak portabel.
SkipBerne
@ SkipBerne: Pertimbangkan untuk menghapus jawaban Anda sebelum Anda akan "diberikan" dengan lebih banyak poin negatif.
Peter VARGA