Bagaimana tipe data C “didukung langsung oleh kebanyakan komputer”?

114

Saya membaca "The C Programming Language" K & R dan menemukan pernyataan ini [Pendahuluan, hal. 3]:

Karena tipe data dan struktur kontrol yang disediakan oleh C didukung langsung oleh sebagian besar komputer , pustaka run-time yang diperlukan untuk mengimplementasikan program mandiri kecil.

Apa maksud dari pernyataan yang dicetak tebal? Apakah ada contoh tipe data atau struktur kontrol yang tidak didukung langsung oleh komputer?

gwg
sumber
1
Saat ini, bahasa C mendukung aritmatika kompleks, tetapi awalnya tidak karena komputer tidak secara langsung mendukung bilangan kompleks sebagai tipe data.
Jonathan Leffler
12
Sebenarnya, secara historis kebalikannya: C dirancang dari operasi & jenis perangkat keras yang tersedia pada saat itu.
Basile Starynkevitch
2
Sebagian besar komputer tidak memiliki dukungan perangkat keras langsung untuk pelampung desimal
PlasmaHH
3
@ MSalters: Saya mencoba memberi petunjuk ke beberapa arah untuk pertanyaan "Apakah ada contoh tipe data atau struktur kontrol yang tidak didukung langsung oleh komputer?" yang tidak saya tafsirkan terbatas pada K&R
PlasmaHH
11
Bagaimana ini bukan duplikat lebih dari 6 tahun setelah Stack Overflow diluncurkan?
Peter Mortensen

Jawaban:

143

Ya, ada tipe data yang tidak didukung secara langsung.

Pada banyak sistem tertanam, tidak ada unit floating point perangkat keras. Jadi, ketika Anda menulis kode seperti ini:

float x = 1.0f, y = 2.0f;
return x + y;

Itu diterjemahkan menjadi sesuatu seperti ini:

unsigned x = 0x3f800000, y = 0x40000000;
return _float_add(x, y);

Kemudian compiler atau pustaka standar harus menyediakan implementasinya _float_add(), yang menghabiskan memori pada sistem tertanam Anda. Jika Anda menghitung byte pada sistem yang sangat kecil, ini bisa bertambah.

Contoh umum lainnya adalah integer 64-bit ( long longdalam standar C sejak 1999), yang tidak secara langsung didukung oleh sistem 32-bit. Sistem SPARC lama tidak mendukung perkalian integer, jadi perkalian harus disediakan oleh runtime. Ada contoh lain.

Bahasa lainnya

Sebagai perbandingan, bahasa lain memiliki bahasa primitif yang lebih rumit.

Misalnya, simbol Lisp membutuhkan banyak dukungan runtime, seperti tabel di Lua, string dengan Python, array di Fortran, dan lain-lain. Jenis yang setara di C biasanya bukan bagian dari pustaka standar sama sekali (tidak ada simbol atau tabel standar) atau mereka jauh lebih sederhana dan tidak memerlukan banyak dukungan waktu proses (array di C pada dasarnya hanya pointer, string yang diakhiri nul adalah hampir sesederhana).

Struktur kontrol

Struktur kontrol penting yang hilang dari C adalah penanganan pengecualian. Keluar nonlokal terbatas pada setjmp()dan longjmp(), yang hanya menyimpan dan memulihkan bagian tertentu dari status prosesor. Sebagai perbandingan, runtime C ++ harus berjalan di tumpukan dan memanggil destruktor dan penangan pengecualian.

Dietrich Epp
sumber
2
pada dasarnya hanya penunjuk ... lebih tepatnya, pada dasarnya hanya potongan memori mentah. Bahkan jika itu memilih rewel, dan jawabannya tetap bagus.
Deduplicator
2
Anda dapat berargumen bahwa string yang diakhiri null memiliki "dukungan perangkat keras" karena terminator string cocok dengan operasi 'lompat jika nol' pada sebagian besar prosesor dan dengan demikian sedikit lebih cepat daripada kemungkinan implementasi string lainnya.
Peteris
1
Memposting jawaban saya sendiri untuk memperluas tentang bagaimana C dirancang untuk memetakan hanya ke asm.
Peter Cordes
1
Tolong jangan gunakan kolokasi "array pada dasarnya hanya penunjuk", itu bisa sangat, sangat menyesatkan pemula seperti OP. Sesuatu di sepanjang baris "array diimplementasikan secara langsung menggunakan pointer di tingkat perangkat keras" akan menjadi IMO yang lebih baik.
The Paramagnetic Croissant
1
@TheParamagneticCroissant: Saya pikir dalam konteks ini itu tepat ... kejelasan datang dengan mengorbankan presisi.
Dietrich Epp
37

