Apakah deklarator tipe data seperti "int" dan "char" disimpan dalam RAM ketika program C dijalankan?

74

Ketika program C sedang berjalan, data disimpan di heap atau stack. Nilai disimpan dalam alamat RAM. Tetapi bagaimana dengan indikator jenis (misalnya, intatau 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 adan x? Yang paling membingungkan, bagaimana eksekusi tahu itu achar dan xint? Maksud saya, apakah intdan chardisebutkan 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 charatau tidak int?

Yang tidak saya mengerti adalah bagaimana komputer tahu, ketika membaca nilai variabel dari alamat seperti 10001, apakah itu sebuah intatau 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 intatau char?

pengguna16307
sumber
24
Informasi ini benar-benar hilang pada saat run-time. Anda (dan kompiler Anda) harus memastikan sebelumnya bahwa memori akan ditafsirkan dengan benar. Apakah ini jawaban yang Anda cari?
5gon12eder
4
Tidak. Karena mengasumsikan bahwa Anda tahu apa yang Anda lakukan, dibutuhkan apa pun yang ditemukan di alamat memori yang Anda berikan, dan menulisnya ke stdout. Jika apa pun yang ditulis sesuai dengan karakter yang dapat dibaca, pada akhirnya akan muncul di konsol seseorang sebagai karakter yang dapat dibaca. Jika tidak sesuai, itu akan muncul sebagai omong kosong, atau mungkin karakter yang dapat dibaca secara acak.
Robert Harvey
22
@ user16307 Jawaban singkatnya adalah bahwa dalam bahasa yang diketik secara statis, setiap kali Anda mencetak char, kompiler akan menghasilkan kode yang berbeda dari yang akan dicetak pada int. Saat runtime tidak ada lagi pengetahuan yang xmerupakan char, tetapi itu adalah kode char-printing yang dijalankan, karena itulah yang dipilih kompilator.
Ixrec
13
@ user16307 Itu selalu disimpan sebagai representasi biner dari angka 65. Apakah itu dicetak sebagai 65 atau sebagai A tergantung pada kode yang dihasilkan kompiler Anda untuk mencetaknya. Tidak ada metadata di sebelah 65 yang mengatakan itu sebenarnya char atau int (setidaknya, tidak dalam bahasa yang diketik secara statis seperti C).
Ixrec
2
Yang sepenuhnya memahami konsep yang Anda tanyakan di sini dan menerapkannya sendiri, Anda mungkin ingin mengambil kursus kompiler, misalnya yang coursera
mucaho

Jawaban:

122

Untuk menjawab pertanyaan yang telah Anda posting di beberapa komentar (yang saya pikir Anda harus mengedit posting Anda):

Apa yang saya tidak mengerti adalah bagaimana komputer tahu memungkinkan ketika membaca nilai variabel dari dan alamat seperti 10001 jika int atau char. Bayangkan saya klik pada program yang disebut anyprog.exe. Segera kode mulai dijalankan. Apakah file exe ini menyertakan informasi tentang apakah variabel disimpan sebagai atau karakter?

Jadi mari kita menaruh beberapa kode padanya. Katakanlah Anda menulis:

int x = 4;

Dan mari kita asumsikan bahwa itu disimpan dalam RAM:

0x00010004: 0x00000004

Bagian pertama adalah alamat, bagian kedua adalah nilai. Ketika program Anda (yang dijalankan sebagai kode mesin) berjalan, yang dilihatnya 0x00010004hanyalah nilainya 0x000000004. 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:

int x = 4;
x = x + 5;

Kami telah membaca dan menulis di sini. Ketika program Anda membaca xdari memori, ia menemukan di 0x00000004sana. Dan program Anda tahu untuk menambahkannya 0x00000005. 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 menambahkan 4dan 5bersama - 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:

0x00000004: 0x12345678

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:

char A = 'a';

Bagaimana komputer tahu untuk ditampilkan adi konsol? Ya, ada banyak langkah untuk itu. Yang pertama adalah pergi ke Alokasi di memori dan membacanya:

0x00000004: 0x00000061

Nilai hex untuk adi 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,, charsetengah dari double, pointer, bagian dari array, bagian dari string, 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 itu xadalah 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 2ke empat byte pertama di mana saya berasumsi xberada 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 ake variabel y.

  • 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 lagi y? Apa yang terjadi ketika seseorang membaca xsebagai charatau ysebagai 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 mengubah xmenjadi 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 menggunakan int xsebagaistring, 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? Apakah xdimulai 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 adalah y, dan saat Anda menulis setiap baris kode, memuat dan menyimpan register, Anda (sebagai manusia) harus mengingat yang mana xdan mana satu y, karena sistem tidak tahu. Dan Anda (sebagai manusia) harus mengingat tipe xdan yapa, karena sekali lagi - sistem tidak tahu.

Shaz
sumber
Penjelasan luar biasa. Hanya bagian yang Anda tulis, "Bagaimana ia tahu mengubah nilai integer menjadi karakter untuk menampilkannya? Sederhananya, kompiler memastikan untuk memasukkan semua langkah yang diperlukan untuk melakukan transisi itu." masih berkabut bagi saya. Katakanlah CPU mengambil 0x00000061 dari register RAM. Dari titik ini apakah Anda mengatakan ada instruksi lain (dalam file exe) yang melakukan transisi ke apa yang kita lihat di layar?
user16307
2
@ user16307 ya, ada instruksi tambahan. Setiap baris kode yang Anda tulis berpotensi diubah menjadi banyak instruksi. Ada instruksi untuk mencari tahu karakter apa yang digunakan, ada instruksi untuk mengubah piksel dan warna apa yang mereka ubah, dll. Ada juga kode yang tidak benar-benar Anda lihat. Misalnya, menggunakan std :: cout berarti Anda menggunakan perpustakaan. Kode Anda untuk menulis ke konsol mungkin hanya satu baris, tetapi fungsi yang Anda panggil akan lebih banyak baris, dan setiap baris dapat berubah menjadi banyak instruksi mesin.
Shaz
8
@ user16307 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.
Charles E. Grant
2
Saya akan menemukan frasa yang berbeda untuk "Kode byte itu sendiri," sebagai kode byte (atau bytecode) biasanya merujuk ke bahasa perantara (seperti Java Bytecode atau MSIL), yang mungkin sebenarnya menyimpan data ini untuk runtime untuk memanfaatkan. Plus, tidak sepenuhnya jelas apa yang dimaksud dengan "kode byte" dalam konteks itu. Kalau tidak, jawaban yang bagus.
jpmc26
6
@ user16307 Cobalah untuk tidak khawatir tentang C ++ dan C #. Apa yang orang-orang katakan adalah jauh di atas pemahaman Anda saat ini tentang bagaimana komputer dan kompiler bekerja. Untuk tujuan apa yang Anda coba pahami, perangkat keras TIDAK tahu apa-apa tentang tipe, karakter, atau int, atau apa pun. Ketika Anda memberi tahu kompiler bahwa beberapa variabel adalah int, itu menghasilkan kode yang dapat dieksekusi untuk menangani lokasi memori SEBAGAI JIKA itu int. Lokasi memori itu sendiri tidak mengandung info tentang jenis; hanya saja program Anda memutuskan untuk memperlakukannya sebagai int. Lupakan semua yang Anda dengar tentang informasi jenis runtime.
Andres F.
43

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 intatau untuk mengeksekusi kode yang menafsirkannya sebagai char? "

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 sebagai intdalam program, itu akan menempatkan kode untuk memperlakukannya adalah a char.

Ada yang alasan untuk mempertahankan jenis saat runtime:

  • Pengetikan Dinamis: dalam pengetikan dinamis, pengecekan tipe terjadi saat runtime, jadi, jelas, tipe tersebut harus diketahui saat runtime. Tetapi C tidak diketik secara dinamis, sehingga jenisnya dapat dihapus dengan aman. (Perhatikan bahwa ini adalah skenario yang sangat berbeda. Jenis Dinamis dan Tipe Statis tidak benar-benar sama, dan dalam bahasa mengetik campuran, Anda masih bisa menghapus tipe statis dan hanya menyimpan tipe dinamis.)
  • Dynamic Polymorphism: jika Anda mengeksekusi kode yang berbeda berdasarkan tipe runtime, maka Anda perlu menjaga tipe runtime tetap ada. C tidak memiliki polimorfisme dinamis (tidak memiliki polimorfisme sama sekali, sungguh, kecuali dalam beberapa kasus hard-coded khusus, misalnya +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.
  • Runtime Reflection: jika Anda mengizinkan program untuk merefleksikan tipenya pada saat runtime, maka Anda jelas perlu menyimpan tipenya pada saat runtime. Anda dapat dengan mudah melihat ini dengan Java, yang membuat tipe orde pertama saat runtime, tetapi menghapus argumen tipe ke tipe generik pada waktu kompilasi, jadi Anda hanya dapat merefleksikan konstruktor tipe ("tipe mentah") tetapi bukan argumen tipe. Sekali lagi, C tidak memiliki refleksi runtime, jadi tidak perlu menyimpan tipe saat runtime.

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.)

Jörg W Mittag
sumber
3
Tidak! Mengapa? Untuk apa informasi itu dibutuhkan? Kompiler mengeluarkan kode untuk membaca a charke dalam biner yang dikompilasi. Tidak output kode untuk int, tidak output kode untuk byte, itu tidak output kode untuk pointer, itu hanya output hanya kode untuk char. 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.
Jörg W Mittag
2
Tidak ada. Kompiler hanya meletakkan kode untuk mencetak char di dalam biner. Titik. Kompiler tahu bahwa pada alamat memori itu, ada char, oleh karena itu ia meletakkan kode untuk mencetak char di dalam biner. Jika nilai di alamat memori itu untuk beberapa alasan aneh kebetulan bukan char, maka, well, semua neraka lepas. Itulah dasarnya bagaimana seluruh kelas eksploitasi keamanan bekerja.
Jörg W Mittag
2
Pikirkan tentang hal ini: jika CPU entah bagaimana tahu tentang tipe data program, maka semua orang di planet ini harus membeli CPU baru setiap kali seseorang menemukan tipe baru. public class JoergsAwesomeNewType {};Lihat? Saya baru saja menemukan tipe baru! Anda perlu membeli CPU baru!
Jörg W Mittag
9
Tidak. Kompiler tahu kode apa yang harus dimasukkan ke dalam biner. Tidak ada gunanya menyimpan informasi ini. Jika Anda mencetak sebuah int, kompiler akan memasukkan kode untuk mencetak sebuah int. Jika Anda mencetak char, kompiler akan memasukkan kode untuk mencetak char. Titik. Tapi itu hanya sedikit pola. Kode untuk mencetak karakter akan menafsirkan pola bit dengan cara tertentu, kode untuk mencetak int akan menafsirkan bit dengan cara yang berbeda, tetapi tidak ada cara untuk membedakan pola bit yang merupakan int dari pola bit yang adalah char, itu serangkaian bit.
Jörg W Mittag
2
@ user16307: "File exe tidak termasuk informasi tentang alamat apa tipe data apa?" Mungkin. Jika Anda mengompilasi dengan data debug, data debug akan mencakup informasi tentang nama variabel, alamat, dan jenis. Dan kadang-kadang data debug disimpan dalam file .exe (sebagai aliran biner). Tetapi ini bukan bagian dari kode yang dapat dieksekusi, dan tidak digunakan oleh aplikasi itu sendiri, hanya oleh debugger.
Ben Voigt
12

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.

Apa namanya
sumber
bagaimana Anda menjelaskan jika CPU menemukan 0x00000061 pada register dan mengambilnya; dan bayangkan program konsol seharusnya menampilkan ini sebagai karakter bukan int. maksud Anda bahwa dalam file exe ada beberapa kode instruksi yang mengetahui alamat 0x00000061 adalah char dan mengkonversi ke karakter dengan menggunakan tabel ASCII?
user16307
7
Perhatikan bahwa "semuanya macet" sebenarnya adalah skenario terbaik. "Hal-hal aneh terjadi" adalah skenario terbaik kedua, "hal-hal aneh terjadi" bahkan lebih buruk, dan kasus terburuk adalah "hal-hal terjadi di belakang Anda bahwa seseorang sengaja memanipulasi untuk terjadi persis seperti yang mereka inginkan", alias mengeksploitasi keamanan.
Jörg W Mittag
@ user16307: Kode dalam program akan memberi tahu komputer untuk mengambil alamat itu kemudian menampilkannya sesuai dengan pengkodean apa pun yang sedang digunakan. Apakah data di lokasi memori adalah karakter ASCII atau sampah lengkap, komputer tidak peduli. Sesuatu yang lain bertanggung jawab untuk mengatur alamat memori tersebut agar memiliki nilai yang diharapkan di dalamnya. Saya pikir mungkin bermanfaat bagi Anda untuk mencoba beberapa program perakitan.
whatsisname
1
@ JörgWMittag: memang. Saya berpikir tentang menyebutkan buffer overflow sebagai contoh tetapi memutuskan itu hanya akan membuat hal-hal lebih membingungkan.
whatsisname
@ user16307: Hal yang menampilkan data ke layar adalah program. Pada unixen tradisional itu adalah terminal (perangkat lunak yang mengemulasi terminal serial DEC VT100 - perangkat keras dengan monitor dan keyboard yang menampilkan apa pun yang masuk ke modemnya ke monitor dan mengirimkan apa pun yang diketikkan pada keyboard ke modem). Pada DOS itu DOS (sebenarnya mode teks kartu VGA Anda tetapi mari abaikan itu) dan pada Windows itu command.com. Program Anda tidak tahu bahwa itu benar-benar mencetak string, hanya mencetak urutan byte (angka).
slebetman
8

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) .

