[Powered by Google Translate] [Bagian 5 - Lebih Nyaman] [Rob Bowden - Harvard University] [Ini adalah CS50. - CS50.TV] Seperti saya katakan dalam email saya, ada banyak hal yang dapat Anda gunakan selain alat untuk benar-benar melakukan set masalah. Kami sarankan Anda melakukannya di alat hanya karena dengan begitu kita bisa lebih mudah membantu Anda dan kita tahu bagaimana semuanya akan bekerja. Tapi sebagai salah satu contoh di mana Anda dapat melakukan hal-hal jika, katakanlah, Anda tidak memiliki akses pada alat atau Anda ingin bekerja di ruang bawah tanah Science Center - yang sebenarnya mereka memiliki alat terlalu - jika Anda ingin bekerja di mana saja. Salah satu contoh adalah apakah Anda melihat / mendengar tentang SSH? SSH pada dasarnya hanya seperti terhubung ke sesuatu. Sebenarnya, sekarang saya SSHed ke dalam alat. Saya pernah bekerja langsung di alat. Berikut adalah alat, dan jika Anda melihat di sini Anda melihat alamat IP ini. Saya pernah bekerja di alat itu sendiri; Aku selalu datang ke jendela jendela / terminal iTerm2. Anda bisa SSH ke alamat IP, ssh jharvard@192.168.129.128. Saya ingat nomor yang sangat mudah karena seperti pola yang bagus. Tapi itu akan meminta saya untuk password saya, dan sekarang aku di alat. Pada dasarnya, pada titik ini, jika Anda membuka terminal dalam alat itu sendiri, antarmuka ini, namun Anda akan menggunakannya, persis sama sebagai antarmuka saya menggunakan di sini tapi sekarang Anda SSHed. Anda tidak perlu SSH ke alat. Salah satu contoh dari tempat lain Anda bisa SSH pesan adalah Aku cukup yakin Anda memiliki secara default - Oh. Bigger. Semua Anda harus memiliki account dengan FAS default pada server FAS. Bagi saya, saya akan SSH ke rbowden@nice.fas.harvard.edu. Ini akan meminta Anda yang pertama kali, dan Anda mengatakan ya. Password saya hanya akan menjadi sandi FAS saya. Dan sekarang, aku SSHed ke server yang bagus, dan saya bisa melakukan apapun yang saya mau di sini. Banyak kelas Anda mungkin mengambil, seperti 124, akan memiliki Anda meng-upload barang-barang ke sini untuk benar-benar menyerahkan set masalah Anda. Tapi mengatakan Anda tidak memiliki akses ke alat Anda. Kemudian Anda dapat melakukan hal-hal, seperti di sini akan mengatakan - Ini hanya bagian kami pertanyaan. Ini akan meminta Anda untuk melakukan hal ini dalam alat. Sebaliknya saya hanya akan melakukannya pada server. Aku akan unzip itu. Masalahnya akan menjadi bahwa Anda terbiasa menggunakan sesuatu seperti gedit atau apa pun dalam alat. Anda tidak akan memiliki pada server FAS. Ini semua hanya akan menjadi interface ini tekstual. Jadi Anda bisa salah satu, cobalah untuk belajar editor teks yang mereka miliki. Mereka memiliki Nano. Nano biasanya cukup mudah digunakan. Anda dapat menggunakan panah Anda dan ketik normal. Jadi itu tidak sulit. Jika Anda ingin benar-benar mewah Anda dapat menggunakan Emacs, yang saya mungkin tidak seharusnya dibuka karena saya bahkan tidak tahu bagaimana menutup Emacs. Kontrol X, Kontrol C? Ya. Atau Anda bisa menggunakan Vim, yang adalah apa yang saya gunakan. Dan sehingga mereka adalah pilihan Anda. Jika Anda tidak ingin melakukan itu, Anda juga bisa, jika Anda melihat-manual.cs50.net - Oh. Pada PC, Anda dapat menggunakan SSH Putty, yang Anda akan harus men-download secara terpisah. Pada Mac, Anda bisa hanya dengan menggunakan Terminal standar atau Anda dapat men-download iTerm2, yang seperti Terminal, baik mewah. Jika Anda pergi ke manual.cs50.net Anda akan melihat link ke Notepad + +, yang adalah apa yang dapat Anda gunakan pada PC. Ini memungkinkan Anda SFTP dari Notepad + +, yang pada dasarnya SSH. Apa ini akan membiarkan Anda lakukan adalah mengedit file secara lokal, dan kemudian setiap kali Anda ingin menyimpannya, maka akan menyimpan ke nice.fas, di mana Anda kemudian dapat menjalankannya. Dan setara pada Mac akan menjadi TextWrangler. Jadi memungkinkan Anda melakukan hal yang sama. Ini memungkinkan Anda mengedit file secara lokal dan menyimpannya ke nice.fas, di mana Anda kemudian dapat menjalankannya. Jadi jika Anda pernah terjebak tanpa sebuah alat, Anda memiliki pilihan ini masih melakukan set masalah Anda. Masalah yang akan menjadi bahwa Anda tidak akan memiliki perpustakaan CS50 karena nice.fas tidak secara default memiliki. Anda dapat men-download perpustakaan CS50 - Saya tidak berpikir saya perlu bahwa pada saat ini. Anda dapat men-download perpustakaan CS50 dan salin ke nice.fas, atau saya pikir pada saat ini kita tidak menggunakannya lagi pula. Atau jika kita lakukan, Anda dapat untuk sementara waktu menggantinya dengan implementasi dari fungsi di perpustakaan CS50 pula. Sehingga tidak boleh yang banyak pembatasan. Dan itu itu. Aku akan kembali ke alat sekarang, kami akan melakukan segalanya dalam alat. Melihat bagian kami pertanyaan, di awal, seperti saya katakan di email saya, kita harus bicara tentang satu pendek Anda seharusnya untuk menonton. Kami memiliki mengarahkan & Pipa dan tiga pertanyaan. Untuk yang aliran apakah fungsi seperti printf menulis secara default? Jadi aliran. Apa sungai? Sebuah stream adalah pada dasarnya seperti itu hanya beberapa - Ini bahkan bukan sumber 1s dan 0s. Aliran itu meminta di sini adalah keluar standar. Dan sehingga standar aliran bahwa ketika Anda menulis untuk itu, muncul di layar. Keluar standar, oleh sungai, itu berarti Anda hanya menulis 1s dan 0s untuk itu, dan ujung keluar standar hanya membaca dari sungai itu. Ini hanya serangkaian 1s dan 0s. Anda dapat menulis ke sungai atau Anda dapat membaca dari stream tergantung pada apa sungai sebenarnya. Dua lainnya stream standar adalah standar dalam dan standard error. Standar adalah setiap kali Anda GetString, itu menunggu Anda untuk barang-barang input. Jadi menunggu untuk Anda, itu benar-benar menunggu standar, yang benar-benar apa yang Anda dapatkan ketika Anda mengetik di keyboard. Anda mengetik ke standar masuk Standard error pada dasarnya setara dengan keluar standar, tapi itu khusus dalam bahwa ketika Anda mencetak ke standard error, Anda seharusnya hanya mencetak pesan kesalahan itu sehingga Anda dapat membedakan antara pesan reguler dicetak ke layar dibandingkan pesan kesalahan tergantung pada apakah mereka pergi ke luar standar atau standard error. File juga. Keluar standar, standar, dan kesalahan standar aliran hanya khusus, tapi benar-benar file apapun, ketika Anda membuka file, itu menjadi aliran byte di mana Anda hanya dapat membaca dari sungai itu. Anda, untuk sebagian besar, hanya bisa memikirkan sebuah file sebagai aliran byte. Jadi aliran apa yang mereka tulis untuk secara default? Standar keluar. Apa perbedaan antara> dan >>? Apakah ada yang menonton video terlebih dahulu? Oke. > Akan menjadi bagaimana Anda redirect ke file, dan >> juga akan mengarahkan output ke file, tapi itu bukan akan menambahkan ke file. Sebagai contoh, katakanlah saya kebetulan punya dict di sini, dan hal-hal hanya dalam dict adalah kucing, kucing, anjing, ikan, anjing. Salah satu perintah yang Anda miliki di baris perintah adalah kucing, yang hanya akan mencetak apa yang ada di file. Jadi ketika saya mengatakan dict kucing, itu akan mencetak kucing, kucing, anjing, ikan, anjing. Itu semua kucing tidak. Itu berarti bahwa itu dicetak untuk keluar standar kucing, anjing, kucing, anjing ikan,. Jika saya bukan ingin mengarahkan bahwa ke file, saya dapat menggunakan> dan redirect ke file apapun adalah. Aku akan memanggil file file. Jadi sekarang jika saya ls, saya akan melihat saya memiliki sebuah file baru yang disebut file. Dan kalau aku membukanya, itu akan memiliki apa kucing dimasukkan pada baris perintah. Jadi sekarang jika saya melakukan hal itu lagi, maka itu akan mengarahkan output ke file, dan aku akan memiliki hal yang persis sama. Jadi secara teknis, itu benar-benar mengesampingkan apa yang kita miliki. Dan kita akan melihat apakah saya mengubah dict, saya mengambil anjing. Sekarang jika kita cat dict ke file lagi, kita akan memiliki versi baru dengan anjing dihapus. Jadi benar-benar mengabaikan itu. Sebaliknya, jika kita menggunakan >>, itu akan menambahkan file. Sekarang, membuka file, kita melihat kita hanya memiliki hal yang sama dua kali dicetak karena itu ada sekali, maka kita ditambahkan dengan aslinya. Apa jadi itu> dan >> lakukan. Apakah yang berikutnya bertanya - Ini tidak bertanya tentang hal itu. Yang lain yang kita miliki adalah <, yang jika> mengarahkan keluar standar, Anda lakukan 2>, yang mengarahkan standard error. Jadi jika sesuatu pergi ke standard error, itu tidak akan bisa dimasukkan ke dalam txt2. Tapi perhatikan jika saya lakukan 2>, maka masih mencetak Hello, Rob! ke baris perintah karena aku hanya mengarahkan standard error, saya tidak mengarahkan keluar standar. Standard error dan keluar standar yang berbeda. Jika Anda ingin benar-benar menulis ke standard error, maka saya bisa mengubah ini menjadi fprintf ke stderr. Jadi printf, secara default, mencetak untuk keluar standar. Jika saya ingin mencetak ke standard error secara manual, maka saya harus menggunakan fprintf dan menentukan apa yang ingin saya mencetak ke. Jika bukan aku stdout fprintf, maka itu pada dasarnya setara dengan printf. Tapi fprintf kesalahan standar. Jadi sekarang, jika saya mengarahkan ini ke txt2, Hello, Rob! masih mendapatkan dicetak pada baris perintah karena ini mendapatkan dicetak ke standard error dan aku hanya mengarahkan keluar standar. Jika sekarang saya mengarahkan standard error, sekarang tidak bisa dicetak, dan txt2 akan menjadi Hello, Rob! Jadi sekarang, Anda dapat mencetak kesalahan yang sebenarnya Anda ke standard error dan mencetak pesan biasa untuk keluar standar. Dan jadi ketika Anda menjalankan program Anda, Anda dapat menjalankannya sebagai / hello. Jenis ini dengan 2> sehingga program Anda akan berjalan normal, tetapi pesan kesalahan yang Anda dapatkan Anda dapat memeriksa nanti dalam log kesalahan Anda, sehingga kesalahan, dan kemudian melihat kemudian dan kesalahan file Anda akan memiliki kesalahan yang terjadi. Pertanyaan? Yang terakhir adalah pipa, yang dapat Anda anggap sebagai mengambil standar keluar dari satu perintah dan membuat standar dalam dari perintah selanjutnya. Contoh di sini adalah gema adalah hal baris perintah yang hanya akan echo apa pun yang saya dimasukkan sebagai argumen. Saya tidak akan meletakkan tanda kutip. Echo bla, bla, bla hanya akan mencetak bla, bla, bla. Sebelumnya, ketika saya berkata saya harus meletakkan Rob ke dalam sebuah file txt karena saya hanya bisa mengarahkan file txt, sebagai gantinya, / jika aku echo Rob dan kemudian pipa ke / halo, yang juga akan melakukan jenis yang sama hal.. Ini adalah mengambil output dari perintah ini, gema Rob, dan menggunakannya sebagai masukan untuk / hello.. Anda dapat menganggapnya sebagai peralihan yang pertama gema Rob ke dalam file dan kemudian masukan ke / hello. bahwa file yang baru saja outputted. Tapi itu butuh file sementara keluar dari gambar. Pertanyaan itu? Pertanyaan berikutnya akan melibatkan ini. Apa yang bisa Anda gunakan pipa untuk menemukan jumlah nama-nama unik dalam sebuah file yang bernama names.txt? Perintah kita akan ingin menggunakan di sini adalah unik, sehingga uniq, dan kemudian wc. Anda dapat melakukan uniq manusia untuk benar-benar melihat apa yang tidak, dan itu hanya akan menyaring garis pencocokan yang berdekatan dari input. Dan pria wc akan mencetak kata, baris, dan jumlah byte untuk setiap file. Dan yang terakhir kita akan ingin menggunakan semacam, yang akan hanya mengurutkan baris file txt. Jika saya membuat beberapa file txt, names.txt, dan itu Rob, Tommy, Yusuf, Tommy, Yusuf, RJ, Rob, apa yang ingin saya lakukan di sini adalah menemukan jumlah nama yang unik dalam file ini. Jadi apa yang harus menjadi jawabannya? >> [Mahasiswa] 4. >> Ya. Ini harus menjadi 4 sejak Rob, Tommy, Joseph, RJ adalah nama hanya unik dalam file ini. Langkah pertama, jika saya hanya melakukan jumlah kata pada names.txt, ini sebenarnya menceritakan segalanya. Ini sebenarnya pencetakan - mari kita lihat, man wc - baris, kata, dan jumlah byte. Jika saya hanya peduli tentang garis, maka saya hanya bisa melakukan wc-l names.txt. Jadi itu langkah 1. Tapi aku tidak ingin wc-l names.txt karena names.txt hanya berisi semua nama, dan saya ingin menyaring setiap non-unik yang. Jadi jika saya melakukan names.txt uniq, yang tidak cukup memberikan apa yang saya inginkan karena nama digandakan masih ada. Mengapa demikian? Mengapa uniq tidak melakukan apa yang saya inginkan? [Mahasiswa] duplikat ini tidak [tak terdengar] >> Ya. Ingat halaman manual untuk uniq mengatakan garis pencocokan saringan yang berdekatan. Mereka tidak berdekatan, sehingga tidak akan menyaring mereka. Jika saya mengurutkan terlebih dahulu, names.txt semacam ini akan menempatkan semua baris duplikat bersama-sama. Jadi sekarang names.txt semacam adalah bahwa. Aku akan ingin menggunakannya sebagai masukan untuk uniq, yang | uniq. Itu memberi saya Joseph, RJ, Rob, Tommy, dan saya ingin menggunakannya sebagai masukan untuk wc-l, yang akan memberi saya 4. Seperti dikatakan di sini, apa yang bisa Anda gunakan pipa? Anda dapat melakukan banyak hal seperti menggunakan serangkaian perintah di mana Anda menggunakan output dari satu perintah sebagai masukan untuk perintah selanjutnya. Anda dapat melakukan banyak hal, banyak hal yang cerdas. Pertanyaan? Oke. Itu saja untuk pipa dan pengalihan. Sekarang kita melanjutkan ke hal-hal yang sebenarnya, hal-hal coding. Di dalam hal ini PDF, Anda akan melihat perintah ini, dan Anda akan ingin menjalankan perintah ini dalam alat Anda. wget adalah perintah untuk hanya mendapatkan sesuatu dari internet, pada dasarnya, sehingga wget dan URL ini. Jika Anda pergi ke URL ini di browser Anda, itu akan men-download file tersebut. Saya hanya diklik di atasnya, sehingga download file untuk saya. Tapi menulis wget hal bahwa di dalam terminal hanya akan men-download ke dalam terminal. Saya memiliki section5.zip, dan Anda akan ingin unzip section5.zip, yang akan memberikan Anda sebuah folder bernama section5, yang akan memiliki semua file kita akan menggunakan hari ini di dalamnya. Sebagai nama file program ini 'menunjukkan, mereka sedikit buggy, sehingga misi Anda adalah untuk mencari tahu mengapa menggunakan gdb. Apakah setiap orang telah mereka download / tahu bagaimana untuk mendapatkan mereka download ke dalam alat mereka? Oke. Menjalankan ./buggy1, ia akan berkata Segmentation fault (inti dibuang), yang setiap kali Anda mendapatkan segfault, itu adalah hal yang buruk. Dalam keadaan apa yang Anda mendapatkan segfault? [Mahasiswa] Dereferencing pointer null. >> Ya. Jadi itu adalah salah satu contoh. Dereferencing null pointer Anda akan mendapatkan segfault. Apa segfault berarti adalah Anda menyentuh memori Anda tidak boleh menyentuh. Jadi dereferencing pointer null menyentuh alamat 0, dan pada dasarnya, semua komputer saat ini mengatakan bahwa 0 adalah alamat memori Anda tidak boleh menyentuh. Jadi itu sebabnya dereferencing sebuah hasil pointer nol dalam segfault. Bila Anda kebetulan tidak menginisialisasi pointer, maka ia memiliki nilai sampah, dan sehingga ketika Anda mencoba untuk dereference itu, kemungkinan besar Anda menyentuh memori itu di antah berantah. Jika Anda kebetulan beruntung dan nilai sampah terjadi untuk menunjuk ke suatu tempat di tumpukan atau sesuatu, maka ketika Anda dereference pointer yang yang Anda belum diinisialisasi, tidak akan salah. Tetapi jika itu menunjuk ke, katakanlah, di suatu tempat antara tumpukan dan tumpukan, atau itu hanya menunjuk ke suatu tempat yang belum digunakan oleh program anda belum, maka Anda menyentuh memori Anda tidak boleh menyentuh dan Anda segfault. Ketika Anda menulis fungsi rekursif dan recurses kali terlalu banyak dan tumpukan Anda tumbuh terlalu besar dan bertabrakan tumpukan ke hal-hal bahwa itu tidak boleh bertabrakan dengan, Anda menyentuh memori Anda tidak boleh menyentuh, sehingga Anda segfault. Itulah yang segfault ini. Ini juga alasan yang sama bahwa jika Anda memiliki string seperti - mari kita kembali ke program sebelumnya. Dalam hello.c--aku hanya akan membuat sesuatu yang lain. char * s = "hello world!"; Jika saya menggunakan * s = sesuatu atau s [0] = 'X'; sehingga membuat halo, / hello., kenapa itu segfault? Mengapa hal ini segfault? Apa yang akan Anda harapkan untuk terjadi? Jika saya melakukan printf ("% s \ n", s); apa yang Anda harapkan untuk dicetak? [Mahasiswa] X halo. >> Ya. Masalahnya adalah bahwa ketika Anda mendeklarasikan string seperti ini, s adalah sebuah pointer yang akan pergi di stack, dan apa s menunjuk ke string ini adalah yang terkandung dalam read-only memory. Jadi hanya dengan memori, nama read-only, Anda harus mendapatkan ide bahwa jika Anda mencoba untuk mengubah apa yang ada di read-only memory, Anda melakukan sesuatu yang Anda tidak harus melakukan dengan memori dan Anda segfault. Ini sebenarnya adalah sebuah perbedaan besar antara char * s dan char s []. Jadi char s [], sekarang string ini akan diletakkan di stack, dan stack tidak read-only, yang berarti bahwa ini harus bekerja baik-baik saja. Dan itu tidak. Ingatlah bahwa ketika saya melakukan char * s = "hello world!", S sendiri pada stack namun s poin ke tempat lain, dan bahwa di tempat lain terjadi menjadi read-only. Tapi char s [] hanyalah sesuatu pada stack. Jadi itu contoh lain dari segfault terjadi. Kami melihat bahwa ./buggy1 menghasilkan segfault. Secara teori, Anda tidak harus melihat buggy1.c segera. Sebaliknya, kita akan melihat melalui gdb. Perhatikan bahwa ketika Anda mendapatkan kesalahan Segmentasi (inti dibuang), Anda mendapatkan file ini di sini disebut inti. Jika kita ls-l, kita akan melihat inti yang biasanya file yang cukup besar. Ini adalah jumlah byte dari file tersebut, sehingga terlihat seperti itu 250-sesuatu kilobyte. Alasan untuk ini adalah bahwa apa yang core dump sebenarnya adalah ketika program anda crash, keadaan memori dari program Anda hanya akan disalin dan disisipkan ke dalam file ini. Ini akan dibuang ke dalam file tersebut. Program ini, sementara itu berlari, kebetulan memiliki penggunaan memori sekitar 250 kilobyte, dan itulah yang mendapat dibuang ke file ini. Sekarang Anda dapat melihat file yang jika kita lakukan gdb inti buggy1. Kami hanya bisa melakukan gdb buggy1, dan itu hanya akan start up gdb teratur, menggunakan buggy1 sebagai file input. Tapi jika Anda melakukannya gdb inti buggy1, maka itu khusus akan memulai gdb dengan melihat file inti. Dan Anda mengatakan buggy1 gdb berarti tahu bahwa file inti berasal dari program buggy1. Jadi gdb buggy1 inti akan segera membawa kita ke mana program terjadi untuk mengakhiri. Kita lihat di sini Program diakhiri dengan sinyal 11, Segmentation fault. Kami kebetulan melihat garis perakitan, yang mungkin tidak sangat membantu. Tetapi jika Anda mengetik bt atau backtrace, yang akan menjadi fungsi yang memberi kita daftar frame kita saat ini tumpukan. Jadi backtrace. Sepertinya kita hanya memiliki dua frame stack. Yang pertama adalah stack frame utama kami, dan yang kedua adalah stack frame untuk fungsi yang kita kebetulan berada di, yang sepertinya kita hanya memiliki kode assembly untuk. Jadi mari kita kembali ke dalam fungsi utama kami, dan untuk melakukan itu kita dapat melakukan frame 1, dan saya pikir kita juga bisa melakukan turun, tapi aku hampir tidak pernah melakukan turun - atau up. Ya. Up and down. Up membawa Anda naik satu stack frame, turun membawa Anda ke bawah stack frame. Saya cenderung untuk tidak pernah menggunakan. Aku hanya khusus mengatakan frame 1, yang pergi ke bingkai berlabel 1. Bingkai 1 akan membawa kita ke dalam stack frame utama, dan mengatakan di sini baris kode kita kebetulan berada di. Jika kita ingin beberapa baris lebih kode, kita dapat mengatakan daftar, dan itu akan memberi kita semua baris kode di sekitarnya. Garis kami segfaulted di 6 adalah: if (strcmp ("CS50 batu", argv [1]) == 0). Jika tidak jelas belum, Anda bisa mendapatkannya langsung dari sini hanya dengan berpikir mengapa segfaulted. Tapi kita bisa mengambil satu langkah lebih jauh dan berkata, "Mengapa argv [1] segfault?" Mari kita cetak argv [1], dan sepertinya 0x0 itu, yang merupakan pointer null. Kami strcmping CS50 batu dan nol, dan sehingga akan segfault. Dan mengapa argv [1] nol? [Mahasiswa] Karena kita tidak memberikan apapun argumen baris perintah. Ya. Kami tidak memberikan setiap argumen baris perintah. Jadi ./buggy1 hanya akan memiliki argv [0] menjadi ./buggy1. Ini tidak akan memiliki argv [1], sehingga akan segfault. Tapi jika, sebaliknya, saya hanya CS50, itu akan mengatakan Anda mendapatkan D karena itulah apa yang seharusnya dilakukan. Melihat buggy1.c, itu seharusnya untuk mencetak "Anda mendapatkan D" - Jika argv [1] tidak "CS50 batu", "Anda mendapatkan D", yang lain "Anda mendapatkan nilai A!" Jadi jika kita ingin A, kita perlu ini untuk membandingkan sebagai benar, yang berarti bahwa dibandingkan dengan 0. Jadi argv [1] perlu "CS50 batu". Jika Anda ingin melakukan itu pada baris perintah, Anda perlu menggunakan \ untuk melarikan diri ruang. Jadi CS50 \ batu dan Anda mendapatkan nilai A! Jika Anda tidak melakukan backslash, mengapa hal ini tidak bekerja? [Mahasiswa] Ini dua argumen yang berbeda. >> Ya. Argv [1] akan menjadi CS50, dan argv [2] akan menjadi batu. Oke. Sekarang ./buggy2 akan segfault lagi. Alih-alih membukanya dengan file intinya, kami hanya akan membuka buggy2 secara langsung, sehingga gdb buggy2. Sekarang jika kita hanya menjalankan program kami, maka itu akan mengatakan Program menerima sinyal SIGSEGV, yang merupakan segfault sinyal, dan ini adalah di mana hal itu terjadi terjadi. Melihat backtrace kami, kami melihat bahwa kami berada di oh_no fungsi, yang disebut oleh mungil fungsi, yang disebut oleh binky fungsi, yang disebut oleh utama. Kita juga bisa melihat argumen untuk fungsi tersebut. Argumen ke mungil dan binky adalah 1. Jika kita daftar fungsi oh_no, kita melihat bahwa oh_no hanya melakukan char ** s = NULL; * S = "BOOM"; Mengapa yang gagal? [Mahasiswa] Anda tidak dapat dereference pointer nol? >> Ya. Ini hanya mengatakan s adalah NULL, tidak peduli apakah itu terjadi menjadi char **, yang, tergantung pada bagaimana Anda menafsirkannya, itu bisa menjadi pointer ke pointer ke string atau array string. Ini s adalah NULL, maka * s dereferencing null pointer, dan jadi ini akan kecelakaan. Ini adalah salah satu cara tercepat yang bisa Anda segfault. Hanya saja mendeklarasikan pointer nol dan segera segfault. Itulah yang oh_no lakukan. Jika kita naik satu frame, maka kita akan masuk ke fungsi yang disebut oh_no. Saya perlu melakukan itu turun. Jika Anda tidak memasukkan perintah dan Anda hanya tekan Enter lagi, itu hanya akan mengulang perintah sebelumnya yang Anda berlari. Kami berada di frame 1. Listing frame ini, kita lihat di sini adalah fungsi kita. Anda dapat menekan daftar lagi, atau Anda dapat melakukan daftar 20 dan akan daftar lebih. The mungil Fungsi mengatakan jika saya adalah 1, kemudian pergi ke fungsi oh_no, lain pergi ke fungsi Slinky. Dan kita tahu i adalah 1 karena kita kebetulan melihat di sini mungil yang disebut dengan argumen 1. Atau Anda hanya dapat melakukan print saya dan akan mengatakan saya adalah 1. Kami sedang dalam mungil, dan jika kita naik frame lain, kita tahu bahwa kita akan berakhir di binky. Up. Sekarang kita berada di binky. Listing fungsi ini - daftar dari sebelum setengah memotong saya - itu dimulai seolah-olah saya adalah 0, maka kita akan menyebutnya oh_no, yang lain menghubungi mungil. Kita tahu i adalah 1, sehingga disebut mungil. Dan sekarang kita kembali main, dan main hanya akan menjadi int i = rand ()% 3; Itu hanya akan memberikan nomor acak yang baik 0, 1, atau 2. Ini akan menelepon binky dengan nomor itu, dan itu akan kembali 0. Melihat hal ini, hanya berjalan melalui program secara manual tanpa menjalankan segera, Anda akan menetapkan titik istirahat di utama, yang berarti bahwa ketika kita menjalankan program program anda berjalan sampai hits titik istirahat. Jadi menjalankan program, itu akan berjalan dan kemudian akan menekan fungsi utama dan berhenti berjalan. Sekarang kita dalam utama, dan langkah atau selanjutnya akan membawa kita ke baris berikutnya kode. Anda dapat melakukan langkah atau berikutnya. Menekan selanjutnya, sekarang saya telah diatur untuk rand ()% 3, jadi kita bisa mencetak nilai i, dan akan mengatakan saya adalah 1. Sekarang itu tidak peduli apakah kita menggunakan berikutnya atau langkah. Saya kira itu penting dalam satu sebelumnya, tapi kami ingin menggunakan selanjutnya. Jika kita menggunakan langkah, kita masuk ke fungsi, yang berarti melihat hal yang sebenarnya yang terjadi dalam binky. Jika kita menggunakan berikutnya, maka itu berarti pergi ke fungsi dan hanya pergi ke baris berikutnya kode dalam fungsi utama kami. Di sini pada baris ini, aku berada di mana dikatakan rand ()% 3; jika aku langkah, itu akan masuk ke pelaksanaan rand dan melihat apa yang terjadi di sana, dan aku bisa melangkah melalui fungsi rand. Tapi aku tidak peduli tentang fungsi rand. Aku hanya ingin pergi ke baris berikutnya kode di main, jadi saya menggunakan berikutnya. Tapi sekarang aku peduli fungsi binky, jadi saya ingin melangkah ke dalam. Sekarang aku di binky. Baris pertama dari kode yang akan mengatakan jika (i == 0), saya mengambil langkah, kita melihat kita berakhir di mungil. Jika kita hal daftar, kita melihat bahwa itu diperiksa adalah i = 0. i tidak sama dengan 0, sehingga pergi ke kondisi yang lain, yang akan menelepon mungil (i). Anda mungkin bingung. Jika Anda hanya melihat garis-garis secara langsung, Anda mungkin berpikir jika (i == 0), oke, maka saya mengambil langkah dan sekarang aku di mungil (i), Anda mungkin berpikir bahwa harus berarti i = 0 atau sesuatu. No Ini hanya berarti bahwa ia tahu itu dapat menempel langsung ke mungil line (i). Karena saya tidak 0, langkah berikutnya tidak akan berakhir di lain tersebut. Lain bukanlah garis itu akan berhenti di. Ini hanya akan pergi ke baris berikutnya itu benar-benar bisa mengeksekusi, yang mungil (i). Melangkah ke mungil (i), kita lihat apakah (i == 1). Kami tahu i = 1, jadi ketika kita melangkah, kita tahu kita akan berakhir di oh_no karena i = 1 panggilan oh_no fungsi, yang Anda dapat melangkah ke, yang akan mengatur char ** s = NULL dan segera "BOOM". Dan kemudian benar-benar melihat pelaksanaan buggy2, ini, saya hanya mendapatkan nomor acak - 0, 1, atau 2 - panggilan binky, yang jika i adalah 0 itu panggilan oh_no, yang lain itu panggilan mungil, yang muncul di sini. Jika i adalah 1, panggilan oh_no, yang lain memanggil Slinky, yang datang ke sini, jika saya adalah 2, sebut oh_no. Aku bahkan tidak berpikir ada cara - Apakah ada yang melihat cara untuk membuat ini sebuah program yang tidak akan segfault? Karena kecuali aku kehilangan sesuatu, jika i adalah 0, Anda segera akan segfault, pun Anda pergi ke fungsi yang jika i adalah 1 Anda segfault, pun Anda pergi ke fungsi di mana jika saya adalah 2 Anda segfault. Jadi tidak peduli apa yang Anda lakukan, Anda segfault. Saya kira salah satu cara untuk memperbaiki itu akan bukannya melakukan char ** s = NULL, Anda bisa malloc ruang untuk string yang. Kita bisa melakukan malloc (sizeof) - sizeof apa? [Mahasiswa] (char) * 5? >> Apakah ini tampaknya benar? Aku menduga ini akan bekerja jika saya benar-benar berlari, tapi itu bukan apa yang saya cari. Lihatlah jenis s. Mari kita tambahkan * int, sehingga int * x. Saya akan melakukan malloc (sizeof (int)). Atau jika saya ingin sebuah array dari 5, saya akan lakukan (sizeof (int) * 5); Bagaimana jika saya memiliki ** int? Apa yang akan aku malloc? [Mahasiswa] Ukuran dari pointer. >> Ya. (Sizeof (int *)); Hal yang sama di sini. Saya ingin (sizeof (char *)); Ini akan mengalokasikan ruang untuk pointer yang menunjuk "BOOM". Saya tidak perlu mengalokasikan ruang untuk "BOOM" itu sendiri karena ini pada dasarnya setara dengan apa yang saya katakan sebelumnya char * x = "BOOM". "BOOM" sudah ada. Hal ini terjadi untuk ada di wilayah read-only memori. Tapi itu sudah ada, yang berarti baris kode ini, jika s adalah char **, maka * s adalah char * dan Anda menetapkan ini char * untuk menunjuk ke "BOOM". Jika saya ingin menyalin "BOOM" ke s, maka saya akan perlu untuk mengalokasikan ruang untuk s. Saya akan melakukan * s = malloc (sizeof (char) * 5); Kenapa 5? Mengapa tidak 4? Sepertinya "BOOM" adalah 4 karakter. >> [Mahasiswa] Karakter null. Ya. Semua string Anda akan membutuhkan karakter null. Sekarang aku bisa melakukan sesuatu seperti strcat - Apa fungsi untuk menyalin string? [Mahasiswa] cpy? >> Strcpy. man strcpy. Jadi strcpy atau strncpy. strncpy sedikit lebih aman karena Anda dapat menentukan dengan tepat berapa banyak karakter, tapi di sini tidak masalah karena kita tahu. Jadi strcpy dan terlihat dalam argumen. Argumen pertama adalah tujuan kami. Argumen kedua adalah sumber kami. Kita akan menyalin ke * tujuan kami s pointer "BOOM". Mengapa Anda ingin melakukan hal ini dengan strcpy bukan hanya apa yang kita miliki sebelumnya dari * s = "BOOM"? Ada alasan Anda mungkin ingin melakukan hal ini, tetapi apa alasan itu? [Mahasiswa] Jika Anda ingin mengubah sesuatu di "BOOM". >> Ya. Sekarang aku bisa melakukan sesuatu seperti s [0] = 'X'; karena poin s ke tumpukan dan ruang yang di heap bahwa s menunjuk ke adalah pointer ke lebih banyak ruang pada tumpukan, yang menyimpan "BOOM". Jadi ini salinan "BOOM" sedang disimpan di heap. Ada teknis dua salinan "BOOM" dalam program kami. Ada yang pertama yang baru saja diberikan oleh konstanta "BOOM" string, dan salinan kedua "BOOM", strcpy menciptakan salinan "BOOM". Namun salinan "BOOM" sedang disimpan di heap, dan tumpukan Anda bebas untuk mengubah. Tumpukan tidak read-only, sehingga berarti bahwa s [0] akan membiarkan Anda mengubah nilai "BOOM". Ini akan membiarkan Anda mengubah karakter tersebut. Pertanyaan? Oke. Pindah ke buggy3, mari kita gdb buggy3. Kami hanya menjalankannya dan kita melihat kita mendapatkan segfault. Jika kita backtrace, hanya ada dua fungsi. Jika kita naik ke fungsi utama kita, kita melihat bahwa kita segfaulted pada baris ini. Jadi hanya melihat baris ini, for (int baris = 0; fgets hal ini tidak NULL tidak sama; baris + +). Frame sebelumnya kami disebut _IO_fgets. Anda akan melihat bahwa banyak dengan built-in fungsi C, bahwa ketika Anda mendapatkan segfault, akan ada nama fungsi benar-benar samar seperti ini _IO_fgets. Tapi itu akan berhubungan dengan ini panggilan fgets. Suatu tempat di dalam sini, kita segfault. Jika kita melihat argumen untuk fgets, kita dapat mencetak penyangga. Mari kita cetak sebagai - Oh, tidak. Cetak tidak akan bekerja persis seperti yang saya inginkan. Mari kita lihat program yang sebenarnya. Buffer adalah array karakter. Ini adalah array karakter dari 128 karakter. Jadi ketika saya mengatakan buffer cetak, itu akan mencetak mereka 128 karakter, yang kurasa adalah apa yang diharapkan. Apa yang saya cari adalah mencetak alamat buffer, tapi itu tidak benar-benar memberitahu saya banyak. Jadi ketika saya kebetulan mengatakan di sini x penyangga, itu menunjukkan saya 0xbffff090, yang, jika Anda ingat dari sebelumnya atau beberapa titik, Oxbffff cenderung menjadi wilayah tumpukan-ish. Tumpukan cenderung untuk memulai suatu tempat hanya di bawah 0xc000. Hanya dengan melihat alamat ini, saya tahu bahwa buffer yang terjadi pada stack. Restart program saya, berlari, naik, penyangga kita lihat adalah ini urutan karakter yang cukup banyak berarti. Kemudian mencetak file, apakah file yang terlihat seperti? [Mahasiswa] Null. >> Ya. File adalah dari jenis * FILE, sehingga pointer, dan nilai dari pointer itu adalah nol. Jadi fgets akan mencoba untuk membaca dari pointer yang secara tidak langsung, tetapi untuk mengakses pointer itu, harus dereference itu. Atau, untuk mengakses apa yang harus menunjuk ke, itu dereferences itu. Jadi itu dereferencing pointer null dan segfaults. Saya bisa restart sana. Jika kita istirahat di titik utama kami dan menjalankan, baris pertama dari kode adalah char * filename = "nonexistent.txt"; Yang seharusnya memberikan petunjuk yang cukup besar seperti mengapa program ini gagal. Mengetik berikutnya membawa saya ke baris berikutnya, di mana saya membuka file ini, dan kemudian saya langsung masuk ke baris kami, di mana setelah saya tekan berikutnya, itu akan segfault. Apakah ada yang ingin membuang alasan mengapa kita mungkin segfault? [Mahasiswa] File tidak ada. >> Ya. Hal ini seharusnya menjadi petunjuk bahwa setiap kali Anda membuka file Anda perlu memeriksa bahwa file tersebut benar-benar ada. Jadi di sini, "nonexistent.txt"; Ketika kita fopen nama file untuk membaca, maka kita perlu untuk mengatakan jika (file == NULL) dan mengatakan printf ("File tidak ada!" atau - lebih baik lagi - filename); return 1; Jadi sekarang kita periksa untuk melihat apakah itu NULL sebelum benar-benar melanjutkan dan mencoba untuk membaca dari file tersebut. Kita bisa remake itu hanya untuk melihat bahwa bekerja. Saya berniat untuk memasukkan baris baru. Jadi sekarang nonexistent.txt tidak ada. Anda harus selalu memeriksa hal semacam ini. Anda harus selalu memeriksa untuk melihat apakah fopen mengembalikan NULL. Anda harus selalu memeriksa untuk memastikan bahwa malloc tidak mengembalikan NULL, atau Anda segfault. Sekarang buggy4.c. Menjalankan. Saya menduga ini sedang menunggu masukan atau looping kemungkinan tak terbatas. Ya, itu perulangan tak terbatas. Jadi buggy4. Sepertinya kita perulangan tak terbatas. Kita bisa istirahat di utama, menjalankan program kami. Dalam gdb, asalkan singkatan yang Anda gunakan adalah ambigu atau singkatan khusus yang mereka berikan untuk Anda, maka Anda dapat menggunakan n untuk menggunakan berikutnya daripada harus mengetikkan berikutnya sepanjang jalan. Dan sekarang aku sudah memukul n sekali, saya hanya bisa menekan Enter untuk terus berikutnya daripada harus memukul n Masukkan, n Masukkan, n Enter. Sepertinya aku dalam beberapa jenis untuk loop yang menetapkan array [i] ke 0. Sepertinya saya tidak pernah melanggar keluar dari ini untuk loop. Jika saya mencetak i, jadi saya adalah 2, maka saya akan pergi berikutnya. Saya akan mencetak i, i adalah 3, maka saya akan pergi berikutnya. Saya akan mencetak i dan i adalah 3. Selanjutnya, cetak i, i adalah 4. Sebenarnya, cetak sizeof (array), sehingga ukuran dari array adalah 20. Tapi sepertinya ada beberapa perintah gdb khusus untuk pergi sampai sesuatu terjadi. Ini seperti pengaturan kondisi pada nilai variabel. Tapi aku tidak ingat apa itu. Jadi jika kita terus - Apa yang kau katakan? Apa yang Anda membawa? [Mahasiswa] Apakah menampilkan saya menambahkan - >> Ya. Jadi menampilkan saya dapat membantu. Jika kita hanya sekedar menampilkan i, itu akan disiapkan di sini apa nilai i adalah jadi saya tidak perlu mencetaknya setiap kali. Jika kita hanya terus berikutnya, kita melihat 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Sesuatu akan beres, dan saya sedang reset ke 0. Melihat buggy4.c, kita melihat semua yang terjadi adalah array int [5]; untuk (i = 0; i <= sizeof (array); i + +) array [i] = 0; Apa yang kita lihat itu salah di sini? Sebagai petunjuk, ketika saya sedang melakukan gdb buggy4 - mari kita istirahat utama, run - Aku cetak sizeof (array) hanya untuk melihat apa kondisi adalah di mana saya akhirnya harus keluar. Dimana saya? Apakah saya lari? Saya tidak menyatakan belum. Jadi mencetak sizeof (array) dan itu 20, yang diharapkan karena array saya adalah ukuran 5 dan itu dari 5 bilangan bulat, sehingga seluruh hal harus 5 * sizeof (int) byte, di mana sizeof (int) cenderung 4. Jadi sizeof (array) adalah 20. Apa yang harus begini? [Mahasiswa] Dibagi dengan sizeof (int). >> Ya, / sizeof (int). Sepertinya masih ada masalah di sini. Saya pikir ini hanya harus < karena cukup banyak selalu > [Bowden] Ya. Ketika kita akan melampaui akhir array kita, entah itu ruang yang kita override adalah mengesampingkan nilai i. Dan jadi jika kita melihat ke buggy4, istirahat utama, run, mari kita mencetak alamat dari i. Sepertinya itu bffff124. Sekarang mari kita mencetak alamat array [0]. 110. Bagaimana [1]? 114. [2], 118. 11c, 120. array [5] adalah bfff124. Jadi array [5] memiliki alamat yang sama seperti saya, yang berarti bahwa array [5] adalah i. Jika mereka memiliki alamat yang sama, mereka adalah hal yang sama. Jadi ketika kita menetapkan array [5] ke 0, kita menetapkan saya ke 0. Dan jika Anda berpikir tentang hal ini dalam hal stack, int i dinyatakan pertama, yang berarti saya mendapatkan beberapa ruang pada stack. Kemudian array [5] dialokasikan, sehingga kemudian 20 byte dialokasikan pada stack. Jadi saya akan dialokasikan terlebih dahulu, maka 20 byte mendapatkan dialokasikan. Jadi saya terjadi tepat sebelum array, dan karena cara, seperti saya katakan pekan lalu, di mana secara teknis stack tumbuh ke bawah, ketika Anda indeks ke dalam array, kita dijamin bahwa posisi 0 dalam array selalu terjadi sebelum posisi pertama dalam array. Ini adalah jenis bagaimana saya menarik minggu lalu. Perhatikan bahwa di bagian bawah kita memiliki alamat 0 dan di atas kita memiliki alamat Max. Tumpukan selalu tumbuh ke bawah. Katakanlah kita mengalokasikan i. Kami mengalokasikan bilangan bulat i, yang berarti mari kita katakan saja di sini bilangan bulat i akan dialokasikan. Kemudian kita mengalokasikan array kita dari 5 bilangan bulat, yang berarti bahwa di balik itu, karena tumpukan tumbuh ke bawah, mereka 5 bilangan bulat mendapatkan dialokasikan. Tapi karena cara array bekerja, kita dijamin bahwa posisi pertama dalam array selalu memiliki alamat kurang dari hal kedua dalam array. Jadi Array 0 posisi selalu terjadi pertama dalam memori, sedangkan array posisi 1 harus terjadi setelah itu dan array posisi 2 harus terjadi setelah itu, yang berarti bahwa 0 posisi array yang akan terjadi di suatu tempat di sini, array posisi 1 akan terjadi di atas bahwa karena bergerak naik lebih tinggi berarti alamat karena alamat maksimal di sini. Jadi array [0] di sini, array [1] di sini, array [2] di sini, array [3] di sini. Perhatikan bagaimana sebelum kita dialokasikan bulat i semua jalan di sini, seperti yang kita bergerak semakin jauh ke dalam array kita, kita semakin dekat dan lebih dekat ke integer kami i. Kebetulan array [5], yang merupakan salah satu posisi di luar array kita, adalah persis di mana bilangan bulat saya kebetulan dialokasikan. Jadi itulah titik di mana kita kebetulan memukul ruang pada stack yang dialokasikan untuk integer i, dan kami sedang menyiapkan bahwa untuk 0. Itulah cara yang bekerja. Pertanyaan? Ya. [Mahasiswa] Sudahlah. Oke. [Mahasiswa] Bagaimana Anda menghindari kesalahan semacam ini? Ini semacam kesalahan? Jangan menggunakan C sebagai bahasa pemrograman Anda. Gunakan bahasa yang memiliki batas array memeriksa. Selama Anda berhati-hati, Anda hanya perlu menghindari pergi melewati batas-batas array Anda. [Mahasiswa] Jadi di sini ketika kami pergi melewati batas-batas array Anda - [Bowden] situlah sesuatu mulai tidak beres. >> [Mahasiswa] Oh, oke. Selama Anda tinggal di dalam memori dialokasikan untuk array Anda, Anda baik-baik saja. Tapi C tidak melakukan pengecekan error. Jika saya melakukan array [1000], dengan senang hati akan hanya memodifikasi apapun yang terjadi - It goes ke awal array, kemudian pergi setelah posisi 1.000 dan set ke 0. Ia tidak melakukan pengecekan bahwa oh, ini tidak benar-benar memiliki 1000 hal di dalamnya. 1000 adalah jauh melampaui apa yang saya harus berubah, sedangkan Jawa atau sesuatu yang Anda akan mendapatkan berbagai keluar dari indeks batas atau indeks keluar dari pengecualian batas. Itu sebabnya banyak bahasa tingkat tinggi memiliki hal-hal di mana jika Anda melampaui batas-batas array, Anda gagal sehingga Anda tidak bisa mengubah hal-hal dari bawah Anda dan kemudian hal-hal pergi jauh lebih buruk daripada hanya mendapatkan pengecualian mengatakan bahwa Anda melampaui akhir array. [Mahasiswa] Dan harus kita baru saja mengubah <= hanya > [Bowden] Ya. Ini harus > [Mahasiswa] Kanan. Lebih banyak pertanyaan? Oke. [Mahasiswa] Saya punya pertanyaan. >> Ya. [Mahasiswa] Apa variabel array yang sebenarnya? [Bowden] Seperti apa adalah array? Array itu sendiri adalah simbol. Ini hanya alamat awal 20 byte yang kita referensi. Anda dapat menganggapnya sebagai pointer, tetapi pointer konstan. Segera setelah hal mendapatkan dikompilasi, array variabel tidak ada lagi. [Mahasiswa] Jadi bagaimana cara menemukan ukuran array? Ukuran array mengacu pada ukuran blok bahwa simbol yang mengacu pada. Ketika saya melakukan sesuatu seperti printf ("% p \ n", array); mari kita menjalankannya. Apa yang saya lakukan salah? 'Array' Array dideklarasikan di sini. Oh, di sini. Clang pintar, dan hal itu terjadi untuk melihat bahwa saya menyatakan array sebagai elemen 5 tapi aku mengindeks ke posisi 1000. Hal ini dapat melakukan hal itu karena ini hanya konstanta. Ini hanya bisa pergi sejauh ini menyadari bahwa aku akan melampaui batas-batas array. Tapi perhatikan sebelum ketika kami memiliki saya tidak benar, itu tidak mungkin menentukan berapa banyak nilai-nilai saya bisa mengambil, sehingga tidak dapat menentukan bahwa saya akan melampaui akhir array. Itu hanya dentang menjadi pintar. Tapi sekarang membuat buggy4. Jadi apa lagi yang saya lakukan salah? Secara implisit menyatakan fungsi perpustakaan 'printf'. Aku akan ingin # include. Oke. Sekarang berjalan buggy4. Mencetak nilai dari array seperti yang kulakukan di sini, mencetaknya sebagai pointer cetakan sesuatu yang tampak seperti ini - bfb8805c - yang adalah alamat beberapa itu di wilayah tumpukan-ish. Array itu sendiri adalah seperti pointer, tetapi bukan merupakan pointer yang sebenarnya, sejak pointer biasa kita dapat berubah. Array adalah hanya beberapa konstan. The 20 blok memori mulai dari alamat 0xbfb8805c. Jadi bfb8805c melalui alamat ini +20--atau saya kira -20 - adalah semua memori dialokasikan untuk array ini. Array, variabel itu sendiri tidak disimpan di mana saja. Bila Anda kompilasi, compiler - tangan gelombang pada itu - tapi compiler hanya akan menggunakan mana tahu array untuk menjadi. Ia tahu di mana array dimulai, dan sehingga dapat selalu saja melakukan hal-hal dalam hal offset dari awal itu. Itu tidak membutuhkan variabel itu sendiri untuk mewakili array. Tapi ketika saya melakukan sesuatu seperti int * p = Array, sekarang p adalah pointer yang menunjuk ke array, dan sekarang p sebenarnya tidak ada di stack. Aku bebas untuk mengubah p. Aku bisa melakukan p = malloc. Jadi awalnya menunjuk ke array, sekarang menunjuk ke beberapa ruang di heap. Saya tidak bisa melakukan berbagai malloc =. Jika dentang pintar, ia akan berteriak padaku langsung dari kelelawar. Sebenarnya, aku cukup yakin gcc akan melakukan hal ini juga. Jadi tipe array 'int [5]' tidak dialihkan. Anda tidak dapat menetapkan sesuatu ke sebuah tipe array karena array hanyalah sebuah konstanta. Ini adalah simbol yang referensi yang 20 byte. Aku tidak bisa mengubahnya. [Mahasiswa] Dan di mana ukuran dari array disimpan? [Bowden] Ini tidak disimpan di mana saja. Justru ketika itu kompilasi. Jadi mana ukuran array disimpan? Anda hanya dapat menggunakan sizeof (array) dalam fungsi yang array dideklarasikan sendiri. Jadi jika saya melakukan beberapa fungsi, foo, dan saya lakukan (int array []) printf ("% d \ n", sizeof (array)); dan kemudian di sini saya sebut foo (array); dalam fungsi ini - mari kita menjalankannya. Ini adalah dentang menjadi pintar lagi. Ia memberi tahu saya bahwa sizeof pada parameter array fungsi akan kembali ukuran '* int'. Ini akan menjadi kesalahan jika itu bukan apa yang saya inginkan untuk terjadi. Mari kita benar-benar mematikan Werror. Peringatan. Peringatan baik-baik saja. Ini masih akan mengkompilasi asalkan memiliki peringatan. . / A.out akan mencetak 4. Peringatan yang dihasilkan merupakan indikator yang jelas tentang apa yang salah. Ini array int hanya akan mencetak sizeof (int *). Bahkan jika saya menempatkan array [5] di sini, itu masih hanya akan mencetak sizeof (int *). Jadi segera setelah Anda lulus ke fungsi, perbedaan antara array dan pointer tidak ada. Hal ini terjadi untuk menjadi sebuah array yang dideklarasikan pada stack, tetapi segera setelah kami melewati nilai tersebut, bahwa 0xBF bla, bla, bla ke dalam fungsi ini, maka pointer ini menunjuk ke array di stack. Jadi itu berarti bahwa sizeof hanya berlaku dalam fungsi yang array dideklarasikan, yang berarti bahwa ketika Anda melakukan compile fungsi ini, ketika dentang berjalan melalui fungsi ini, ia melihat array adalah array int ukuran 5. Jadi itu melihat sizeof (array). Nah, itu 20. Itu sebenarnya bagaimana sizeof pada dasarnya bekerja untuk hampir semua kasus. Sizeof bukan fungsi, melainkan operator. Anda tidak memanggil fungsi sizeof. Sizeof (int), compiler hanya akan menerjemahkan ke 4. Got it? Oke. [Mahasiswa] Jadi apa perbedaan antara sizeof (array) di utama dan di foo? Ini karena kita katakan sizeof (array), yang adalah tipe int *, sedangkan array di sini adalah bukan dari * tipe int, itu sebuah array int. [Mahasiswa] Jadi jika Anda memiliki parameter dalam array [] bukan array * int, akan itu berarti bahwa Anda masih bisa mengubah berbagai karena sekarang itu pointer? [Bowden] Seperti ini? >> [Mahasiswa] Ya. Dapatkah Anda mengubah susunan dalam fungsi sekarang? [Bowden] Anda bisa mengubah array dalam kedua kasus. Dalam kedua kasus, Anda bebas untuk mengatakan array [4] = 0. [Mahasiswa] Tapi Anda bisa membuat titik array ke sesuatu yang lain? [Bowden] Oh. Ya. Dalam kedua kasus - >> [mahasiswa] Ya. [Bowden] Perbedaan antara array [] dan sebuah array int *, tidak ada. Anda juga bisa mendapatkan beberapa array multidimensi di sini untuk beberapa sintaks yang nyaman, tapi masih hanya pointer. Ini berarti bahwa saya bebas untuk melakukan array = malloc (sizeof (int)), dan sekarang menunjukkan di tempat lain. Tapi seperti bagaimana ini bekerja selamanya dan selalu, mengubah array ini dengan membuatnya menunjuk ke sesuatu yang lain tidak mengubah array ini di sini karena salinan argumen, itu bukan pointer ke argumen itu. Dan sebenarnya, seperti indikasi lagi bahwa itu persis sama - kita sudah melihat cetakan pencetakan apa yang array - bagaimana kalau kita mencetak alamat dari array atau alamat dari alamat dari array ke salah dari mereka? Mari kita mengabaikan satu ini. Oke. Ini baik-baik saja. Ini sekarang berjalan / a.out.. Array Printing, kemudian mencetak alamat dari array, adalah hal yang sama. Array hanya tidak ada. Ia tahu ketika Anda mencetak array, Anda mencetak simbol yang mengacu pada 20 byte. Mencetak alamat array, baik, array tidak ada. Ia tidak memiliki alamat, sehingga hanya mencetak alamat tersebut 20 byte. Segera setelah Anda mengkompilasi bawah, seperti di buggy4 Anda dikompilasi / a.out., array tidak ada. Pointer ada. Array tidak. Blok memori yang mewakili array masih ada, tetapi variabel array dan variabel jenis yang tidak ada. Mereka adalah seperti perbedaan utama antara array dan pointer adalah sebagai segera setelah Anda membuat panggilan fungsi, tidak ada perbedaan. Namun dalam fungsi bahwa array itu sendiri dinyatakan, sizeof bekerja secara berbeda karena Anda mencetak ukuran blok bukan ukuran jenis, dan Anda tidak bisa mengubahnya karena itu simbol. Mencetak hal dan alamat dari hal yang mencetak hal yang sama. Dan itu cukup banyak itu. [Mahasiswa] Bisakah Anda mengatakan bahwa sekali lagi? Saya mungkin telah melewatkan sesuatu. Percetakan array dan alamat array mencetak hal yang sama, sedangkan jika Anda mencetak pointer versus alamat pointer, satu hal mencetak alamat dari apa yang Anda menunjuk ke, yang lain mencetak alamat dari pointer pada stack. Anda dapat mengubah pointer, Anda tidak dapat mengubah simbol array. Dan pointer sizeof akan mencetak ukuran jenis pointer. Jadi int * p sizeof (p) akan mencetak 4, tapi int array [5] cetak sizeof (array) akan mencetak 20. [Mahasiswa] Jadi int array [5] akan mencetak 20? >> Ya. Itulah sebabnya dalam buggy4 ketika digunakan untuk menjadi sizeof (array) ini melakukan i <20, yang tidak apa yang kita inginkan. Kami ingin i <5. >> [Mahasiswa] Oke. [Bowden] Dan kemudian segera setelah Anda mulai melewati dalam fungsi, jika kita melakukan int * p = array; dalam fungsi ini, pada dasarnya kita dapat menggunakan p dan array yang persis dengan cara yang sama, kecuali untuk masalah sizeof dan masalah perubahan. Tapi p [0] = 1; adalah sama dengan mengatakan array [0] = 1; Dan segera setelah kami katakan foo (array), atau foo (p); dalam fungsi foo, ini adalah panggilan yang sama dua kali. Tidak ada perbedaan antara kedua panggilan. Baik pada semua orang? Oke. Kami memiliki 10 menit. Kami akan mencoba untuk mendapatkan melalui program typer Hacker, website ini, yang keluar tahun lalu atau sesuatu. Hanya saja seharusnya seperti Anda mengetik secara acak dan mencetak - Apapun file itu terjadi telah dimuat adalah apa yang tampak seperti Anda sedang mengetik. Sepertinya semacam kode sistem operasi. Itu yang kita ingin menerapkan. Anda harus memiliki executable biner bernama hacker_typer yang mengambil dalam satu argumen, file tersebut ke "jenis hacker." Menjalankan eksekusi harus menghapus layar dan kemudian mencetak satu karakter dari file berlalu-in setiap kali pengguna menekan tombol. Jadi apapun yang Anda menekan tombol, harus membuang dan bukannya mencetak karakter dari file itu adalah argumen. Aku akan cukup banyak memberitahu Anda apa hal-hal yang kita akan perlu tahu adalah. Tapi kami ingin memeriksa perpustakaan termios. Saya tidak pernah menggunakan perpustakaan ini sepanjang hidup saya, sehingga memiliki tujuan yang sangat minim. Tapi ini akan menjadi perpustakaan bisa kita gunakan untuk membuang karakter anda menekan ketika Anda mengetik ke standar masuk Jadi hacker_typer.c, dan kita akan ingin # include. Melihat halaman manual untuk termios - aku menebak terminal itu OS atau sesuatu - Saya tidak tahu bagaimana membacanya. Melihat hal ini, ia mengatakan untuk menyertakan 2 file, jadi kita akan melakukan itu. Hal pertama yang pertama, kami ingin mengambil satu argumen, yang merupakan file kita harus membuka. Jadi apa yang saya ingin lakukan? Bagaimana cara memeriksa untuk melihat saya memiliki satu argumen? [Mahasiswa] Jika argc sama itu. >> [Bowden] Ya. Jadi, jika (argc = 2!) Printf ("penggunaan:% s [file untuk membuka]"). Jadi sekarang jika saya menjalankan ini tanpa memberikan argumen kedua - oh, saya memerlukan baris baru - Anda akan melihat ia mengatakan penggunaan: / hacker_typer,. dan kemudian argumen kedua harus file saya ingin membuka. Sekarang apa yang harus saya lakukan? Saya ingin membaca dari file ini. Bagaimana cara membaca dari file? [Mahasiswa] Anda membukanya terlebih dahulu. >> Ya. Jadi fopen. Apa fopen terlihat seperti? [Mahasiswa] Filename. >> [Bowden] Nama file akan menjadi argv [1]. [Mahasiswa] Dan kemudian apa yang ingin Anda lakukan dengan itu, sehingga - >> [Bowden] Ya. Jadi jika Anda tidak ingat, Anda hanya bisa melakukan fopen man, di mana itu akan menjadi jalur * const char mana jalan adalah nama file, const * modus char. Jika Anda kebetulan tidak ingat apa modus ini, maka Anda dapat mencari modus. Di dalam halaman manual, karakter garis miring adalah apa yang dapat Anda gunakan untuk mencari sesuatu. Jadi saya ketik / modus untuk mencari modus. n dan N adalah apa yang dapat Anda gunakan untuk siklus melalui pertandingan pencarian. Di sini dikatakan poin argumen mode untuk string dimulai dengan salah satu urutan berikut. Jadi r, file teks Terbuka untuk membaca. Itulah yang kami ingin lakukan. Untuk membaca, dan saya ingin menyimpan itu. Hal ini akan menjadi * FILE. Sekarang apa yang ingin saya lakukan? Beri aku detik. Oke. Sekarang apa yang ingin saya lakukan? [Mahasiswa] Periksa apakah itu NULL. >> [Bowden] Ya. Setiap kali Anda membuka file, pastikan bahwa Anda berhasil dapat membukanya. Sekarang saya ingin melakukan hal-hal termios mana saya ingin pertama kali membaca pengaturan saya saat ini dan menyelamatkan mereka menjadi sesuatu, maka saya ingin mengubah setting saya membuang setiap karakter yang saya ketik, dan kemudian saya ingin memperbarui pengaturan tersebut. Dan kemudian pada akhir program, saya ingin mengubah kembali ke pengaturan asli saya. Jadi struct akan menjadi termios jenis, dan aku akan ingin dua dari mereka. Yang pertama akan menjadi current_settings saya, dan kemudian mereka akan menjadi hacker_settings saya. Pertama, aku akan ingin menyimpan pengaturan saya saat ini, maka aku akan ingin memperbarui hacker_settings, dan kemudian jalan pada akhir program saya, saya ingin kembali ke pengaturan saat ini. Jadi menyimpan pengaturan saat ini, cara yang bekerja, kita termios manusia. Kita melihat bahwa kita memiliki tcsetattr int, int tcgetattr. Saya lulus dalam struct termios oleh pointer. Cara ini akan terlihat adalah - aku sudah lupa apa fungsi dipanggil. Salin dan tempel. Jadi tcgetattr, maka saya ingin lulus dalam struct bahwa aku menyimpan informasi dalam, yang akan menjadi current_settings, dan argumen pertama adalah file descriptor untuk hal yang saya ingin menyimpan atribut. Apa file descriptor adalah seperti setiap kali Anda membuka file, ia mendapat file descriptor. Ketika saya fopen argv [1], ia mendapat file descriptor yang Anda referensi setiap kali Anda ingin membaca atau menulis untuk itu. Itu bukan file descriptor saya ingin gunakan di sini. Ada tiga deskriptor file yang Anda miliki secara default, yang standar di, keluar standar, dan standard error. Secara default, saya pikir itu adalah standar di 0, keluar standar 1, dan kesalahan standar 2. Jadi apa yang saya ingin mengubah pengaturan? Saya ingin mengubah pengaturan setiap kali aku memukul karakter, Aku ingin membuang karakter yang pergi bukannya mencetak ke layar. Apa aliran - standar dalam, keluar standar, atau standard error - menanggapi hal-hal ketika saya mengetik di keyboard? >> [Mahasiswa] Standar masuk >> Ya. Jadi saya dapat melakukan 0 atau aku bisa melakukan stdin. Saya mendapatkan current_settings standar masuk Sekarang saya ingin memperbarui pengaturan tersebut, jadi pertama saya akan menyalin ke hacker_settings apa current_settings saya. Dan bagaimana structs kerja itu hanya akan menyalin. Ini salinan semua bidang, seperti yang Anda harapkan. Sekarang saya ingin memperbarui beberapa bidang. Melihat termios, Anda harus membaca banyak ini hanya untuk melihat apa yang akan Anda ingin mencari, tapi bendera Anda akan ingin mencari adalah gema, sehingga ECHO Echo karakter masukan. Pertama saya ingin mengatur - aku sudah lupa apa bidang yang. Ini adalah apa yang tampak seperti struct. Jadi mode masukan saya pikir kita ingin mengubah. Kita akan melihat solusi untuk memastikan bahwa apa yang ingin kita ubah. Kami ingin mengubah lflag untuk mencegah perlu untuk melihat melalui semua ini. Kami ingin mengubah mode lokal. Anda harus membaca ini seluruh hal untuk memahami di mana segala sesuatu adalah milik bahwa kita ingin mengubah. Tapi itu dalam mode lokal di mana kita akan ingin mengubah itu. Jadi hacker_settings.cc_lmode adalah apa namanya. c_lflag. Di sinilah kita masuk ke operator bitwise. Kami agak kehabisan waktu, tapi kami akan pergi melalui itu cepat. Di sinilah kita masuk ke operator bitwise, di mana saya pikir saya mengatakan satu lama bahwa setiap kali Anda mulai berurusan dengan bendera, Anda akan menggunakan bitwise operator yang banyak. Setiap bit dalam bendera sesuai dengan semacam perilaku. Jadi di sini, bendera ini memiliki banyak hal yang berbeda, di mana mereka semua berarti sesuatu yang berbeda. Tapi apa yang saya ingin lakukan adalah hanya mematikan bit yang sesuai dengan ECHO. Jadi untuk matikan itu saya lakukan & = ¬ ECHO. Sebenarnya, saya pikir itu seperti Techo atau sesuatu. Aku hanya akan memeriksa lagi. Saya bisa termios itu. Hanya saja ECHO. ECHO akan menjadi satu bit. ¬ ECHO akan berarti semua bit diatur ke 1, yang berarti semua flag diatur ke true kecuali untuk bit ECHO. Dengan mengakhiri bendera lokal saya dengan ini, itu berarti semua bendera yang saat ini diatur ke benar masih akan diatur ke true. Jika bendera ECHO saya diatur ke benar, maka ini tentu diatur ke false pada bendera ECHO. Jadi baris kode ini hanya mematikan bendera ECHO. Garis lain dari kode, saya hanya akan menyalinnya untuk kepentingan waktu dan kemudian menjelaskan mereka. Dalam larutan, katanya 0. Ini mungkin lebih baik untuk secara eksplisit mengatakan stdin. Perhatikan bahwa saya juga melakukan ECHO | ICANON sini. ICANON mengacu pada sesuatu yang terpisah, yang berarti modus kanonik. Apa artinya modus kanonik biasanya ketika Anda mengetik baris perintah, standar tidak memproses apapun sampai Anda mencapai baris baru. Jadi, ketika Anda GetString, Anda mengetik banyak hal, maka anda menekan baris baru. Itulah ketika itu dikirim ke standar masuk Itulah default. Ketika saya mematikan modus kanonik, sekarang setiap karakter tunggal Anda menekan adalah apa yang akan diproses, yang biasanya jenis buruk karena itu lambat untuk memproses hal-hal ini, itulah sebabnya mengapa ada baiknya untuk buffer ke seluruh baris. Tapi aku ingin setiap karakter untuk diproses karena saya tidak ingin menunggu saya untuk memukul newline sebelum memproses semua karakter saya telah mengetik. Ini akan menonaktifkan modus kanonik. Hal ini hanya berarti padahal sebenarnya proses karakter. Ini berarti proses mereka segera, secepat saya mengetik mereka, mengolahnya. Dan ini adalah fungsi yang memperbarui pengaturan saya untuk standar, dan sarana TCSA melakukannya sekarang. Pilihan yang lain menunggu sampai segala sesuatu yang saat ini di sungai diproses. Itu tidak terlalu penting. Hanya sekarang mengubah pengaturan saya untuk menjadi apa pun saat ini sedang dalam hacker_typer_settings. Saya kira saya menyebutnya hacker_settings, jadi mari kita mengubah itu. Mengubah segalanya untuk hacker_settings. Sekarang di akhir program kami kita akan ingin kembali untuk apa saat ini dalam normal_settings, yang akan hanya terlihat seperti & normal_settings. Perhatikan Aku tidak berubah salah satu normal_settings saya sejak awalnya mendapatkan itu. Kemudian untuk hanya mengubah mereka kembali, saya melewati mereka kembali di akhir. Ini adalah pembaruan. Oke. Sekarang dalam sini saya hanya akan menjelaskan kode untuk kepentingan waktu. Ini bukan bahwa kode banyak. Kita melihat kita membaca karakter dari file tersebut. Kami menyebutnya f. Sekarang Anda bisa manusia fgetc, tapi bagaimana fgetc akan bekerja hanya itu akan mengembalikan karakter yang baru saja Anda baca atau EOF, yang sesuai dengan akhir file atau beberapa terjadi kesalahan. Kami looping, terus membaca karakter tunggal dari file tersebut, sampai kita sudah kehabisan karakter untuk dibaca. Dan sementara kita melakukan itu, kita menunggu satu karakter dari standar masuk Setiap kali Anda mengetik sesuatu di baris perintah, yang membaca karakter dari standar masuk Kemudian putchar hanya akan menempatkan char kita baca di sini dari file ke luar standar. Anda dapat man putchar, tapi itu hanya menempatkan standar keluar, itu mencetak bahwa karakter. Anda juga bisa hanya melakukan printf ("% c", c); ide yang sama. Itu akan melakukan sebagian besar pekerjaan kami. Hal terakhir yang kita akan ingin lakukan adalah hanya fclose file kita. Jika Anda tidak fclose, itu adalah kebocoran memori. Kami ingin fclose file yang kita awalnya dibuka, dan saya pikir itu saja. Jika kita membuat itu, saya sudah punya masalah. Mari kita lihat. Apa itu mengeluh tentang? Diharapkan 'int' tapi argumen adalah tipe 'struct _IO_FILE *'. Kita akan melihat apakah yang bekerja. Hanya diperbolehkan dalam C99. Augh. Oke, membuat hacker_typer. Sekarang kita mendapatkan gambaran yang lebih berguna. Jadi penggunaan undeclared identifier 'normal_settings'. Saya tidak menyebutnya normal_settings. Saya menyebutnya current_settings. Jadi mari kita mengubah semua itu. Sekarang melewatkan argumen. Aku akan membuat 0 ini untuk saat ini. Oke. / Hacker_typer. Cp.c. Saya juga tidak menghapus layar di awal. Tapi Anda bisa melihat kembali ke set masalah terakhir untuk melihat bagaimana Anda membersihkan layar. Hanya saja mencetak beberapa karakter sementara ini adalah melakukan apa yang ingin saya lakukan. Oke. Dan berpikir tentang mengapa hal ini perlu 0 bukan stdin, yang harus # define 0, ini mengeluh bahwa - Sebelum ketika saya mengatakan bahwa ada file deskriptor tapi kemudian Anda juga memiliki FILE * Anda, file descriptor hanyalah satu bilangan bulat, sedangkan * FILE memiliki sejumlah besar barang-barang yang terkait dengannya. Alasan kita perlu dikatakan 0 bukan stdin adalah stdin itu adalah * FILE yang menunjuk ke hal yang referensi file descriptor 0. Jadi bahkan sampai di sini ketika saya melakukan fopen (argv [1], saya mendapatkan * FILE kembali. Tapi di suatu tempat dalam * FILE adalah hal yang sesuai dengan file descriptor untuk file itu. Jika Anda melihat halaman manual untuk terbuka, jadi saya pikir Anda harus melakukan man 3 terbuka - tidak - man 2 terbuka - ya. Jika Anda melihat halaman untuk terbuka, terbuka seperti fopen tingkat rendah, dan itu mengembalikan descriptor file yang sebenarnya. fopen melakukan banyak hal di atas terbuka, yang bukannya kembali hanya saja file descriptor mengembalikan seluruh FILE pointer * di dalamnya ada file descriptor kecil kami. Jadi standar dalam mengacu pada hal * FILE, sedangkan 0 mengacu hanya standar file descriptor dalam dirinya sendiri. Pertanyaan? [Tertawa] Meniup melalui itu. Baiklah. Kita sudah selesai. [Tertawa] [CS50.TV]