Sebenarnya, saya berani bertaruh bahwa isi pengantar ini tidak banyak berubah sejak 1978 ketika Kernighan dan Ritchie pertama kali menulisnya di Edisi Pertama buku, dan mereka merujuk pada sejarah dan evolusi C pada waktu itu lebih dari modern. implementasi.

Komputer pada dasarnya hanyalah bank memori dan prosesor sentral, dan setiap prosesor beroperasi menggunakan kode mesin; bagian dari desain setiap prosesor adalah arsitektur set instruksi, yang disebut Bahasa Majelis , yang memetakan satu-ke-satu dari satu set mnemonik yang dapat dibaca manusia ke kode mesin, yang semuanya angka.

Penulis bahasa C - dan bahasa B dan BCPL yang mendahuluinya - bermaksud untuk mendefinisikan konstruksi dalam bahasa yang dikumpulkan seefisien mungkin ke dalam Assembly ... pada kenyataannya, mereka dipaksa oleh batasan dalam target perangkat keras. Seperti jawaban lain telah menunjukkan, ini melibatkan cabang (GOTO dan kontrol aliran lainnya di C), gerakan (tugas), operasi logis (& | ^), aritmatika dasar (menambah, mengurangi, menambah, mengurangi), dan pengalamatan memori (pointer ). Contoh yang bagus adalah operator pre- / post-increment dan decrement dalam C, yang seharusnya ditambahkan ke bahasa B oleh Ken Thompson secara khusus karena mereka mampu menerjemahkan langsung ke opcode tunggal setelah dikompilasi.

Inilah yang dimaksud penulis ketika mereka mengatakan "didukung langsung oleh sebagian besar komputer". Mereka tidak berarti bahwa bahasa lain yang terdapat jenis dan struktur yang tidak didukung langsung - mereka berarti bahwa dengan desain C konstruksi diterjemahkan paling langsung (kadang-kadang secara harfiah langsung) ke Majelis.

Hubungan dekat dengan Assembly yang mendasarinya, sambil tetap menyediakan semua elemen yang diperlukan untuk pemrograman terstruktur, inilah yang menyebabkan adopsi awal C, dan yang membuatnya tetap menjadi bahasa populer saat ini di lingkungan di mana efisiensi kode yang dikompilasi masih menjadi kuncinya.

Untuk artikel menarik tentang sejarah bahasa, lihat Perkembangan Bahasa C - Dennis Ritchie

John Castleman
sumber
14

Jawaban singkatnya adalah, sebagian besar konstruksi bahasa yang didukung oleh C juga didukung oleh mikroprosesor komputer target, oleh karena itu, kode C yang dikompilasi menerjemahkan dengan sangat baik dan efisien ke bahasa rakitan mikroprosesor, sehingga menghasilkan kode yang lebih kecil dan footprint yang lebih kecil.

Jawaban yang lebih panjang membutuhkan sedikit pengetahuan bahasa assembly. Di C, pernyataan seperti ini:

int myInt = 10;

akan menerjemahkan sesuatu seperti ini dalam perakitan:

myInt dw 1
mov myInt,10

Bandingkan ini dengan sesuatu seperti C ++:

MyClass myClass;
myClass.set_myInt(10);

Kode bahasa assembly yang dihasilkan (bergantung pada seberapa besar MyClass ()), dapat menambahkan hingga ratusan baris bahasa assembly.

Tanpa benar-benar membuat program dalam bahasa assembly, C murni mungkin adalah kode "paling kurus" dan "paling ketat" tempat Anda dapat membuat program.

EDIT

Mengingat komentar pada jawaban saya, saya memutuskan untuk menjalankan tes, hanya untuk kewarasan saya sendiri. Saya membuat program bernama "test.c", yang terlihat seperti ini:

#include <stdio.h>

void main()
{
    int myInt=10;

    printf("%d\n", myInt);
}