8bree
sumber
Apa yang saya tidak mengerti adalah bagaimana komputer tahu memungkinkan ketika membaca nilai variabel dari dan alamat seperti 10001 jika int atau char. Bayangkan saya klik pada program yang disebut anyprog.exe. Segera kode mulai dijalankan. Apakah file exe ini menyertakan informasi tentang apakah variabel disimpan sebagai atau karakter? -
user16307
@ user16307 Tidak, tidak ada informasi tambahan tentang apakah ada sesuatu yang int atau char. Saya akan menambahkan beberapa contoh barang nanti, dengan asumsi tidak ada orang lain yang mengalahkan saya.
8bittree
1
@ user16307: File exe berisi informasi itu secara tidak langsung. Prosesor yang menjalankan program tidak peduli dengan jenis yang digunakan saat menulis program, tetapi sebagian besar dapat disimpulkan dari instruksi yang digunakan untuk mengakses berbagai lokasi memori.
Bart van Ingen Schenau
@ user16307 sebenarnya ada sedikit informasi tambahan. File exe tahu bahwa integer adalah 4 byte jadi ketika Anda menulis "int a" kompiler memesan 4 byte untuk variabel dan dengan demikian dapat menghitung alamat a dan variabel lainnya setelahnya.
Esben Skov Pedersen
1
@ user16307 tidak ada perbedaan praktis (di samping ukuran jenis) perbedaan antara int a = 65dan char b = 'A'setelah kode dikompilasi.
6

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:

