Saya telah meninjau pemrograman C dan hanya ada beberapa hal yang mengganggu saya.
Mari kita ambil kode ini sebagai contoh:
int myArray[5] = {1, 2, 2147483648, 4, 5};
int* ptr = myArray;
int i;
for(i=0; i<5; i++, ptr++)
printf("\n Element %d holds %d at address %p", i, myArray[i], ptr);
Saya tahu bahwa int dapat memiliki nilai maksimum positif 2.147.483.647. Jadi dengan pergi satu ke itu, apakah itu "tumpah" ke alamat memori berikutnya yang menyebabkan elemen 2 muncul sebagai "-2147483648" di alamat itu? Tapi kemudian itu tidak masuk akal karena dalam output masih mengatakan bahwa alamat berikutnya memegang nilai 4, lalu 5. Jika nomor tersebut telah tumpah ke alamat berikutnya maka tidak akan mengubah nilai yang disimpan di alamat itu ?
Saya samar-samar ingat dari pemrograman di Majelis MIPS dan menonton alamat mengubah nilai selama program langkah demi langkah bahwa nilai yang ditugaskan ke alamat tersebut akan berubah.
Kecuali jika saya salah mengingat maka berikut adalah pertanyaan lain: Jika nomor yang ditugaskan ke alamat tertentu lebih besar dari jenisnya (seperti di myArray [2]) maka apakah itu tidak mempengaruhi nilai yang disimpan di alamat berikutnya?
Contoh: Kami memiliki int myNum = 4 miliar di alamat 0x10010000. Tentu saja myNum tidak dapat menyimpan 4 miliar sehingga muncul sebagai angka negatif di alamat itu. Meskipun tidak dapat menyimpan jumlah besar ini, itu tidak berpengaruh pada nilai yang disimpan di alamat berikutnya 0x10010004. Benar?
Alamat memori hanya memiliki ruang yang cukup untuk menampung ukuran angka / karakter tertentu, dan jika ukurannya melampaui batas maka akan direpresentasikan secara berbeda (seperti mencoba menyimpan 4 miliar ke int tetapi akan muncul sebagai angka negatif) dan sehingga tidak berpengaruh pada angka / karakter yang tersimpan di alamat selanjutnya.
Maaf jika saya berlebihan. Saya sudah memiliki otak besar kentut sepanjang hari dari ini.
sumber
int c = INT.MAXINT; c+=1;
dan lihat apa yang terjadi pada c.Jawaban:
Tidak. Di C, variabel memiliki set alamat memori yang tetap untuk dikerjakan. Jika Anda bekerja pada sistem dengan 4-byte
ints
, dan Anda mengaturint
variabel untuk2,147,483,647
kemudian menambahkan1
, variabel biasanya akan berisi-2147483648
. (Pada kebanyakan sistem. Perilaku ini sebenarnya tidak terdefinisi.) Tidak ada lokasi memori lain yang akan dimodifikasi.Intinya, kompiler tidak akan membiarkan Anda menetapkan nilai yang terlalu besar untuk tipe tersebut. Ini akan menghasilkan kesalahan kompiler. Jika Anda memaksakannya menggunakan case, nilainya akan terpotong.
Dilihat dengan cara bitwise, jika jenisnya hanya dapat menyimpan 8 bit, dan Anda mencoba untuk memaksa nilai
1010101010101
ke dalamnya dengan case, Anda akan berakhir dengan 8 bit terbawah, atau01010101
.Dalam contoh Anda, apa pun yang Anda lakukan
myArray[2]
,myArray[3]
akan berisi '4'. Tidak ada "tumpahan". Anda mencoba untuk meletakkan sesuatu yang lebih dari 4-byte, itu hanya akan memotong segala sesuatu di ujung yang tinggi, meninggalkan bagian bawah 4 byte. Pada kebanyakan sistem, ini akan menghasilkan-2147483648
.Dari sudut pandang praktis, Anda ingin memastikan ini tidak pernah terjadi. Jenis luapan seperti ini sering mengakibatkan cacat yang sulit dipecahkan. Dengan kata lain, jika Anda berpikir ada peluang sama sekali nilai Anda akan mencapai miliaran, jangan gunakan
int
.sumber
Overflow integer yang ditandatangani adalah perilaku yang tidak terdefinisi. Jika ini terjadi, program Anda tidak valid. Kompiler tidak diharuskan untuk memeriksa ini untuk Anda, sehingga dapat menghasilkan executable yang tampaknya melakukan sesuatu yang masuk akal, tetapi tidak ada jaminan bahwa itu akan dilakukan.
Namun, integer overflow unsigned didefinisikan dengan baik. Ini akan membungkus modulo UINT_MAX +1. Memori yang tidak ditempati oleh variabel Anda tidak akan terpengaruh.
Lihat juga https://stackoverflow.com/q/18195715/951890
sumber
int
. Kurasa mereka bisa menggunakan kode Gray atau BCD atau EBCDIC . Entah mengapa ada orang yang mendesain perangkat keras untuk melakukan aritmatika dengan kode Gray atau EBCDIC, tapi sekali lagi, saya tidak tahu mengapa ada orang yang melakukanunsigned
dengan biner dan menandatanganiint
dengan apa pun selain komplemen 2's.Jadi, ada dua hal di sini:
Di tingkat bahasa:
Dalam C:
Bagi mereka yang menginginkan contoh "apa pun", saya telah melihat:
berubah menjadi:
dan ya, ini adalah transformasi yang sah.
Ini berarti bahwa memang ada potensi risiko menimpa memori pada overflow karena beberapa transformasi kompiler yang aneh.
Catatan: pada penggunaan Dentang atau gcc
-fsanitize=undefined
di Debug untuk mengaktifkan Sanitizer Perilaku Tidak Terdefinisi yang akan batal pada saat underflow / overflow dari bilangan bulat yang ditandatangani.Atau itu berarti Anda bisa menimpa memori dengan menggunakan hasil operasi untuk mengindeks (tidak dicentang) ke dalam array. Sayangnya ini jauh lebih mungkin terjadi tanpa adanya deteksi underflow / overflow.
Catatan: pada penggunaan Dentang atau gcc
-fsanitize=address
di Debug untuk mengaktifkan Sanitizer Alamat yang akan membatalkan akses di luar batas.Di tingkat mesin :
Itu sangat tergantung pada instruksi perakitan dan CPU yang Anda gunakan:
Add
:Perhatikan bahwa apakah hal-hal terjadi dalam register atau memori, dalam kedua kasus CPU tidak menimpa memori pada overflow.
sumber
Untuk menjawab lebih lanjut @ StevenBurnap, alasan ini terjadi adalah karena cara kerja komputer pada level mesin.
Array Anda disimpan dalam memori (misalnya dalam RAM). Ketika operasi aritmatika dilakukan, nilai dalam memori disalin ke register input dari rangkaian yang melakukan aritmatika (ALU: Unit Logika Aritmatika ), operasi kemudian dilakukan pada data dalam register input, menghasilkan hasil dalam register keluaran. Hasil ini kemudian disalin kembali ke memori pada alamat yang benar dalam memori, meninggalkan area memori lain yang tidak tersentuh.
sumber
Pertama (dengan asumsi standar C99), Anda mungkin ingin memasukkan
<stdint.h>
header standar dan menggunakan beberapa jenis yang didefinisikan di sana, terutamaint32_t
yang merupakan integer bertanda 32 bit, atauuint64_t
yang persis integer 64 bit tanpa tanda, dan sebagainya. Anda mungkin ingin menggunakan tipe sepertiint_fast16_t
untuk alasan kinerja.Baca jawaban lain yang menjelaskan bahwa aritmatika yang tidak ditandatangani tidak pernah tumpah (atau meluap) ke lokasi memori yang berdekatan. Waspadalah terhadap perilaku tidak terdefinisi pada luapan yang ditandatangani .
Kemudian, jika Anda perlu untuk menghitung persis angka integer besar (misalnya Anda ingin menghitung faktorial 1000 dengan semua 2568 digit dalam desimal), Anda ingin bigints alias nomor presisi sewenang-wenang (atau bignums). Algoritma untuk aritmatika bigint yang efisien sangat cerdas, dan biasanya membutuhkan penggunaan instruksi mesin khusus (misalnya beberapa kata tambah dengan carry, jika prosesor Anda memilikinya). Karenanya saya sangat menyarankan dalam hal ini untuk menggunakan beberapa perpustakaan bigint yang ada seperti GMPlib
sumber