Saya menyusun ini ke perakitan menggunakan gcc. Saya menggunakan baris perintah berikut untuk mengkompilasinya:

gcc -S -O2 test.c

Berikut adalah bahasa assembly yang dihasilkan:

    .file   "test.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB24:
    .cfi_startproc
    movl    $10, %edx
    movl    $.LC0, %esi
    movl    $1, %edi
    xorl    %eax, %eax
    jmp __printf_chk
    .cfi_endproc
.LFE24:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

Saya kemudian membuat file bernama "test.cpp" yang mendefinisikan kelas dan mengeluarkan hal yang sama seperti "test.c":

#include <iostream>
using namespace std;

class MyClass {
    int myVar;
public:
    void set_myVar(int);
    int get_myVar(void);
};

void MyClass::set_myVar(int val)
{
    myVar = val;
}

int MyClass::get_myVar(void)
{
    return myVar;
}

int main()
{
    MyClass myClass;
    myClass.set_myVar(10);

    cout << myClass.get_myVar() << endl;

    return 0;
}

Saya menyusunnya dengan cara yang sama, menggunakan perintah ini:

g++ -O2 -S test.cpp

Berikut adalah file assembly yang dihasilkan:

    .file   "test.cpp"
    .section    .text.unlikely,"ax",@progbits
    .align 2
.LCOLDB0:
    .text
.LHOTB0:
    .align 2
    .p2align 4,,15
    .globl  _ZN7MyClass9set_myVarEi
    .type   _ZN7MyClass9set_myVarEi, @function
_ZN7MyClass9set_myVarEi:
.LFB1047:
    .cfi_startproc
    movl    %esi, (%rdi)
    ret
    .cfi_endproc
.LFE1047:
    .size   _ZN7MyClass9set_myVarEi, .-_ZN7MyClass9set_myVarEi
    .section    .text.unlikely
.LCOLDE0:
    .text
.LHOTE0:
    .section    .text.unlikely
    .align 2
.LCOLDB1:
    .text
.LHOTB1:
    .align 2
    .p2align 4,,15
    .globl  _ZN7MyClass9get_myVarEv
    .type   _ZN7MyClass9get_myVarEv, @function
_ZN7MyClass9get_myVarEv:
.LFB1048:
    .cfi_startproc
    movl    (%rdi), %eax
    ret
    .cfi_endproc
.LFE1048:
    .size   _ZN7MyClass9get_myVarEv, .-_ZN7MyClass9get_myVarEv
    .section    .text.unlikely
.LCOLDE1:
    .text
.LHOTE1:
    .section    .text.unlikely
.LCOLDB2:
    .section    .text.startup,"ax",@progbits
.LHOTB2:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB1049:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $10, %esi
    movl    $_ZSt4cout, %edi
    call    _ZNSolsEi
    movq    %rax, %rdi
    call    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE1049:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE2:
    .section    .text.startup
.LHOTE2:
    .section    .text.unlikely
.LCOLDB3:
    .section    .text.startup
.LHOTB3:
    .p2align 4,,15
    .type   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, @function
_GLOBAL__sub_I__ZN7MyClass9set_myVarEi:
.LFB1056:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $_ZStL8__ioinit, %edi
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, %edx
    movl    $_ZStL8__ioinit, %esi
    movl    $_ZNSt8ios_base4InitD1Ev, %edi
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp __cxa_atexit
    .cfi_endproc
.LFE1056:
    .size   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi, .-_GLOBAL__sub_I__ZN7MyClass9set_myVarEi
    .section    .text.unlikely
.LCOLDE3:
    .section    .text.startup
.LHOTE3:
    .section    .init_array,"aw"
    .align 8
    .quad   _GLOBAL__sub_I__ZN7MyClass9set_myVarEi
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .hidden __dso_handle
    .ident  "GCC: (Ubuntu 4.9.1-16ubuntu6) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

Seperti yang dapat Anda lihat dengan jelas, file assembly yang dihasilkan jauh lebih besar pada file C ++ daripada pada file C. Bahkan jika Anda memotong semua hal lainnya dan hanya membandingkan C "utama" dengan C ++ "utama", ada banyak hal tambahan.