int main( void )
{
  int x, y, z;

  x = 1;
  y = 2;

  z = x + y;

  return 0;
}

Inilah daging kode perakitan yang dihasilkan sesuai dengan sumber di atas (menggunakan gcc -S), dengan komentar yang ditambahkan oleh saya:

main:
.LFB2:
        pushq   %rbp               ;; save the current frame pointer value
.LCFI0:
        movq    %rsp, %rbp         ;; make the current stack pointer value the new frame pointer value
.LCFI1:                            
        movl    $1, -12(%rbp)      ;; x = 1
        movl    $2, -8(%rbp)       ;; y = 2
        movl    -8(%rbp), %eax     ;; copy the value of y to the eax register
        addl    -12(%rbp), %eax    ;; add the value of x to the eax register
        movl    %eax, -4(%rbp)     ;; copy the value in eax to z
        movl    $0, %eax           ;; eax gets the return value of the function
        leave                      ;; exit and restore the stack
        ret

Ada beberapa hal tambahan yang mengikuti ret, tetapi tidak relevan dengan diskusi.

%eaxadalah register data tujuan umum 32-bit. %rspadalah register 64-bit yang disediakan untuk menyimpan penunjuk tumpukan , yang berisi alamat benda terakhir yang didorong ke tumpukan. %rbpadalah 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 variabel xadalah 12 byte "di bawah" alamat yang disimpan %rbp.

