[Powered by Google Translate] [CS50 Perpustakaan]
[Nate Hardison] [Harvard University]
[Ini adalah CS50. CS50.TV]
Perpustakaan CS50 adalah alat yang berguna yang kita telah diinstal pada alat
untuk membuatnya lebih mudah bagi Anda untuk menulis program yang meminta pengguna untuk input.
Dalam video ini, kita akan menarik kembali tirai dan melihat apa sebenarnya di perpustakaan CS50.
>> Dalam video pada library C, kita berbicara tentang bagaimana Anda menyertakan file header #
dari perpustakaan dalam kode sumber Anda,
dan kemudian Anda menghubungkan dengan file library biner selama fase menghubungkan
dari proses kompilasi.
File header menentukan antarmuka dari perpustakaan.
Artinya, mereka detail semua sumber daya yang perpustakaan telah tersedia untuk Anda gunakan,
seperti deklarasi fungsi, konstanta, dan tipe data.
File library biner berisi implementasi dari perpustakaan,
yang disusun dari file header perpustakaan dan perpustakaan file sumber. kode c.
>> File library biner tidak sangat menarik untuk melihat karena itu, baik, dalam biner.
Jadi, mari kita lihat di file header untuk perpustakaan sebagai gantinya.
Dalam kasus ini, hanya ada satu header file bernama cs50.h.
Kami telah diinstal di pengguna termasuk direktori
bersama dengan file header perpustakaan sistem lainnya '.
>> Salah satu hal pertama yang akan Anda perhatikan adalah bahwa cs50.h # termasuk file header dari perpustakaan lain -
float, batas, standar bool, dan lib standar.
Sekali lagi, mengikuti prinsip tidak menciptakan kembali roda,
kami telah membangun perpustakaan CS0 menggunakan alat-alat yang lain yang disediakan bagi kita.
>> Hal berikutnya yang Anda akan melihat di perpustakaan adalah bahwa kita mendefinisikan sebuah tipe baru yang disebut "string."
Baris ini benar-benar hanya menciptakan sebuah alias untuk jenis char *,
sehingga tidak ajaib mengilhami tipe string baru dengan atribut
umumnya terkait dengan objek string dalam bahasa lain,
seperti panjang.
Alasan kita lakukan ini adalah untuk melindungi programmer baru dari rincian berdarah
dari pointer sampai mereka siap.
>> Bagian berikutnya dari file header deklarasi fungsi
bahwa perpustakaan CS50 menyediakan bersama dengan dokumentasi.
Perhatikan tingkat rincian dalam komentar di sini.
Ini adalah super penting agar orang tahu bagaimana menggunakan fungsi-fungsi ini.
Kami menyatakan, pada gilirannya, berfungsi untuk meminta pengguna dan karakter kembali, ganda, mengapung, ints,
lama rindu, dan string, menggunakan tipe string kita sendiri.
Mengikuti prinsip menyembunyikan informasi,
kami telah menempatkan definisi kita dalam file terpisah implementasi c -. cs50.c--
terletak di direktori source pengguna.
Kami telah menyediakan file tersebut sehingga Anda dapat melihat hal itu,
belajar dari itu, dan mengkompilasi ulang pada mesin yang berbeda jika Anda ingin,
meskipun kita pikir lebih baik untuk bekerja pada alat untuk kelas ini.
Pokoknya, mari kita lihat sekarang.
>> Fungsi getchar, GetDouble, GetFloat, GetInt, dan GetLongLong
semuanya dibangun di atas fungsi GetString.
Ternyata bahwa mereka semua mengikuti pola yang sama dasarnya.
Mereka menggunakan loop sementara untuk meminta pengguna untuk satu baris masukan.
Mereka mengembalikan nilai khusus jika pengguna masukan sebuah baris kosong.
Mereka berusaha untuk mengurai masukan pengguna sebagai jenis yang sesuai,
baik itu, char ganda, pelampung, dll
Dan kemudian mereka baik kembali hasilnya jika input berhasil diurai
atau mereka reprompt pengguna.
>> Pada tingkat tinggi, tidak ada yang benar-benar rumit di sini.
Anda mungkin telah menulis kode terstruktur sama diri sendiri di masa lalu.
Mungkin bagian yang paling samar yang tampak adalah panggilan sscanf yang mem-parsing input pengguna.
Sscanf merupakan bagian dari keluarga format masukan konversi.
Ia tinggal di io.h standar, dan tugasnya adalah untuk mengurai string C,
sesuai dengan format tertentu, menyimpan hasil parse dalam variabel
disediakan oleh pemanggil.
Karena fungsi konversi format masukan yang sangat berguna, fungsi banyak digunakan
yang tidak super intuitif pada awalnya,
kita akan pergi atas bagaimana sscanf bekerja.
>> Argumen pertama yang sscanf adalah char * - pointer ke karakter.
Untuk fungsi untuk bekerja dengan baik,
karakter yang harus menjadi karakter pertama dari string C,
diakhiri dengan karakter \ nol 0.
Ini adalah string untuk mengurai
Argumen kedua sscanf adalah format string,
biasanya berlalu dalam sebagai konstanta string,
dan Anda mungkin telah melihat string seperti ini sebelumnya ketika menggunakan printf.
Sebuah tanda persen dalam format string menunjukkan specifier konversi.
Karakter segera setelah tanda persen,
menunjukkan tipe C yang kita inginkan sscanf untuk mengkonversi ke.
Dalam GetInt, Anda melihat bahwa ada% d dan c%.
Ini berarti bahwa sscanf akan mencoba untuk int desimal -% d - dan char - c%.
Untuk setiap specifier konversi dalam format string,
sscanf mengharapkan argumen yang sesuai kemudian dalam daftar argumen.
Argumen yang harus menunjuk ke lokasi yang tepat diketik
di mana untuk menyimpan hasil konversi.
>> Cara khas untuk melakukan hal ini adalah untuk membuat variabel pada stack sebelum panggilan sscanf
untuk setiap item yang Anda ingin mengurai dari string
dan kemudian menggunakan operator alamat - ampersand - untuk lulus pointer
untuk variabel ke panggilan sscanf.
Anda dapat melihat bahwa dalam GetInt kita melakukan hal ini.
Tepat sebelum panggilan sscanf, kami mendeklarasikan int disebut n dan c char pada panggilan stack,
dan kami melewati pointer ke mereka ke dalam panggilan sscanf.
Menempatkan variabel-variabel pada stack lebih disukai daripada menggunakan ruang yang dialokasikan
pada tumpukan dengan malloc, karena Anda menghindari overhead dari panggilan malloc,
dan Anda tidak perlu khawatir tentang kebocoran memori.
Karakter tidak diawali dengan tanda persen tidak meminta konversi.
Sebaliknya mereka hanya menambah spesifikasi format.
>> Misalnya, jika format string di GetInt adalah% d sebaliknya,
sscanf akan mencari surat diikuti oleh int,
dan sementara itu akan berusaha untuk mengkonversi int, itu tidak akan melakukan hal lain dengan itu.
Satu-satunya pengecualian untuk ini adalah spasi.
Karakter spasi dalam format string dapat ditemukan pada jumlah spasi -
bahkan tidak sama sekali.
Jadi, itu sebabnya komentar menyebutkan mungkin dengan memimpin dan / atau trailing spasi.
Jadi, pada titik ini sepertinya panggilan sscanf kami akan mencoba untuk mengurai string masukan pengguna
dengan memeriksa spasi terkemuka mungkin,
diikuti oleh int yang akan dikonversi dan disimpan dalam variabel n int
diikuti oleh beberapa jumlah spasi, dan diikuti oleh karakter
disimpan dalam variabel char c.
>> Bagaimana dengan nilai kembali?
Sscanf akan mengurai baris masukan dari awal sampai akhir,
berhenti ketika mencapai akhir atau ketika karakter dalam input
tidak cocok dengan karakter format atau ketika tidak bisa membuat konversi.
Nilai kembali itu digunakan untuk satu-ketika berhenti.
Jika berhenti, karena mencapai akhir dari string masukan
sebelum melakukan konversi dan sebelum gagal untuk mencocokkan bagian dari format string,
maka EOF konstan khusus dikembalikan.
Jika tidak, ia mengembalikan jumlah konversi yang berhasil,
yang bisa 0, 1, atau 2, karena kita sudah meminta dua konversi.
Dalam kasus kami, kami ingin memastikan bahwa pengguna mengetik int dan hanya int.
>> Jadi, kami ingin sscanf kembali 1. Melihat mengapa?
Jika sscanf kembali 0, maka tidak ada konversi dibuat,
sehingga pengguna mengetik sesuatu selain int pada awal input.
Jika sscanf kembali 2, maka pengguna tidak benar mengetikkannya pada awal input,
tetapi mereka kemudian diketik dalam beberapa karakter non-spasi setelah
karena% c konversi berhasil.
Wow, itu cukup penjelasan panjang untuk satu fungsi panggil.
Lagi pula, jika Anda ingin informasi lebih lanjut tentang sscanf dan turunannya,
periksa halaman manual, Google, atau keduanya.
Ada banyak pilihan format string,
dan ini dapat menghemat banyak tenaga kerja manual ketika mencoba untuk mengurai string di C.
>> Fungsi terakhir di perpustakaan untuk lihat adalah GetString.
Ternyata GetString adalah fungsi sulit untuk menulis dengan baik,
meskipun tampaknya seperti tugas sederhana, umum.
Mengapa hal ini terjadi?
Nah, mari kita berpikir tentang bagaimana kita akan menyimpan garis bahwa pengguna jenis masuk
Karena string adalah urutan karakter,
kita mungkin ingin menyimpannya dalam array di stack,
tapi kita perlu tahu berapa lama array akan menjadi saat mendeklarasikannya.
Demikian juga, jika kita ingin meletakkannya di heap,
kita perlu untuk lolos ke malloc jumlah byte kita ingin cadangan,
tapi ini tidak mungkin.
Kami tidak tahu berapa banyak karakter pengguna akan ketik
sebelum pengguna sebenarnya tidak mengetiknya.
>> Sebuah solusi naif untuk masalah ini adalah dengan hanya memesan sebagian besar dari ruang, katakanlah,
blok dari 1000 karakter untuk input pengguna,
dengan asumsi bahwa pengguna tidak akan pernah ketik dalam string yang panjang.
Ini adalah ide yang buruk karena dua alasan.
Pertama, dengan asumsi bahwa pengguna biasanya tidak mengetikkan string yang lama,
Anda bisa membuang banyak memori.
Pada mesin modern, hal ini mungkin tidak menjadi masalah jika Anda melakukan hal ini
dalam satu atau dua kasus yang terisolasi,
tetapi jika Anda mengambil masukan pengguna dalam satu lingkaran dan menyimpan untuk digunakan nanti,
Anda dapat dengan cepat menyedot ton memori.
Selain itu, jika program Anda menulis adalah untuk komputer yang lebih kecil -
perangkat seperti smartphone atau sesuatu yang lain dengan memori terbatas -
solusi ini akan menimbulkan masalah jauh lebih cepat.
Alasan kedua yang lebih serius untuk tidak melakukan hal ini adalah bahwa ia meninggalkan program anda rentan
untuk apa yang disebut serangan buffer overflow.
Dalam pemrograman, buffer adalah memori yang digunakan untuk menyimpan data sementara input atau output,
yang dalam hal ini adalah 1000-char blok kami.
Sebuah buffer overflow terjadi ketika data ditulis melewati ujung blok.
>> Misalnya, jika pengguna sebenarnya ketik lebih dari 1000 karakter.
Anda mungkin pernah mengalami hal ini sengaja ketika pemrograman dengan array.
Jika Anda memiliki sebuah array dari 10 ints, tidak ada yang menghentikan Anda dari mencoba untuk membaca atau menulis
int 15.
Ada peringatan kompilator tidak ada atau kesalahan.
Program ini hanya kesalahan lurus ke depan dan mengakses memori
di mana ia berpikir int 15 akan, dan ini dapat menimpa variabel lain.
Dalam kasus terburuk, Anda dapat menimpa beberapa internal program Anda
mekanisme kontrol, menyebabkan program Anda untuk benar-benar menjalankan instruksi yang berbeda
dari yang Anda inginkan.
>> Sekarang, itu tidak umum untuk melakukan hal ini sengaja,
tapi ini adalah teknik yang cukup umum bahwa orang-orang jahat gunakan untuk memecahkan program
dan menempatkan kode berbahaya pada komputer orang lain.
Oleh karena itu, kita tidak bisa hanya menggunakan solusi naif kami.
Kita perlu cara untuk mencegah program kami dari menjadi rentan
untuk serangan buffer overflow.
Untuk melakukan hal ini, kita perlu memastikan bahwa buffer kita bisa tumbuh seperti yang kita baca
lebih masukan dari pengguna.
Solusinya? Kami menggunakan buffer dialokasikan tumpukan.
Karena kita dapat mengubah ukurannya dengan menggunakan mengubah ukuran fungsi realloc,
dan kami melacak dua angka - indeks slot kosong berikutnya dalam buffer
dan panjang atau kapasitas buffer.
Kita membaca dalam karakter dari pengguna satu per satu menggunakan fungsi fgetc.
Argumen fungsi fgetc mengambil - stdin - adalah referensi ke string input standar,
yang merupakan saluran masukan preconnected yang digunakan untuk mentransfer masukan pengguna
dari terminal ke program.
>> Setiap kali pengguna jenis dalam karakter baru, kita periksa untuk melihat apakah indeks
dari slot free berikutnya ditambah 1 lebih besar dari kapasitas buffer.
The +1 datang karena jika indeks bebas berikutnya adalah 5,
maka panjang buffer kita harus 6 berkat 0 pengindeksan.
Jika kita sudah kehabisan ruang dalam buffer, maka kita berusaha untuk mengubah ukurannya,
menggandakan sehingga kita mengurangi jumlah kali bahwa kita mengubah ukuran
jika pengguna mengetikkan string sangat panjang.
Jika string yang sudah terlalu lama atau jika kita kehabisan memori heap,
kita membebaskan penyangga dan nol kembali.
>> Akhirnya, kita tambahkan char ke buffer.
Setelah hits pengguna memasuki atau kembali, menandakan baris baru,
atau char khusus - control d - yang merupakan sinyal dari akhir masukan,
kita melakukan cek untuk melihat apakah pengguna benar-benar mengetik apa-apa.
Jika tidak, kita kembali nol.
Jika tidak, karena penyangga kami mungkin lebih besar daripada yang kita butuhkan,
dalam kasus terburuk itu hampir dua kali lebih besar seperti yang kita butuhkan
karena kita dua kali lipat setiap kali kita mengubah ukuran,
kita membuat salinan baru dari string hanya menggunakan jumlah ruang yang kita butuhkan.
Kami menambahkan 1 ekstra untuk panggilan malloc,
sehingga ada ruang untuk karakter khusus terminator nol - yang \ 0,
yang kita tambahkan ke string setelah kita copy di seluruh karakter,
menggunakan strncpy bukan strcpy
sehingga kita dapat menentukan dengan tepat berapa banyak karakter yang ingin kita copy.
Strcpy salinan sampai hits \ 0.
Kemudian kita membebaskan penyangga kami dan mengembalikan salinan ke pemanggil.
>> Siapa yang tahu seperti fungsi sederhana-tampak bisa begitu rumit?
Sekarang Anda tahu apa yang masuk ke perpustakaan CS50.
>> Nama saya Nate Hardison, dan ini adalah CS50.
[CS50.TV]