Icemanind
sumber
14
"Kode C ++" itu bukan C ++. Dan kode nyata seperti MyClass myClass { 10 }di C ++ sangat mungkin untuk dikompilasi ke perakitan yang persis sama. Kompiler C ++ modern telah menghilangkan penalti abstraksi. Dan sebagai hasilnya, mereka seringkali dapat mengalahkan kompiler C. Misalnya penalti abstraksi dalam C qsortnyata, tetapi C ++ std::sorttidak memiliki penalti abstraksi bahkan setelah pengoptimalan dasar.
MSalters
1
Anda dapat dengan mudah melihat menggunakan IDA Pro bahwa sebagian besar konstruksi C ++ dikompilasi ke hal yang sama seperti melakukannya secara manual di C, konstruktor dan dtor disisipkan untuk objek sepele, kemudian pengoptimalan di masa mendatang diterapkan
paulm
7

K&R berarti bahwa sebagian besar ekspresi C (arti teknis) dipetakan ke satu atau beberapa instruksi assembly, bukan panggilan fungsi ke pustaka dukungan. Pengecualian biasanya adalah pembagian integer pada arsitektur tanpa instruksi div perangkat keras, atau floating point pada mesin tanpa FPU.

Ada kutipannya:

C menggabungkan fleksibilitas dan kekuatan bahasa assembly dengan bahasa assembly yang ramah pengguna.

( Ditemukan di sini . Saya pikir saya ingat variasi yang berbeda, seperti "kecepatan bahasa assembly dengan kenyamanan dan ekspresi bahasa assembly".)

long int biasanya memiliki lebar yang sama dengan register mesin asli.

Beberapa bahasa tingkat yang lebih tinggi menentukan lebar yang tepat dari tipe datanya, dan implementasi pada semua mesin harus bekerja dengan cara yang sama. Tapi bukan C.

Jika Anda ingin bekerja dengan int 128bit pada x86-64, atau dalam kasus umum BigInteger dengan ukuran arbitrer, Anda memerlukan pustaka fungsi untuk itu. Semua CPU sekarang menggunakan komplemen 2 sebagai representasi biner dari bilangan bulat negatif, tetapi bahkan itu tidak terjadi saat C dirancang. (Itulah mengapa beberapa hal yang akan memberikan hasil berbeda pada mesin non-pelengkap 2 secara teknis tidak ditentukan dalam standar C.)

C pointer ke data atau fungsi bekerja dengan cara yang sama seperti alamat assembly.

Jika Anda menginginkan referensi yang dihitung ulang, Anda harus melakukannya sendiri. Jika Anda menginginkan fungsi anggota virtual c ++ yang memanggil fungsi yang berbeda tergantung pada jenis objek yang dituju penunjuk Anda, kompilator C ++ harus menghasilkan lebih dari sekadar callinstruksi dengan alamat tetap.

String hanyalah array

Di luar fungsi perpustakaan, satu-satunya operasi string yang disediakan adalah membaca / menulis karakter. Tanpa concat, tanpa substring, tanpa pencarian. (String disimpan sebagai '\0'array nul-terminated ( ) dari 8bit integer, bukan pointer + length, jadi untuk mendapatkan substring Anda harus menulis nul ke dalam string aslinya.)

CPU terkadang memiliki instruksi yang dirancang untuk digunakan oleh fungsi pencarian string, tetapi biasanya masih memproses satu byte per instruksi yang dieksekusi, dalam satu lingkaran. (atau dengan awalan rep x86. Mungkin jika C dirancang pada x86, pencarian atau perbandingan string akan menjadi operasi asli, bukan panggilan fungsi perpustakaan.)

Banyak jawaban lain memberikan contoh hal-hal yang tidak didukung secara native, seperti penanganan pengecualian, tabel hash, daftar. Filosofi desain K & R adalah alasan C tidak memiliki semua ini secara asli.

Peter Cordes
sumber
"K&R berarti bahwa sebagian besar ekspresi C (arti teknis) dipetakan ke satu atau beberapa instruksi assembly, bukan pemanggilan fungsi ke pustaka dukungan." Ini adalah penjelasan yang sangat intuitif. Terima kasih.
gwg
1
Saya baru saja menemukan istilah "bahasa von Neumann" ( en.wikipedia.org/wiki/Von_Neumann_programming_languages ). Ini PERSIS apa itu C.
Peter Cordes
1
Inilah mengapa saya menggunakan C. Tetapi yang mengejutkan saya ketika saya belajar C adalah bahwa dengan mencoba menjadi efisien untuk berbagai perangkat keras, terkadang tidak kompeten dan tidak efisien pada kebanyakan perangkat keras modern. Maksud saya misalnya tidak ada cara-berguna-dan-dapat diandalkan-untuk-mendeteksi-integer-overflow-in-c dan multi-kata-penambahan-menggunakan-membawa-bendera .
Z boson
6