Dalam kode di atas, kami menyalin nilai integer x(1, disimpan di -12(%rbp)) ke register %eaxmenggunakan movlinstruksi, yang digunakan untuk menyalin kata 32-bit dari satu lokasi ke lokasi lain. Kami kemudian memanggil addl, yang menambahkan nilai integer y(disimpan di -8(%rbp)) ke nilai yang sudah ada di %eax. Kami kemudian menyimpan hasilnya -4(%rbp), yaitu z.

Sekarang mari kita ubah itu jadi kita berurusan dengan doublenilai alih-alih intnilai:

int main( void )
{
  double x, y, z;

  x = 1;
  y = 2;

  z = x + y;

  return 0;
}

Berlari gcc -Slagi memberi kita:

main:
.LFB2:
        pushq   %rbp                              
.LCFI0:
        movq    %rsp, %rbp
.LCFI1:
        movabsq $4607182418800017408, %rax ;; copy literal 64-bit floating-point representation of 1.00 to rax
        movq    %rax, -24(%rbp)            ;; save rax to x
        movabsq $4611686018427387904, %rax ;; copy literal 64-bit floating-point representation of 2.00 to rax
        movq    %rax, -16(%rbp)            ;; save rax to y
        movsd   -24(%rbp), %xmm0           ;; copy value of x to xmm0 register
        addsd   -16(%rbp), %xmm0           ;; add value of y to xmm0 register
        movsd   %xmm0, -8(%rbp)            ;; save result to z
        movl    $0, %eax                   ;; eax gets return value of function
        leave                              ;; exit and restore the stack
        ret

