Ketika program C sedang berjalan, data disimpan di heap atau stack. Nilai disimpan dalam alamat RAM. Tetapi bagaimana dengan indikator jenis (misalnya, int
atau char
)? Apakah mereka juga disimpan?
Pertimbangkan kode berikut:
char a = 'A';
int x = 4;
Saya membaca bahwa A dan 4 disimpan dalam alamat RAM di sini. Tapi bagaimana dengan a
dan x
? Yang paling membingungkan, bagaimana eksekusi tahu itu a
char dan x
int? Maksud saya, apakah int
dan char
disebutkan di suatu tempat di RAM?
Katakanlah nilai disimpan di suatu tempat di RAM sebagai 10011001; jika saya adalah program yang mengeksekusi kode, bagaimana saya tahu apakah 10011001 ini adalah char
atau tidak int
?
Yang tidak saya mengerti adalah bagaimana komputer tahu, ketika membaca nilai variabel dari alamat seperti 10001, apakah itu sebuah int
atau char
. Bayangkan saya klik pada program yang disebut anyprog.exe
. Segera kode mulai dijalankan. Apakah file yang dapat dieksekusi ini menyertakan informasi apakah variabel yang disimpan adalah tipe int
atau char
?
x
merupakan char, tetapi itu adalah kode char-printing yang dijalankan, karena itulah yang dipilih kompilator.Jawaban:
Untuk menjawab pertanyaan yang telah Anda posting di beberapa komentar (yang saya pikir Anda harus mengedit posting Anda):
Jadi mari kita menaruh beberapa kode padanya. Katakanlah Anda menulis:
Dan mari kita asumsikan bahwa itu disimpan dalam RAM:
Bagian pertama adalah alamat, bagian kedua adalah nilai. Ketika program Anda (yang dijalankan sebagai kode mesin) berjalan, yang dilihatnya
0x00010004
hanyalah nilainya0x000000004
. Itu tidak 'tahu' jenis data ini, dan tidak tahu bagaimana 'seharusnya' digunakan.Jadi, bagaimana program Anda mengetahui hal yang benar untuk dilakukan? Pertimbangkan kode ini:
Kami telah membaca dan menulis di sini. Ketika program Anda membaca
x
dari memori, ia menemukan di0x00000004
sana. Dan program Anda tahu untuk menambahkannya0x00000005
. Dan alasan program Anda 'tahu' ini adalah operasi yang valid, karena kompiler memastikan bahwa operasi itu valid melalui tipe-safety. Kompiler Anda telah memverifikasi bahwa Anda dapat menambahkan4
dan5
bersama - sama. Jadi ketika kode biner Anda berjalan (exe), itu tidak harus melakukan verifikasi itu. Itu hanya mengeksekusi setiap langkah secara membabi buta, dengan asumsi semuanya baik-baik saja (hal-hal buruk terjadi ketika mereka sebenarnya, bukan OK).Cara lain untuk memikirkannya adalah seperti ini. Saya memberi Anda informasi ini:
Format yang sama seperti sebelumnya - alamat di sebelah kiri, nilai di sebelah kanan. Jenis apa nilainya? Pada titik ini, Anda tahu sebanyak mungkin informasi tentang nilai itu seperti halnya komputer Anda saat menjalankan kode. Jika saya mengatakan kepada Anda untuk menambahkan 12743 ke nilai itu, Anda bisa melakukannya. Anda tidak tahu apa akibat dari operasi itu pada keseluruhan sistem, tetapi menambahkan dua angka adalah sesuatu yang benar-benar Anda kuasai, sehingga Anda bisa melakukannya. Apakah itu membuat nilai menjadi
int
? Tidak harus - Yang Anda lihat adalah dua nilai 32-bit dan operator tambahan.Mungkin beberapa kebingungan kemudian mendapatkan kembali data. Jika kita memiliki:
Bagaimana komputer tahu untuk ditampilkan
a
di konsol? Ya, ada banyak langkah untuk itu. Yang pertama adalah pergi keA
lokasi di memori dan membacanya:Nilai hex untuk
a
di ASCII adalah 0x61, jadi di atas mungkin sesuatu yang akan Anda lihat di memori. Jadi sekarang kode mesin kami tahu nilai integer. Bagaimana cara mengetahui nilai integer menjadi karakter untuk menampilkannya? Sederhananya, kompiler memastikan untuk memasukkan semua langkah yang diperlukan untuk melakukan transisi itu. Tetapi komputer Anda sendiri (atau program / exe) tidak tahu apa tipe data itu. Nilai 32-bit itu bisa berupa apa saja -int
,,char
setengah daridouble
, pointer, bagian dari array, bagian daristring
, bagian dari instruksi, dll.Berikut adalah interaksi singkat yang mungkin dimiliki program Anda (exe) dengan komputer / sistem operasi.
Program: Saya ingin memulai. Saya membutuhkan memori 20 MB.
Sistem Operasi: menemukan 20 MB memori gratis yang tidak digunakan dan menyerahkannya
(Catatan penting adalah bahwa ini bisa kembali setiap 20 MB memori, mereka bahkan tidak harus berdekatan. Pada titik ini, program ini sekarang dapat beroperasi dalam memori itu tanpa berbicara dengan OS)
Program: Saya akan berasumsi bahwa tempat pertama dalam memori adalah variabel integer 32-bit
x
.(Kompiler memastikan bahwa akses ke variabel lain tidak akan pernah menyentuh tempat ini dalam memori. Tidak ada pada sistem yang mengatakan byte pertama adalah variabel
x
, atau variabel itux
adalah bilangan bulat. Analogi: Anda memiliki tas. Anda memberi tahu orang-orang bahwa Anda hanya akan memasukkan bola berwarna kuning ke dalam tas ini. Ketika seseorang kemudian menarik sesuatu keluar dari tas, maka akan mengejutkan bahwa mereka akan mengeluarkan sesuatu yang berwarna biru atau kubus - sesuatu yang sangat salah terjadi. Hal yang sama berlaku untuk komputer: Anda: Program sekarang mengasumsikan tempat memori pertama adalah variabel x dan itu adalah bilangan bulat.Jika sesuatu yang lain pernah ditulis di atas byte memori ini atau diasumsikan sebagai sesuatu yang lain - sesuatu yang mengerikan telah terjadi.Kompiler memastikan hal-hal semacam ini tidak akan terjadi)Program: Sekarang saya akan menulis
2
ke empat byte pertama di mana saya berasumsix
berada di.Program: Saya ingin menambahkan 5
x
.Membaca nilai X menjadi register sementara
Menambahkan 5 ke register sementara
Menyimpan nilai register sementara kembali ke byte pertama, yang masih dianggap
x
.Program: Saya akan menganggap byte yang tersedia berikutnya adalah variabel char
y
.Program: Saya akan menulis
a
ke variabely
.Pustaka digunakan untuk menemukan nilai byte untuk
a
Byte ditulis ke alamat yang diasumsikan oleh program
y
.Program: Saya ingin menampilkan konten
y
Membaca nilai di tempat memori kedua
Menggunakan perpustakaan untuk mengkonversi dari byte ke karakter
Menggunakan pustaka grafis untuk mengubah layar konsol (mengatur piksel dari hitam ke putih, menggulir satu baris, dll)
(Dan itu berlanjut dari sini)
Yang mungkin membuat Anda terpaku adalah - apa yang terjadi ketika titik pertama dalam ingatan tidak lagi
x
? atau yang kedua tidak lagiy
? Apa yang terjadi ketika seseorang membacax
sebagaichar
atauy
sebagai penunjuk? Singkatnya, hal-hal buruk terjadi. Beberapa dari hal-hal ini memiliki perilaku yang jelas, dan beberapa memiliki perilaku yang tidak terdefinisi. Perilaku yang tidak terdefinisi adalah persis seperti itu - apa pun dapat terjadi, mulai dari tidak ada sama sekali, hingga crash program atau sistem operasi. Bahkan perilaku yang didefinisikan dengan baik bisa berbahaya. Jika saya dapat mengubahx
menjadi pointer ke program saya, dan membuat program Anda menggunakannya sebagai pointer, maka saya bisa membuat program Anda untuk mulai menjalankan program saya - yang persis seperti yang dilakukan peretas. Kompiler ada untuk membantu memastikan kita tidak menggunakanint x
sebagaistring
, dan hal-hal semacam itu. Kode mesin itu sendiri tidak mengetahui tipe, dan hanya akan melakukan apa yang diperintahkan oleh instruksi. Ada juga sejumlah besar informasi yang ditemukan pada saat run-time: byte memori manakah yang diperbolehkan untuk digunakan oleh program? Apakahx
dimulai pada byte pertama atau ke-12?Tetapi Anda dapat membayangkan betapa mengerikannya menulis program seperti ini (dan Anda bisa, dalam bahasa majelis). Anda memulai dengan 'mendeklarasikan' variabel Anda - Anda memberi tahu diri sendiri bahwa byte 1 adalah
x
, byte 2 adalahy
, dan saat Anda menulis setiap baris kode, memuat dan menyimpan register, Anda (sebagai manusia) harus mengingat yang manax
dan mana satuy
, karena sistem tidak tahu. Dan Anda (sebagai manusia) harus mengingat tipex
dany
apa, karena sekali lagi - sistem tidak tahu.sumber
Otherwise how can console or text file outputs a character instead of int
Karena ada urutan instruksi yang berbeda untuk mengeluarkan konten lokasi memori sebagai integer atau sebagai karakter alfanumerik. Kompilator mengetahui tentang tipe variabel, dan memilih urutan instruksi yang sesuai pada waktu kompilasi, dan mencatatnya dalam EXE.Saya pikir pertanyaan utama Anda tampaknya adalah: "Jika jenis itu dihapus pada waktu kompilasi dan tidak disimpan pada saat runtime, lalu bagaimana komputer tahu apakah untuk mengeksekusi kode yang menafsirkannya sebagai
int
atau untuk mengeksekusi kode yang menafsirkannya sebagaichar
? "Dan jawabannya adalah ... komputer tidak. Namun, kompiler memang tahu, dan itu hanya akan menempatkan kode yang benar dalam biner di tempat pertama. Jika variabel diketik sebagai
char
, maka kompiler tidak akan memasukkan kode untuk memperlakukannya sebagaiint
dalam program, itu akan menempatkan kode untuk memperlakukannya adalah achar
.Ada yang alasan untuk mempertahankan jenis saat runtime:
+
operator), sehingga tidak perlu tipe runtime untuk alasan itu. Namun, sekali lagi, tipe runtime adalah sesuatu yang berbeda dengan tipe statis, misalnya di Jawa, Anda secara teoritis dapat menghapus tipe statis dan tetap menyimpan tipe runtime untuk polimorfisme. Perhatikan juga, bahwa jika Anda mendesentralisasikan dan mengkhususkan kode jenis-pencarian dan memasukkannya ke dalam objek (atau kelas), maka Anda juga tidak perlu tipe runtime, misalnya C ++ vtables.Satu-satunya alasan untuk menjaga jenis saat runtime di C adalah untuk debugging, namun, debugging biasanya dilakukan dengan sumber yang tersedia, dan kemudian Anda cukup mencari jenis dalam file sumber.
Jenis Penghapusan cukup normal. Itu tidak memengaruhi keamanan tipe: tipe-tipe tersebut diperiksa pada waktu kompilasi, setelah kompiler puas bahwa programnya aman-tipe, tipe-tipe itu tidak lagi diperlukan (karena alasan itu). Ini tidak memengaruhi polimorfisme statis (alias kelebihan beban): begitu resolusi kelebihan beban selesai, dan kompiler telah mengambil kelebihan yang tepat, tidak perlu jenis lagi. Jenis juga dapat memandu pengoptimalan, tetapi sekali lagi, setelah pengoptimal memilih pengoptimalannya berdasarkan pada jenisnya, pengoptimal tidak memerlukannya lagi.
Mempertahankan tipe saat runtime hanya diperlukan saat Anda ingin melakukan sesuatu dengan tipe saat runtime.
Haskell adalah salah satu bahasa yang diketik secara statis, paling ketat, paling ketat, dan jenis-aman, dan kompiler Haskell biasanya menghapus semua jenis. (Pengecualian adalah lewatnya kamus metode untuk kelas tipe, saya percaya.)
sumber
char
ke dalam biner yang dikompilasi. Tidak output kode untukint
, tidak output kode untukbyte
, itu tidak output kode untuk pointer, itu hanya output hanya kode untukchar
. Tidak ada keputusan runtime yang dibuat berdasarkan jenisnya. Anda tidak perlu tipenya. Ini sama sekali dan sama sekali tidak relevan. Semua keputusan yang relevan telah dibuat pada waktu kompilasi.public class JoergsAwesomeNewType {};
Lihat? Saya baru saja menemukan tipe baru! Anda perlu membeli CPU baru!Komputer tidak "tahu" alamat apa itu apa, tetapi pengetahuan tentang apa yang dimasukkan ke dalam instruksi program Anda.
Ketika Anda menulis program C yang menulis dan membaca variabel char, kompiler membuat kode assembly yang menulis sepotong data di suatu tempat sebagai char, dan ada beberapa kode lain di tempat lain yang membaca alamat memori dan menafsirkannya sebagai char. Satu-satunya hal yang mengikat kedua operasi ini bersama-sama adalah lokasi dari alamat memori itu.
Ketika tiba saatnya untuk membaca, instruksi tidak mengatakan "lihat tipe data apa yang ada", itu hanya mengatakan sesuatu seperti "memuat memori itu sebagai pelampung". Jika alamat yang akan dibaca telah diubah, atau sesuatu telah menimpa memori itu dengan sesuatu selain float, CPU akan dengan senang hati memuat memori itu sebagai float, dan segala macam hal aneh dapat terjadi sebagai hasilnya.
Waktu analogi yang buruk: bayangkan gudang pengiriman yang rumit, di mana gudang tersebut adalah memori dan orang-orang yang memilih barang adalah CPU. Salah satu bagian dari 'program' gudang menempatkan berbagai barang di rak. Program lain berjalan dan mengambil barang dari gudang dan memasukkannya ke dalam kotak. Ketika mereka ditarik, mereka tidak diperiksa, mereka hanya pergi ke tempat sampah. Seluruh gudang berfungsi dengan segala sesuatu yang bekerja secara sinkron, dengan barang yang tepat selalu berada di tempat yang tepat pada waktu yang tepat, jika tidak semuanya macet, seperti dalam program yang sebenarnya.
sumber
Tidak. Setelah C dikompilasi ke kode mesin, mesin hanya melihat banyak bit. Bagaimana bit-bit tersebut diinterpretasikan tergantung pada operasi apa yang sedang dilakukan pada mereka yang bertentangan dengan beberapa metadata tambahan.
Jenis yang Anda masukkan dalam kode sumber Anda hanya untuk kompiler. Dibutuhkan jenis apa yang Anda katakan data seharusnya dan, dengan kemampuan terbaiknya, mencoba memastikan bahwa data hanya digunakan dengan cara yang masuk akal. Setelah kompiler melakukan pekerjaan sebaik mungkin dalam memeriksa logika kode sumber Anda, ia mengkonversinya menjadi kode mesin, dan membuang data tipe, karena kode mesin tidak memiliki cara untuk menyatakan hal itu (setidaknya pada kebanyakan mesin) .
sumber
int a = 65
danchar b = 'A'
setelah kode dikompilasi.Sebagian besar prosesor memberikan instruksi berbeda untuk bekerja dengan data dari tipe yang berbeda, sehingga informasi tipe biasanya "dimasukkan" ke kode mesin yang dihasilkan. Tidak perlu menyimpan metadata tipe tambahan.
Beberapa contoh nyata mungkin bisa membantu. Kode mesin di bawah ini dihasilkan menggunakan gcc 4.1.2 pada sistem x86_64 yang menjalankan SuSE Linux Enterprise Server (SLES) 10.
Asumsikan kode sumber berikut:
Inilah daging kode perakitan yang dihasilkan sesuai dengan sumber di atas (menggunakan
gcc -S
), dengan komentar yang ditambahkan oleh saya:Ada beberapa hal tambahan yang mengikuti
ret
, tetapi tidak relevan dengan diskusi.%eax
adalah register data tujuan umum 32-bit.%rsp
adalah register 64-bit yang disediakan untuk menyimpan penunjuk tumpukan , yang berisi alamat benda terakhir yang didorong ke tumpukan.%rbp
adalah register 64-bit yang disediakan untuk menyimpan pointer bingkai , yang berisi alamat frame stack saat ini . Frame stack dibuat pada stack ketika Anda memasukkan fungsi, dan itu menghemat ruang untuk argumen fungsi dan variabel lokal. Argumen dan variabel diakses dengan menggunakan offset dari frame pointer. Dalam hal ini, memori untuk variabelx
adalah 12 byte "di bawah" alamat yang disimpan%rbp
.Dalam kode di atas, kami menyalin nilai integer
x
(1, disimpan di-12(%rbp)
) ke register%eax
menggunakanmovl
instruksi, yang digunakan untuk menyalin kata 32-bit dari satu lokasi ke lokasi lain. Kami kemudian memanggiladdl
, yang menambahkan nilai integery
(disimpan di-8(%rbp)
) ke nilai yang sudah ada di%eax
. Kami kemudian menyimpan hasilnya-4(%rbp)
, yaituz
.Sekarang mari kita ubah itu jadi kita berurusan dengan
double
nilai alih-alihint
nilai:Berlari
gcc -S
lagi memberi kita:Beberapa perbedaan. Alih-alih
movl
danaddl
, kita menggunakanmovsd
danaddsd
(menetapkan dan menambahkan mengapung presisi ganda). Alih-alih menyimpan nilai sementara%eax
, kami menggunakan%xmm0
.Inilah yang saya maksud ketika saya mengatakan bahwa jenisnya "dipanggang" ke kode mesin. Kompiler hanya menghasilkan kode mesin yang tepat untuk menangani jenis tertentu.
sumber
Secara historis , C menganggap memori terdiri dari sejumlah kelompok slot bernomor jenis
unsigned char
(juga disebut "byte", meskipun tidak harus selalu 8 bit). Kode apa pun yang menggunakan apa pun yang tersimpan dalam memori perlu mengetahui slot atau slot mana informasi itu disimpan, dan tahu apa yang harus dilakukan dengan informasi di sana [misalnya "menafsirkan empat byte mulai dari alamat 123: 456 sebagai 32-bit nilai floating-point "atau" menyimpan 16 bit yang lebih rendah dari kuantitas yang paling baru dihitung menjadi dua byte mulai dari alamat 345: 678]. Memori itu sendiri tidak akan tahu atau tidak peduli apa nilai-nilai yang disimpan dalam slot memori "berarti". Jika kode mencoba menulis memori menggunakan satu jenis dan membacanya sebagai yang lain, pola bit yang disimpan oleh tulis akan ditafsirkan sesuai dengan aturan jenis kedua, dengan konsekuensi apa pun yang mungkin terjadi.Misalnya, jika kode disimpan
0x12345678
ke 32-bitunsigned int
, dan kemudian mencoba membaca duaunsigned int
nilai 16-bit berturut-turut dari alamatnya dan yang di atas, maka tergantung pada setengah dari yangunsigned int
disimpan di mana, kode mungkin membaca nilai-nilai 0x1234 dan 0x5678, atau 0x5678 dan 0x1234.Standar C99, bagaimanapun, tidak lagi mensyaratkan bahwa memori berperilaku sebagai sekelompok slot bernomor yang tidak tahu apa-apa tentang apa yang mewakili pola bit mereka . Kompiler diperbolehkan berperilaku seolah-olah slot memori mengetahui tipe data yang disimpan di dalamnya, dan hanya akan memungkinkan data yang ditulis menggunakan jenis apa pun selain
unsigned char
dibaca menggunakan tipeunsigned char
atau tipe yang sama seperti yang ditulis dengan; kompiler lebih lanjut diperbolehkan berperilaku seolah-olah slot memori memiliki kekuatan dan kecenderungan untuk secara sewenang-wenang merusak perilaku setiap program yang mencoba mengakses memori dengan cara yang bertentangan dengan aturan-aturan tersebut.Diberikan:
beberapa implementasi mungkin mencetak 0x1234, dan yang lain mungkin mencetak 0x5678, tetapi di bawah Standar C99 itu akan sah untuk implementasi untuk mencetak "FRINK ATURAN!" atau melakukan hal lain, berdasarkan teori yang sah untuk lokasi memori yang
a
menyertakan perangkat keras yang mencatat jenis apa yang digunakan untuk menulisnya, dan untuk perangkat keras tersebut merespons upaya baca yang tidak valid dengan cara apa pun, termasuk dengan menyebabkan "PERATURAN ATURAN!" menjadi output.Perhatikan bahwa tidak masalah jika perangkat keras semacam itu benar-benar ada - fakta bahwa perangkat keras semacam itu dapat secara hukum ada membuatnya legal bagi kompiler untuk menghasilkan kode yang berperilaku seolah-olah itu berjalan pada sistem seperti itu. Jika kompilator dapat menentukan bahwa lokasi memori tertentu akan ditulis sebagai satu jenis dan dibaca sebagai yang lain, ia dapat berpura-pura bahwa itu berjalan pada sistem yang perangkat kerasnya dapat membuat tekad tersebut, dan dapat merespons dengan tingkat kesukaran apa pun yang menurut pembuat kompiler cocok. .
Tujuan dari aturan ini adalah untuk memungkinkan kompiler yang tahu bahwa sekelompok byte yang memegang nilai dari suatu jenis memegang nilai tertentu pada suatu titik waktu, dan bahwa tidak ada nilai dari jenis yang sama yang telah ditulis sejak itu, untuk menyimpulkan bahwa kelompok itu byte akan tetap memiliki nilai itu. Sebagai contoh, sebuah prosesor telah membaca sekelompok byte ke dalam register, dan kemudian ingin menggunakan informasi yang sama lagi ketika masih dalam register, kompiler dapat menggunakan konten register tanpa harus membaca ulang nilai dari memori. Pengoptimalan yang bermanfaat. Selama sekitar sepuluh tahun pertama dari aturan, melanggar itu umumnya berarti bahwa jika variabel ditulis dengan tipe selain yang digunakan untuk membacanya, penulisan mungkin atau mungkin tidak mempengaruhi nilai baca. Perilaku seperti itu dalam beberapa kasus dapat menjadi bencana, tetapi dalam kasus lain mungkin tidak berbahaya,
Sekitar tahun 2009, bagaimanapun, penulis dari beberapa kompiler seperti CLANG telah menentukan bahwa karena Standar memungkinkan kompiler untuk melakukan apapun yang mereka suka dalam kasus di mana memori ditulis menggunakan satu jenis dan dibaca sebagai yang lain, kompiler harus menyimpulkan bahwa program tidak akan pernah menerima input yang dapat menyebabkan hal seperti itu terjadi. Karena Standar mengatakan bahwa kompiler diperbolehkan untuk melakukan apa pun yang disukainya ketika input yang tidak valid tersebut diterima, kode yang hanya akan memiliki efek dalam kasus-kasus di mana Standar tidak memaksakan persyaratan dapat (dan dalam pandangan beberapa penulis kompiler, harus) dihilangkan. tidak relevan. Ini mengubah perilaku aliasing pelanggaran dari menjadi seperti memori yang, dengan permintaan baca, dapat secara sewenang-wenang mengembalikan nilai terakhir yang ditulis menggunakan jenis yang sama dengan permintaan baca atau nilai terbaru lainnya yang ditulis menggunakan jenis lain,
sumber
int x,y,z;
ekspresix*y > z
tidak akan pernah melakukan apa pun selain mengembalikan 1 atau 0, atau di mana pelanggaran aliasing akan berdampak apa pun selain membiarkan kompiler secara sewenang-wenang mengembalikan nilai lama atau baru.unsigned char
nilai yang digunakan untuk membangun tipe "berasal". Jika suatu program menguraikan pointer menjadiunsigned char[]
, tunjukkan konten hex-nya secara singkat di layar, dan kemudian hapus pointer, theunsigned char[]
, dan kemudian terima beberapa angka hex dari keyboard, salin kembali ke sebuah pointer, dan kemudian referensi pointer itu , perilaku akan didefinisikan dengan baik dalam kasus di mana nomor yang diketik cocok dengan nomor yang ditampilkan.Di C, tidak. Bahasa lain (misalnya, Lisp, Python) memiliki tipe dinamis tetapi C diketik secara statis. Itu berarti bahwa program Anda harus tahu tipe data apa yang ditafsirkan dengan benar adalah sebagai karakter, bilangan bulat, dll.
Biasanya kompiler menangani ini untuk Anda, dan jika Anda melakukan sesuatu yang salah, Anda akan mendapatkan kesalahan waktu kompilasi (atau peringatan).
sumber
10001
. Baik pekerjaan Anda atau pekerjaan kompiler , tergantung kasus, untuk mengikuti hal-hal seperti itu secara manual saat menulis mesin atau kode perakitan.Anda harus membedakan antara
compiletime
danruntime
di satu sisi dancode
dandata
di sisi lain.Dari perspektif mesin, tidak ada perbedaan antara apa yang Anda panggil
code
atauinstructions
dan apa yang Anda panggildata
. Semuanya bermuara pada angka. Tetapi beberapa urutan - apa yang kita sebutcode
- melakukan sesuatu yang kita temukan berguna, yang lain hanyacrash
mesin.Pekerjaan yang dilakukan oleh CPU adalah loop 4 langkah sederhana:
instruction
)Ini disebut siklus instruksi .
a
danx
adalah variabel, yang merupakan penampung untuk alamat, di mana program dapat menemukan "konten" dari variabel. Jadi, kapan saja variabela
digunakan, secara efektif ada alamat dari konten yanga
digunakan.Eksekusi tidak tahu apa-apa. Dari apa yang dikatakan dalam pendahuluan, CPU hanya mengambil data dan menafsirkan data ini sebagai instruksi.
Fungsi printf dirancang untuk "tahu", input apa yang Anda masukkan ke dalamnya, yaitu kode yang dihasilkannya memberikan instruksi yang tepat bagaimana menangani segmen memori khusus. Tentu saja, dimungkinkan untuk menghasilkan keluaran nonsense: menggunakan alamat, di mana tidak ada string yang disimpan bersama dengan "% s"
printf()
akan menghasilkan output nonsense dihentikan hanya oleh lokasi memori acak, di mana 0 (\0
) adalah.Hal yang sama berlaku untuk titik masuk suatu program. Di bawah C64 dimungkinkan untuk menempatkan program Anda di (hampir) setiap alamat yang diketahui. Program Assembly dimulai dengan instruksi yang disebut
sys
diikuti oleh alamat:sys 49152
adalah tempat umum untuk meletakkan kode assembler Anda. Tapi tidak ada yang mencegah Anda dari memuat data grafis misalnya49152
, mengakibatkan mesin crash setelah "mulai" dari titik ini. Dalam hal ini, siklus instruksi dimulai dengan membaca "data grafis" dan mencoba menafsirkannya sebagai "kode" (yang tentu saja tidak masuk akal); efeknya terkadang mengejutkan;)Seperti yang dikatakan: "Konteks" - yaitu instruksi sebelumnya dan selanjutnya - membantu merawat data dengan cara yang kita inginkan. Dari perspektif mesin, tidak ada perbedaan dalam lokasi memori.
int
danchar
hanya kosa kata, yang masuk akalcompiletime
; selamaruntime
(pada tingkat perakitan), tidak adachar
atauint
.Komputer tidak tahu apa-apa. The programmer tidak. Kode yang dikompilasi menghasilkan konteks , yang diperlukan untuk menghasilkan hasil yang bermakna bagi manusia.
Ya dan Tidak . Informasi, apakah itu suatu
int
atau yangchar
hilang. Tetapi di sisi lain, konteks (instruksi yang memberi tahu, bagaimana menangani lokasi memori, di mana data disimpan) dipertahankan; jadi secara implisit ya, "informasi" tersedia secara tersirat .sumber
Mari kita bahas diskusi ini hanya dalam bahasa C.
Program yang Anda maksud ditulis dalam bahasa tingkat tinggi seperti C. Komputer hanya mengerti bahasa mesin. Bahasa tingkat yang lebih tinggi memberi programmer kemampuan untuk mengekspresikan logika dengan cara yang lebih ramah manusia yang kemudian diterjemahkan ke dalam kode mesin yang dapat didekodekan dan dieksekusi oleh mikroprosesor. Sekarang mari kita bahas kode yang Anda sebutkan:
Mari kita coba menganalisis setiap bagian:
Jadi pengenal tipe data int / char hanya digunakan oleh kompiler dan bukan oleh mikroprosesor selama eksekusi program. Karenanya mereka tidak disimpan dalam memori.
sumber
Jawaban saya di sini agak disederhanakan dan hanya akan merujuk ke C.
Tidak, ketikkan informasi tidak disimpan dalam program.
int
atauchar
tidak mengetik indikator ke CPU; hanya ke kompiler.Exe yang dibuat oleh kompiler akan memiliki instruksi untuk memanipulasi
int
s jika variabel dideklarasikan sebagaiint
. Demikian juga, jika variabel dideklarasikan sebagai achar
, exe akan berisi instruksi untuk memanipulasi achar
.Dalam C:
Program ini akan mencetak pesannya, karena
char
danint
memiliki yang sama nilai-nilai dalam RAM.Sekarang, jika Anda bertanya-tanya bagaimana
printf
mengelola output65
untukint
danA
untukchar
, itu karena Anda harus menentukan dalam "format string" bagaimanaprintf
seharusnya memperlakukan nilainya .(Misalnya,
%c
berarti memperlakukan nilai sebagai achar
, dan%d
berarti memperlakukan nilai sebagai bilangan bulat; nilai yang sama bagaimanapun juga.)sumber
printf
. @OP:int a = 65; printf("%c", a)
akan menampilkan'A'
. Mengapa? Karena prosesornya tidak peduli. Untuk itu, yang dilihatnya adalah bit. Program Anda memberi tahu prosesor untuk menyimpan 65 (secara kebetulan nilai'A'
dalam ASCII) padaa
dan kemudian menampilkan karakter, yang dengan senang hati melakukannya. Mengapa? Karena tidak peduli.Pada level terendah, dalam CPU fisik yang sebenarnya tidak ada tipe sama sekali (mengabaikan unit floating point). Hanya pola bit. Komputer bekerja dengan memanipulasi pola bit, sangat, sangat cepat.
Itu semua yang pernah dilakukan CPU, semua bisa dilakukan. Tidak ada yang namanya int, atau char.
Akan dieksekusi sebagai:
Instruksi iadd memicu perangkat keras yang berperilaku seolah register 1 dan 2 adalah bilangan bulat. Jika mereka tidak benar-benar mewakili bilangan bulat, semua hal bisa salah nanti. Hasil terbaik biasanya menabrak.
Ada di kompiler untuk memilih instruksi yang benar berdasarkan jenis yang diberikan dalam sumber, tetapi dalam kode mesin yang sebenarnya dieksekusi oleh CPU, tidak ada jenis, di mana saja.
sunting: Perhatikan bahwa kode mesin yang sebenarnya tidak menyebutkan 4, atau 5, atau bilangan bulat di mana saja. itu hanya dua pola bit, dan instruksi yang mengambil dua pola bit, mengasumsikan mereka int, dan menambahkannya bersama-sama.
sumber
Jawaban singkatnya, tipe ini dikodekan dalam instruksi CPU yang dihasilkan oleh kompiler.
Meskipun informasi tentang jenis atau ukuran informasi tidak disimpan secara langsung, kompiler tetap melacak informasi ini ketika mengakses, memodifikasi, dan menyimpan nilai dalam variabel-variabel ini.
Tidak, tetapi ketika kompiler menghasilkan kode mesin, ia tahu. An
int
dan achar
bisa dari berbagai ukuran. Dalam arsitektur di mana char adalah ukuran byte dan int adalah 4 byte, maka variabelx
tidak ada di alamat 10001, tetapi juga di 10002, 10003 dan 10004. Ketika kode perlu memuat nilaix
ke dalam register CPU, menggunakan instruksi untuk memuat 4 byte. Saat memuat char, ia menggunakan instruksi untuk memuat 1 byte.Bagaimana cara memilih mana dari dua instruksi tersebut? Compiler memutuskan selama kompilasi, itu tidak dilakukan saat runtime setelah memeriksa nilai-nilai dalam memori.
Perhatikan juga bahwa register dapat memiliki ukuran yang berbeda. Pada CPU Intel x86 EAX adalah lebar 32 bit, setengahnya adalah AX, yaitu 16, dan AX dibagi menjadi AH dan AL, keduanya 8 bit.
Jadi jika Anda ingin memuat integer (pada CPU x86), Anda menggunakan instruksi MOV untuk integer, untuk memuat char Anda menggunakan instruksi MOV untuk chars. Keduanya disebut MOV, tetapi mereka memiliki kode op yang berbeda. Secara efektif menjadi dua instruksi yang berbeda. Jenis variabel dikodekan dalam instruksi yang digunakan.
Hal yang sama terjadi dengan operasi lain. Ada banyak instruksi untuk melakukan penambahan, tergantung pada ukuran operan, dan bahkan jika itu ditandatangani atau tidak ditandatangani. Lihat https://en.wikipedia.org/wiki/ADD_(x86_instruction) yang berisi daftar kemungkinan penambahan yang berbeda.
Pertama, char adalah 10011001, tetapi int adalah 00000000 00000000 00000000 10011001, karena ukurannya berbeda (pada komputer dengan ukuran yang sama seperti yang disebutkan di atas). Tapi mari kita pertimbangkan kasus untuk
signed char
vsunsigned char
.Apa yang disimpan di lokasi memori dapat diartikan pula sesuai keinginan Anda. Bagian dari tanggung jawab kompiler C adalah untuk memastikan bahwa apa yang disimpan dan dibaca dari suatu variabel dilakukan secara konsisten. Jadi bukan karena program itu tahu apa yang tersimpan di lokasi memori, tetapi ia setuju sebelumnya bahwa ia akan selalu membaca dan menulis hal-hal yang sama di sana. (tidak termasuk hal-hal seperti tipe casting).
sumber
Dalam bahasa yang diperiksa tipe seperti C #, pemeriksaan tipe dilakukan oleh kompiler. Kode benji menulis:
Hanya menolak untuk mengkompilasi. Demikian pula jika Anda mencoba mengalikan string dan integer (saya akan mengatakan add, tetapi operator '+' kelebihan beban dengan penggabungan string dan mungkin hanya bekerja).
Kompiler hanya akan menolak untuk menghasilkan kode mesin dari C # ini, tidak peduli seberapa banyak string Anda menciumnya.
sumber
Jawaban lainnya benar karena pada dasarnya setiap perangkat konsumen yang Anda temui tidak menyimpan informasi jenis. Namun, ada beberapa desain perangkat keras di masa lalu (dan hari ini, dalam konteks penelitian) yang menggunakan arsitektur yang ditandai - mereka menyimpan data dan jenisnya (dan mungkin juga informasi lainnya). Ini paling menonjol termasuk mesin Lisp .
Samar-samar saya ingat pernah mendengar tentang arsitektur perangkat keras yang dirancang untuk pemrograman berorientasi objek yang memiliki sesuatu yang serupa, tetapi saya tidak dapat menemukannya sekarang.
sumber