Bahasa assembly dari suatu proses umumnya berhubungan dengan jump (go to), statement, move statement, binary arthritis (XOR, NAND, AND OR, etc), memory field (atau address). Mengategorikan memori menjadi dua jenis, instruksi dan data. Itu tentang semua bahasa assembly adalah (saya yakin programmer assembly akan berpendapat ada yang lebih dari itu, tetapi intinya adalah ini secara umum). C sangat mirip dengan kesederhanaan ini.

C adalah merangkai apa itu aljabar menjadi aritmatika.

C merangkum dasar-dasar perakitan (bahasa prosesor). Mungkin pernyataan yang lebih benar daripada "Karena tipe data dan struktur kontrol yang disediakan oleh C didukung langsung oleh sebagian besar komputer"

terary
sumber
5

Waspadalah terhadap perbandingan yang menyesatkan

  1. Pernyataan tersebut bergantung pada gagasan "perpustakaan run-time" , yang sebagian besar telah ketinggalan zaman sejak itu, setidaknya untuk bahasa tingkat tinggi arus utama. (Ini masih relevan untuk sistem tertanam terkecil.) Run-time adalah dukungan minimal yang diperlukan program dalam bahasa tersebut untuk dieksekusi saat Anda hanya menggunakan konstruksi yang dibangun ke dalam bahasa (sebagai lawan secara eksplisit memanggil fungsi yang disediakan oleh perpustakaan) .
  2. Sebaliknya, bahasa modern cenderung tidak membedakan antara run-time dan pustaka standar , seringkali cukup ekstensif.
  3. Pada saat buku K&R dibuat, C bahkan tidak memiliki perpustakaan standar . Sebaliknya, pustaka C yang tersedia sedikit berbeda di antara rasa Unix yang berbeda.
  4. Untuk memahami pernyataan, Anda tidak boleh membandingkan dengan bahasa dengan pustaka standar (seperti Lua dan Python yang disebutkan dalam jawaban lain), tetapi dengan bahasa dengan lebih banyak konstruksi bawaan (seperti LISP lama dan FORTRAN lama yang disebutkan di jawaban). Contoh lainnya adalah BASIC (interaktif, seperti LISP) atau PASCAL (dikompilasi, seperti FORTRAN) yang keduanya memiliki (antara lain) fitur input / output yang dibangun langsung ke dalam bahasa itu sendiri.
  5. Sebaliknya, tidak ada cara standar untuk mendapatkan hasil komputasi dari program C yang hanya menggunakan waktu proses, bukan pustaka apa pun.
Lutz Prechelt
sumber
Di sisi lain, sebagian besar bahasa modern berjalan di dalam lingkungan runtime khusus yang menyediakan fasilitas seperti pengumpulan sampah.
Nate CK
5

Apakah ada contoh tipe data atau struktur kontrol yang tidak didukung langsung oleh komputer?

Semua tipe data fundamental dan operasinya dalam bahasa C dapat diimplementasikan oleh satu atau beberapa instruksi bahasa mesin tanpa perulangan - mereka secara langsung didukung oleh (hampir setiap) CPU.

Beberapa tipe data populer dan operasinya memerlukan lusinan instruksi bahasa mesin, atau memerlukan iterasi dari beberapa runtime loop, atau keduanya.

Banyak bahasa memiliki sintaks singkat khusus untuk tipe seperti itu dan operasinya - menggunakan tipe data seperti itu di C biasanya membutuhkan pengetikan lebih banyak kode.