Beberapa perbedaan. Alih-alih movldan addl, kita menggunakan movsddan addsd(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.

John Bode
sumber
4

Secara historis , C menganggap memori terdiri dari sejumlah kelompok slot bernomor jenisunsigned 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 0x12345678ke 32-bit unsigned int, dan kemudian mencoba membaca dua unsigned intnilai 16-bit berturut-turut dari alamatnya dan yang di atas, maka tergantung pada setengah dari yang unsigned intdisimpan 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 chardibaca menggunakan tipe unsigned charatau 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:

unsigned int a = 0x12345678;
unsigned short p = (unsigned short *)&a;
printf("0x%04X",*p);

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 amenyertakan 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,

supercat
sumber
1
Menyebutkan perilaku yang tidak terdefinisi saat mengetik pemangkasan kepada seseorang yang tidak mengerti bagaimana tidak ada RTTI tampaknya berlawanan dengan intuisi
Cole Johnson
@ColeJohnson: Sayang sekali tidak ada nama formal atau standar untuk dialek C yang didukung oleh 99% dari kompiler pra-2009, karena baik dari perspektif pengajaran dan praktis, mereka harus dianggap bahasa yang berbeda secara mendasar. Karena nama yang sama diberikan untuk kedua dialek yang mengembangkan sejumlah perilaku yang dapat diprediksi dan dioptimalkan selama 35 tahun, dialek yang membuang perilaku seperti itu untuk tujuan optimasi, sulit untuk menghindari kebingungan ketika berbicara tentang hal-hal yang bekerja secara berbeda di dalamnya .
supercat
Secara historis C berlari pada mesin Lisp yang tidak memungkinkan longgar seperti bermain dengan tipe. Saya cukup yakin bahwa banyak "perilaku yang dapat diprediksi dan dioptimalkan" yang terlihat 30 tahun yang lalu sama sekali tidak berfungsi di mana pun kecuali BSD Unix di VAX.
prosfilaes
@prosfilaes: Mungkin "99% dari kompiler yang digunakan dari tahun 1999 hingga 2009" akan lebih akurat? Bahkan ketika kompiler memiliki opsi untuk beberapa optimisasi integer yang agak agresif, mereka hanya itu - opsi. Saya tidak tahu bahwa saya pernah melihat kompiler sebelum 1999 yang tidak memiliki mode yang tidak menjamin bahwa mengingat int x,y,z;ekspresi x*y > ztidak 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.
supercat
1
... tempat unsigned charnilai yang digunakan untuk membangun tipe "berasal". Jika suatu program menguraikan pointer menjadi unsigned char[], tunjukkan konten hex-nya secara singkat di layar, dan kemudian hapus pointer, the unsigned 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.
supercat
3

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).

Mike Harris
sumber
Apa yang saya tidak mengerti adalah bagaimana komputer tahu memungkinkan ketika membaca nilai variabel dari dan alamat seperti 10001 jika int atau char. Bayangkan saya klik pada program yang disebut anyprog.exe. Segera kode mulai dijalankan. Apakah file exe ini menyertakan informasi tentang apakah variabel disimpan sebagai atau karakter? -
user16307
1
@ user16307 Pada dasarnya tidak, semua informasi itu benar-benar hilang. Terserah kode mesin untuk dirancang dengan cukup baik untuk melakukan tugasnya dengan benar bahkan tanpa informasi itu. Semua komputer peduli adalah bahwa ada delapan bit berturut-turut di alamat 10001. Baik pekerjaan Anda atau pekerjaan kompiler , tergantung kasus, untuk mengikuti hal-hal seperti itu secara manual saat menulis mesin atau kode perakitan.
Panzercrisis
1
Perhatikan bahwa pengetikan dinamis bukan satu-satunya alasan untuk mempertahankan jenis. Java diketik secara statis, tetapi masih harus mempertahankan jenisnya, karena memungkinkan untuk merefleksikan jenis tersebut secara dinamis. Plus, ia memiliki runtime polymorphism, yaitu metode pengiriman berdasarkan pada tipe runtime, yang juga membutuhkan tipe tersebut. C ++ menempatkan metode pengiriman kode ke objek (atau lebih tepatnya kelas) itu sendiri, jadi, tidak perlu mengetik dalam arti tertentu (walaupun tentu saja vtable dalam beberapa hal merupakan bagian dari tipe, jadi, benar-benar setidaknya bagian dari jenis yang dipertahankan), tetapi di Jawa, kode metode pengiriman terpusat.
Jörg W Mittag
lihat pertanyaan saya, saya menulis "ketika sebuah program C dijalankan?" bukankah mereka secara tidak langsung disimpan dalam file exe di antara kode instruksi dan akhirnya terjadi di memori? Saya menulis ini untuk Anda lagi: Jika CPU menemukan 0x00000061 pada register dan mengambilnya; dan bayangkan program konsol seharusnya menampilkan ini sebagai karakter bukan int. apakah ada dalam file exe (kode mesin / biner) beberapa kode instruksi yang mengetahui alamat 0x00000061 adalah char dan mengkonversi ke karakter dengan menggunakan tabel ASCII? Jika demikian itu berarti pengidentifikasi karakter tidak langsung di biner ???
user16307
Jika nilainya 0x61 dan dinyatakan sebagai char (yaitu, 'a') dan Anda memanggil rutin untuk menampilkannya, akan ada [akhirnya] panggilan sistem untuk menampilkan karakter itu. Jika Anda telah menyatakannya sebagai int dan memanggil tampilan rutin, kompiler akan tahu untuk menghasilkan kode untuk mengkonversi 0x61 (desimal 97) ke urutan ASCII 0x39, 0x37 ('9', '7'). Intinya: kode yang dihasilkan berbeda karena kompiler tahu untuk memperlakukannya secara berbeda.
Mike Harris
3

Anda harus membedakan antara compiletimedan runtimedi satu sisi dan codedan datadi sisi lain.

Dari perspektif mesin, tidak ada perbedaan antara apa yang Anda panggil codeatau instructionsdan apa yang Anda panggil data. Semuanya bermuara pada angka. Tetapi beberapa urutan - apa yang kita sebut code- melakukan sesuatu yang kita temukan berguna, yang lain hanya crashmesin.

Pekerjaan yang dilakukan oleh CPU adalah loop 4 langkah sederhana:

  • Ambil "data" dari alamat yang diberikan
  • Decode instruksi (yaitu "menafsirkan" nomor sebagai instruction)
  • Baca alamat yang efektif
  • Jalankan dan simpan hasil

Ini disebut siklus instruksi .

Saya membaca bahwa A dan 4 disimpan dalam alamat RAM di sini. Tapi bagaimana dengan a dan x?

adan xadalah variabel, yang merupakan penampung untuk alamat, di mana program dapat menemukan "konten" dari variabel. Jadi, kapan saja variabel adigunakan, secara efektif ada alamat dari konten yang adigunakan.

Yang paling membingungkan, bagaimana eksekusi tahu bahwa a adalah char dan x adalah int?

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 sysdiikuti oleh alamat: sys 49152adalah tempat umum untuk meletakkan kode assembler Anda. Tapi tidak ada yang mencegah Anda dari memuat data grafis misalnya 49152, 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;)

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 int?

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. intdan charhanya kosa kata, yang masuk akal compiletime; selama runtime(pada tingkat perakitan), tidak ada charatau int.