Jenis data, dan operasi tersebut meliputi:

  • manipulasi string teks panjang arbiter - penggabungan, substring, menetapkan string baru ke variabel yang diinisialisasi dengan beberapa string lain, dll. ('s = "Hello World!"; s = (s + s) [2: -2] 'dengan Python)
  • set
  • objek dengan destruktor virtual bertingkat, seperti di C ++ dan setiap bahasa pemrograman berorientasi objek lainnya
  • Perkalian dan pembagian matriks 2D; menyelesaikan sistem linier ("C = B / A; x = A \ b" dalam MATLAB dan banyak bahasa pemrograman array)
  • ekspresi reguler
  • variabel-length array - khususnya, menambahkan item ke akhir array, yang (terkadang) membutuhkan alokasi lebih banyak memori.
  • membaca nilai variabel yang mengubah jenis saat runtime - terkadang berupa float, di lain waktu berupa string
  • array asosiatif (sering disebut "peta" atau "kamus")
  • daftar
  • rasio ("(+ 1/3 2/7)" menghasilkan "13/21" di Lisp )
  • aritmatika presisi sewenang-wenang (sering disebut "bignum")
  • mengonversi data menjadi representasi yang dapat dicetak (metode ".tostring" di JavaScript)
  • bilangan titik tetap jenuh (sering digunakan dalam program C tertanam)
  • mengevaluasi string yang diketik pada waktu proses seolah-olah itu adalah ekspresi ("eval ()" dalam banyak bahasa pemrograman).

Semua operasi ini memerlukan lusinan instruksi bahasa mesin atau memerlukan iterasi beberapa putaran runtime di hampir setiap prosesor.

Beberapa struktur kontrol populer yang juga memerlukan lusinan instruksi atau perulangan bahasa mesin meliputi:

  • penutupan
  • lanjutan
  • pengecualian
  • evaluasi malas

Baik ditulis dalam C atau bahasa lain, ketika sebuah program memanipulasi tipe data tersebut, CPU pada akhirnya harus menjalankan instruksi apa pun yang diperlukan untuk memanipulasi tipe data tersebut. Instruksi tersebut sering kali terdapat dalam "perpustakaan". Setiap bahasa pemrograman, bahkan C, memiliki "run-time library" untuk setiap platform yang disertakan secara default di setiap executable.

Kebanyakan orang yang menulis kompiler meletakkan instruksi untuk memanipulasi semua tipe data yang "dibangun ke dalam bahasa" ke dalam pustaka run-time mereka. Karena C tidak memiliki apapun satu tipe data di atas dan operasi serta struktur kontrol yang dibangun ke dalam bahasa, tidak ada satupun yang disertakan dalam pustaka run-time C - yang membuat pustaka run-time C lebih kecil daripada run- pustaka waktu dari bahasa pemrograman lain yang memiliki lebih banyak hal di atas yang ada di dalam bahasa tersebut.

Ketika seorang programmer menginginkan sebuah program - dalam C atau bahasa lain pilihannya - untuk memanipulasi tipe data lain yang tidak "dibangun ke dalam bahasa", programmer tersebut biasanya memberi tahu kompiler untuk menyertakan pustaka tambahan dengan program itu, atau terkadang (untuk "menghindari ketergantungan") menulis implementasi lain dari operasi tersebut langsung dalam program.

David Cary
sumber
Jika implementasi Lisp Anda mengevaluasi (+ 1/3 2/7) sebagai 3/21, saya pikir Anda harus memiliki implementasi yang sangat kreatif ...
RobertB
4

Apa saja tipe data bawaan C? Mereka adalah hal-hal seperti int, char, * int, float, array dll ... tipe data ini dipahami oleh CPU. CPU tahu bagaimana bekerja dengan array, bagaimana melakukan dereferensi pointer dan bagaimana melakukan aritmatika pada pointer, integer dan angka floating point.

Tetapi ketika Anda beralih ke bahasa pemrograman tingkat yang lebih tinggi, Anda telah membangun tipe data abstrak dan konstruksi yang lebih kompleks. Sebagai contoh, lihat beragam kelas bawaan dalam bahasa pemrograman C ++. CPU tidak memahami kelas, objek, atau tipe data abstrak, sehingga waktu proses C ++ menjembatani kesenjangan antara CPU dan bahasa. Ini adalah contoh tipe data yang tidak didukung langsung oleh kebanyakan komputer.