Apa yang saya tidak mengerti adalah bagaimana komputer tahu, ketika membaca nilai variabel dari alamat seperti 10001, apakah itu int atau char.

Komputer tidak tahu apa-apa. The programmer tidak. Kode yang dikompilasi menghasilkan konteks , yang diperlukan untuk menghasilkan hasil yang bermakna bagi manusia.

Apakah file yang dapat dieksekusi ini memasukkan informasi tentang apakah variabel yang disimpan adalah tipe int atau char

Ya dan Tidak . Informasi, apakah itu suatu intatau yang charhilang. 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 .

Thomas Junk
sumber
Perbedaan yang bagus antara waktu kompilasi & runtime.
Michael Blackburn
2

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:

char a = 'A';
int x = 4;

Mari kita coba menganalisis setiap bagian:

char / int dikenal sebagai tipe data. Ini memberitahu kompiler untuk mengalokasikan memori. Dalam hal charini akan menjadi 1 byte dan int2 byte. (Harap dicatat ukuran memori ini tergantung lagi pada mikroprosesor).

a / x dikenal sebagai pengidentifikasi. Sekarang ini Anda dapat mengatakan nama "ramah pengguna" yang diberikan ke lokasi memori dalam RAM.

= memberitahu kompiler untuk menyimpan 'A' di lokasi memori adan 4 di lokasi memori x.

Jadi pengenal tipe data int / char hanya digunakan oleh kompiler dan bukan oleh mikroprosesor selama eksekusi program. Karenanya mereka tidak disimpan dalam memori.

prasad
sumber
ok int / char pengidentifikasi tipe data tidak secara langsung disimpan dalam memori sebagai variabel, tetapi bukankah mereka secara tidak langsung disimpan dalam file exe di antara kode instruksi dan akhirnya terjadi di memori? Saya menulis ini untuk Anda lagi: Jika CPU menemukan 0x00000061 pada register dan mengambilnya; dan bayangkan program konsol seharusnya menampilkan ini sebagai karakter bukan int. apakah ada dalam file exe (kode mesin / biner) beberapa kode instruksi yang mengetahui alamat 0x00000061 adalah char dan mengkonversi ke karakter dengan menggunakan tabel ASCII? Jika demikian itu berarti pengidentifikasi karakter tidak langsung di biner ???
user16307
Tidak untuk CPU, semua nomornya. Untuk contoh spesifik Anda, mencetak pada konsol tidak bergantung pada apakah variabel char atau int. Saya akan memperbarui jawaban saya dengan aliran detail tentang bagaimana program tingkat tinggi dikonversi ke dalam bahasa mesin sampai pelaksanaan program.
prasad
2

Jawaban saya di sini agak disederhanakan dan hanya akan merujuk ke C.

Tidak, ketikkan informasi tidak disimpan dalam program.

intatau chartidak mengetik indikator ke CPU; hanya ke kompiler.

Exe yang dibuat oleh kompiler akan memiliki instruksi untuk memanipulasi ints jika variabel dideklarasikan sebagai int. Demikian juga, jika variabel dideklarasikan sebagai a char, exe akan berisi instruksi untuk memanipulasi a char.

Dalam C:

int main()
{
    int a = 65;
    char b = 'A';
    if(a == b)
    {
        printf("Well, what do you know. A char can equal an int.\n");
    }
    return 0;
}

Program ini akan mencetak pesannya, karena chardan intmemiliki yang sama nilai-nilai dalam RAM.

Sekarang, jika Anda bertanya-tanya bagaimana printfmengelola output 65untuk intdan Auntuk char, itu karena Anda harus menentukan dalam "format string" bagaimana printfseharusnya memperlakukan nilainya .
(Misalnya, %cberarti memperlakukan nilai sebagai a char, dan %dberarti memperlakukan nilai sebagai bilangan bulat; nilai yang sama bagaimanapun juga.)

BenjiWiebe
sumber
2
Saya berharap seseorang akan menggunakan contoh menggunakan 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) pada adan kemudian menampilkan karakter, yang dengan senang hati melakukannya. Mengapa? Karena tidak peduli.
Cole Johnson
tetapi mengapa beberapa mengatakan di sini dalam kasus C #, bukan ceritanya? saya membaca beberapa komentar orang lain dan mereka mengatakan dalam C # dan C ++ cerita (info tentang tipe data) berbeda dan bahkan CPU tidak melakukan komputasi. Ada ide tentang itu?
user16307
@ user16307 Jika CPU tidak melakukan komputasi, program tidak berjalan. :) Adapun C #, saya tidak tahu, tapi saya pikir jawaban saya juga berlaku di sana. Sedangkan untuk C ++, saya tahu jawaban saya berlaku di sana.
BenjiWiebe
0

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.

x = 4 + 5

Akan dieksekusi sebagai:

  1. Muat 00000100 ke dalam register 1
  2. Muat 00000101 ke dalam register 2
  3. IAdd daftar 1 untuk mendaftar 2, dan simpan di register 1

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.

Leliel
sumber
0

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.

bagaimana eksekusi tahu bahwa a adalah char dan x adalah int?

Tidak, tetapi ketika kompiler menghasilkan kode mesin, ia tahu. An intdan a charbisa dari berbagai ukuran. Dalam arsitektur di mana char adalah ukuran byte dan int adalah 4 byte, maka variabel xtidak ada di alamat 10001, tetapi juga di 10002, 10003 dan 10004. Ketika kode perlu memuat nilai xke 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.

Katakanlah nilai disimpan di suatu tempat di RAM sebagai 10011001; jika saya adalah program yang mengeksekusi kode, bagaimana saya akan tahu apakah 10011001 ini adalah char atau int

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 charvs unsigned 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).

bekukoi
sumber
tetapi mengapa beberapa mengatakan di sini dalam kasus C #, bukan ceritanya? saya membaca beberapa komentar orang lain dan mereka mengatakan dalam C # dan C ++ cerita (info tentang tipe data) berbeda dan bahkan CPU tidak melakukan komputasi. Ada ide tentang itu?
user16307
0

tetapi mengapa beberapa mengatakan di sini dalam kasus C #, bukan ceritanya? Saya membaca beberapa komentar orang lain dan mereka mengatakan dalam C # dan C ++ cerita (info tentang tipe data) berbeda dan bahkan CPU tidak melakukan komputasi. Ada ide tentang itu?

Dalam bahasa yang diperiksa tipe seperti C #, pemeriksaan tipe dilakukan oleh kompiler. Kode benji menulis:

int main()
{
    int a = 65;
    char b = 'A';
    if(a == b)
    {
        printf("Well, what do you know. A char can equal an int.\n");
    }
    return 0;
}

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).

int a = 42;
string b = "Compilers are awesome.";
double[] c = a * b;

Kompiler hanya akan menolak untuk menghasilkan kode mesin dari C # ini, tidak peduli seberapa banyak string Anda menciumnya.

Michael Blackburn
sumber
-4

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.

Nathan Ringo
sumber
3
Pertanyaan secara khusus menyatakan itu merujuk ke bahasa C (bukan Lisp), dan bahasa C tidak menyimpan metadata variabel. Meskipun implementasi C mungkin untuk dilakukan, karena standar tidak melarangnya, dalam praktiknya hal itu tidak pernah terjadi. Jika Anda memiliki contoh yang relevan dengan pertanyaan, berikan kutipan spesifik dan memberikan referensi yang berhubungan dengan bahasa C .
Nah, Anda bisa menulis kompiler C untuk mesin Lisp, tetapi tidak ada yang menggunakan mesin Lisp pada zaman ini secara umum. Omong - omong , arsitektur berorientasi objek adalah Rekursiv .
Nathan Ringo
2
Saya pikir jawaban ini tidak membantu. Ini menyulitkan banyak hal di luar tingkat pemahaman OP saat ini. Jelas OP tidak memahami model eksekusi dasar dari CPU + RAM, dan bagaimana kompiler menerjemahkan sumber tingkat tinggi simbolis ke biner yang dapat dieksekusi. Memori yang ditandai, RTTI, Lisp, dll, jauh melampaui apa yang perlu diketahui oleh penanya menurut pendapat saya, dan hanya akan membingungkannya.
Andres F.
tetapi mengapa beberapa mengatakan di sini dalam kasus C #, bukan ceritanya? saya membaca beberapa komentar orang lain dan mereka mengatakan dalam C # dan C ++ cerita (info tentang tipe data) berbeda dan bahkan CPU tidak melakukan komputasi. Ada ide tentang itu?
user16307