hhafez.dll
sumber
2
x86 tahu cara bekerja dengan beberapa larik, tetapi tidak semua. Untuk ukuran elemen yang besar atau tidak biasa, perlu melakukan aritmatika integer untuk mengubah indeks array menjadi offset pointer. Dan di platform lain, ini selalu dibutuhkan. Dan gagasan bahwa CPU tidak memahami kelas C ++ sungguh menggelikan. Itu hanya offset pointer, seperti C struct. Anda tidak memerlukan runtime untuk itu.
MSalters
@MSalters ya, tetapi metode aktual dari kelas pustaka standar seperti iostreams dll adalah fungsi pustaka daripada didukung langsung oleh kompiler. Namun, bahasa tingkat yang lebih tinggi yang mungkin mereka bandingkan bukanlah C ++, tetapi bahasa kontemporer seperti FORTRAN dan PL / I.
Acak832
1
Kelas C ++ dengan fungsi anggota virtual menerjemahkan lebih dari sekadar offset ke dalam struct.
Peter Cordes
4

Itu tergantung pada komputer. Pada PDP-11, tempat C ditemukan, longtidak didukung dengan baik (ada modul tambahan opsional yang dapat Anda beli yang mendukung beberapa, tetapi tidak semua, operasi 32-bit). Hal yang sama berlaku untuk berbagai tingkatan pada sistem 16-bit apa pun, termasuk PC IBM asli. Dan juga untuk operasi 64-bit pada mesin 32-bit atau dalam program 32-bit, meskipun bahasa C pada saat buku K&R tidak memiliki operasi 64-bit sama sekali. Dan tentu saja ada banyak sistem sepanjang tahun 80-an dan 90-an [termasuk 386 dan sekitar 486 prosesor], dan bahkan beberapa sistem tertanam saat ini, yang tidak secara langsung mendukung aritmatika floating point ( floatatau double).

Untuk contoh yang lebih eksotis, beberapa arsitektur komputer hanya mendukung pointer "berorientasi kata" (menunjuk pada integer dua byte atau empat byte dalam memori), dan pointer byte ( char *atau void *) harus diimplementasikan dengan menambahkan bidang offset ekstra. Pertanyaan ini membahas beberapa detail tentang sistem semacam itu.

Fungsi "run-time library" yang dirujuknya bukanlah yang akan Anda lihat di manual, tetapi fungsi seperti ini, dalam library waktu proses kompiler modern , yang digunakan untuk mengimplementasikan operasi tipe dasar yang tidak didukung oleh mesin . Perpustakaan runtime yang dirujuk oleh K&R sendiri dapat ditemukan di situs web The Unix Heritage Society - Anda dapat melihat fungsi seperti ldiv(berbeda dari fungsi C dengan nama yang sama, yang tidak ada pada saat itu) yang digunakan untuk mengimplementasikan pembagian Nilai 32-bit, yang tidak didukung PDP-11 bahkan dengan add-on, dan csv(dan cretjuga di csv.c) yang menyimpan dan memulihkan register di stack untuk mengelola panggilan dan pengembalian dari fungsi.

Mereka mungkin juga mengacu pada pilihan mereka untuk tidak mendukung banyak tipe data yang tidak secara langsung didukung oleh mesin yang mendasarinya, tidak seperti bahasa kontemporer lainnya seperti FORTRAN, yang memiliki semantik array yang tidak memetakan juga ke dukungan penunjuk yang mendasari CPU seperti Array C. Fakta bahwa array C selalu diindeks nol dan selalu berukuran diketahui di semua peringkat tetapi yang pertama berarti bahwa tidak perlu menyimpan rentang indeks atau ukuran array, dan tidak perlu memiliki fungsi pustaka runtime untuk mengaksesnya - kompiler dapat dengan mudah melakukan hardcode aritmatika pointer yang diperlukan.

Acak832
sumber
3

Pernyataan tersebut secara sederhana berarti bahwa data dan struktur kontrol di C berorientasi pada mesin.

Ada dua aspek yang perlu dipertimbangkan di sini. Salah satunya adalah bahwa bahasa C memiliki definisi (standar ISO) yang memungkinkan garis lintang dalam cara tipe data didefinisikan. Ini berarti implementasi bahasa C disesuaikan dengan mesin . Tipe data kompilator C cocok dengan apa yang tersedia di mesin yang ditargetkan kompilator, karena bahasa memiliki garis lintang untuk itu. Jika sebuah mesin memiliki ukuran kata yang tidak biasa, seperti 36 bit, maka jenisnya intatau longdapat dibuat untuk menyesuaikan dengan itu. Program yang mengasumsikan intpersis 32 bit akan rusak.

Kedua, karena masalah portabilitas seperti itu, ada efek kedua. Di satu sisi, pernyataan dalam K&R telah menjadi semacam ramalan yang terpenuhi dengan sendirinya , atau mungkin sebaliknya. Artinya, pelaksana prosesor baru sadar akan kebutuhan yang tajam untuk mendukung kompiler C, dan mereka tahu bahwa terdapat banyak kode C yang mengasumsikan bahwa "setiap prosesor terlihat seperti 80386". Arsitektur dirancang dengan C dalam pikiran: dan tidak hanya C dalam pikiran, tetapi dengan kesalahpahaman umum tentang portabilitas C juga. Anda tidak bisa lagi memperkenalkan mesin dengan 9 bit byte atau apa pun untuk penggunaan tujuan umum lagi. Program yang mengasumsikan bahwa tipechar adalah lebar 8 bit yang tepat akan rusak. Hanya beberapa program yang ditulis oleh ahli portabilitas yang akan terus bekerja: sepertinya tidak cukup untuk menggabungkan sistem lengkap dengan toolchain, kernel, ruang pengguna, dan aplikasi yang berguna, dengan upaya yang wajar. Dengan kata lain, tipe C terlihat seperti apa yang tersedia dari perangkat keras karena perangkat keras dibuat agar terlihat seperti perangkat keras lain yang banyak program C nonportable telah ditulis.

Apakah ada contoh tipe data atau struktur kontrol yang tidak didukung langsung oleh komputer?

Tipe data tidak secara langsung didukung dalam banyak bahasa mesin: integer multi-presisi; daftar tertaut; tabel hash; string karakter.

Struktur kontrol tidak secara langsung didukung di sebagian besar bahasa mesin: kelanjutan kelas satu; coroutine / benang; generator; penanganan pengecualian.

Semua ini membutuhkan kode dukungan run-time yang cukup yang dibuat menggunakan berbagai instruksi tujuan umum, dan tipe data yang lebih mendasar.

C memiliki beberapa tipe data standar yang tidak didukung oleh beberapa mesin. Sejak C99, C memiliki bilangan kompleks. Mereka dibuat dari dua nilai floating-point dan dibuat untuk bekerja dengan rutinitas perpustakaan. Beberapa mesin tidak memiliki unit floating-point sama sekali.

Berkenaan dengan beberapa tipe data, tidak jelas. Jika mesin memiliki dukungan untuk pengalamatan memori menggunakan satu register sebagai alamat dasar, dan yang lainnya sebagai perpindahan berskala, apakah itu berarti bahwa array adalah tipe data yang didukung secara langsung?

Juga, berbicara tentang floating-point, ada standarisasi: IEEE 754 floating-point. Mengapa compiler C Anda memiliki a doubleyang sesuai dengan format floating-point yang didukung oleh prosesor bukan hanya karena keduanya dibuat untuk setuju, tetapi karena ada standar independen untuk representasi itu.

Kaz
sumber
2

Hal-hal seperti

  • Daftar Digunakan di hampir semua bahasa fungsional.

  • Pengecualian .

  • Array asosiatif (Maps) - termasuk dalam misalnya PHP dan Perl.

  • Pengumpulan sampah .

  • Tipe data / struktur kontrol termasuk dalam banyak bahasa, tetapi tidak didukung secara langsung oleh CPU.

MTilsted
sumber
2

Didukung secara langsung harus dipahami sebagai pemetaan secara efisien ke set instruksi prosesor.

  • Dukungan langsung untuk tipe integer adalah aturannya, kecuali untuk yang panjang (mungkin memerlukan rutinitas aritmatika yang diperpanjang) dan ukuran pendek (mungkin memerlukan masking).

  • Dukungan langsung untuk tipe floating-point memerlukan ketersediaan FPU.

  • Dukungan langsung untuk bidang bit luar biasa.

  • Struktur dan array memerlukan komputasi alamat, yang secara langsung didukung sampai batas tertentu.

  • Pointer selalu didukung secara langsung melalui pengalamatan tidak langsung.

  • goto / if / while / for / do secara langsung didukung oleh cabang tak bersyarat / bersyarat.

  • sakelar dapat langsung didukung ketika tabel lompat berlaku.

  • Panggilan fungsi secara langsung didukung melalui fitur stack.

Yves Daoust
sumber