[Powered by Google Translate] [Bagian 4 - Lebih Nyaman]
[Rob Bowden - Harvard University]
[Ini adalah CS50. - CS50.TV]
Kami memiliki besok kuis, dalam kasus kalian tidak tahu itu.
Hal ini pada dasarnya pada semua yang anda bisa melihat di kelas atau seharusnya melihat di kelas.
Itu termasuk pointer, meskipun mereka topik yang sangat baru-baru ini.
Anda setidaknya harus memahami tingkat tinggi dari mereka.
Apa pun yang pergi lebih di kelas Anda harus memahami untuk kuis.
Jadi jika Anda memiliki pertanyaan pada mereka, Anda dapat meminta mereka sekarang.
Tapi ini akan menjadi sesi yang sangat dipimpin mahasiswa di mana kalian mengajukan pertanyaan,
jadi mudah-mudahan orang memiliki pertanyaan.
Apakah ada yang punya pertanyaan?
Ya. >> [Mahasiswa] Dapatkah Anda pergi ke pointer lagi?
Aku akan pergi ke pointer.
Semua variabel Anda selalu hidup dalam memori,
tetapi biasanya Anda tidak perlu khawatir tentang itu dan Anda hanya mengatakan x + 2 dan y + 3
dan compiler akan mencari tahu di mana hal-hal yang hidup untuk Anda.
Setelah Anda sedang berhadapan dengan pointer, sekarang kau eksplisit menggunakan alamat tersebut memori.
Jadi variabel tunggal hanya akan pernah tinggal di satu alamat pada waktu tertentu.
Jika kita ingin mendeklarasikan pointer, apa jenis akan terlihat seperti?
>> Saya ingin menyatakan p pointer. Apa jenis terlihat seperti?
[Mahasiswa] int * p. >> Ya. Jadi int * p.
Dan bagaimana cara membuatnya menunjuk ke x? >> [Mahasiswa] Ampersand.
[Bowden] Jadi ampersand secara harfiah disebut alamat operator.
Jadi ketika saya mengatakan & x itu mendapatkan alamat memori dari variabel x.
Jadi sekarang aku punya p pointer, dan di mana saja di kode saya, saya dapat menggunakan p *
atau saya bisa menggunakan x dan itu akan menjadi hal yang sama persis.
(* P). Apa ini lakukan? Apa Bintang artinya?
[Mahasiswa] Ini berarti nilai pada saat itu. >> Ya.
Jadi jika kita melihatnya, itu bisa sangat berguna untuk menarik keluar diagram
di mana ini adalah sebuah kotak kecil dari memori untuk x, yang kebetulan memiliki nilai 4,
maka kita memiliki sebuah kotak kecil dari memori untuk p,
dan sebagainya poin p ke x, jadi kita menarik panah dari p ke x.
Jadi ketika kita mengatakan * p kita katakan pergi ke kotak yang p.
Star adalah mengikuti panah dan kemudian melakukan apa pun yang Anda inginkan dengan kotak yang di sana.
Jadi bisa saya katakan * p = 7, dan yang akan masuk ke kotak yang x dan perubahan yang ke 7.
Atau aku bisa mengatakan int z = * p * 2; Itu membingungkan karena bintang itu, bintang.
Bintang satu dereferencing p, bintang lain mengalikan dengan 2.
Perhatikan Aku bisa saja juga menggantikan p * dengan x.
Anda dapat menggunakannya dengan cara yang sama.
Dan kemudian nanti saya bisa memiliki titik p ke hal yang sama sekali baru.
Saya hanya dapat mengatakan p = &z;
Jadi sekarang p ada poin lagi untuk x; menunjuk ke z.
Dan setiap kali saya melakukan p * itu sama dengan melakukan z.
Jadi hal yang berguna tentang ini adalah sekali kita mulai masuk ke fungsi.
>> Ini semacam berguna untuk menyatakan pointer yang menunjuk ke sesuatu
dan kemudian Anda hanya dereferencing itu
ketika Anda bisa menggunakan variabel asli untuk memulai.
Tapi ketika Anda masuk ke fungsi - jadi mari kita mengatakan bahwa kita memiliki beberapa fungsi, int foo,
yang mengambil pointer dan hanya melakukan * p = 6;
Seperti yang kita lihat sebelumnya dengan swap, Anda tidak dapat melakukan swap yang efektif dan fungsi yang terpisah
dengan hanya melewati bilangan bulat karena segala sesuatu di C selalu lewat nilai.
Bahkan ketika Anda melewati pointer Anda sedang lewat nilai.
Kebetulan bahwa nilai-nilai adalah alamat memori.
Jadi ketika saya mengatakan foo (p), saya sedang melewati pointer ke fungsi foo
dan kemudian melakukan foo * p = 6;
Jadi dalam fungsi itu, * p masih setara dengan x,
tapi saya tidak dapat menggunakan x dalam fungsi yang karena itu tidak scoped dalam fungsi itu.
Jadi * p = 6 adalah satu-satunya cara saya dapat mengakses variabel lokal dari fungsi lain.
Atau, juga, pointer adalah satu-satunya cara saya dapat mengakses variabel lokal dari fungsi lain.
[Mahasiswa] Mari mengatakan Anda ingin kembali pointer. Bagaimana tepatnya Anda melakukannya?
[Bowden] Kembali pointer seperti dalam sesuatu seperti int y = 3, kembali & y? >> [Mahasiswa] Ya.
[Bowden] Oke. Anda tidak harus melakukan ini. Ini buruk.
Saya pikir saya melihat dalam slide kuliah Anda mulai melihat ini diagram seluruh memori
di mana di sini Anda punya alamat memori 0
dan di sini Anda memiliki alamat memori 4 gigs atau 2 ke 32.
Jadi Anda punya beberapa hal dan beberapa barang dan kemudian Anda memiliki tumpukan Anda
dan Anda punya tumpukan Anda, yang Anda baru saja mulai belajar tentang, tumbuh dewasa.
[Mahasiswa] Bukankah tumpukan di atas tumpukan?
>> Ya. Tumpukan adalah di atas, bukan? >> [Mahasiswa] Yah, ia meletakkan 0 di atas.
[Mahasiswa] Oh, ia meletakkan 0 di atas. >> [Mahasiswa] Oh, oke.
Disclaimer: Di mana saja dengan CS50 Anda akan melihatnya dengan cara ini. >> [Mahasiswa] Oke.
Hanya saja ketika Anda pertama kali melihat tumpukan,
seperti ketika Anda memikirkan tumpukan Anda memikirkan hal-hal yang menumpuk di atas satu sama lain.
Jadi kita cenderung untuk flip ini sekitar sehingga tumpukan tersebut tumbuh seperti stack biasanya akan
bukannya stack tergantung ke bawah. >> [Mahasiswa] Jangan tumpukan teknis tumbuh juga, meskipun?
Itu tergantung pada apa yang Anda maksud dengan tumbuh.
Tumpukan dan tumpukan selalu tumbuh dalam arah yang berlawanan.
Sebuah tumpukan selalu tumbuh dalam arti bahwa itu tumbuh
menuju alamat memori yang lebih tinggi, dan tumpukan tumbuh ke bawah
di bahwa itu berkembang menuju alamat memori yang lebih rendah.
Jadi atas adalah 0 dan bagian bawah adalah alamat memori tinggi.
Mereka berdua tumbuh, hanya dalam menentang arah.
[Mahasiswa] Saya hanya berarti bahwa karena Anda mengatakan Anda menaruh tumpukan di bagian bawah
karena tampaknya lebih intuitif karena untuk stack untuk memulai di bagian atas tumpukan,
tumpukan yang ada di atas dirinya sendiri juga, jadi ltu - >> Ya.
Anda juga memikirkan tumpukan sebagai orang yang tumbuh dan besar, tetapi tumpukan lebih.
Jadi tumpukan adalah salah satu yang kami jenis ingin menunjukkan tumbuh dewasa.
Tapi di mana-mana Anda melihat sebaliknya akan menampilkan alamat 0 di bagian atas
dan alamat memori tertinggi di bagian bawah, jadi ini adalah pandangan yang biasa Anda memori.
>> Apakah Anda memiliki pertanyaan?
[Mahasiswa] Bisakah Anda ceritakan lebih lanjut tentang tumpukan?
Ya. Aku akan mendapatkan bahwa dalam satu detik.
Pertama, akan kembali ke mengapa kembali & y adalah hal yang buruk,
di stack Anda memiliki banyak frame stack yang mewakili semua fungsi
yang telah dipanggil.
Jadi mengabaikan hal-hal sebelumnya, bagian atas tumpukan Anda selalu akan menjadi fungsi utama
karena itulah fungsi pertama yang dipanggil.
Dan kemudian ketika Anda memanggil fungsi lain, stack akan tumbuh ke bawah.
Jadi jika saya memanggil beberapa fungsi, foo, dan itu akan stack frame sendiri,
dapat memanggil beberapa fungsi, bar, mendapat tumpukan bingkai sendiri.
Dan bar bisa rekursif dan bisa menyebut dirinya,
dan agar panggilan kedua ke bar akan mendapatkan tumpukan bingkai sendiri.
Dan jadi apa yang terjadi dalam frame stack semua variabel lokal
dan semua argumen fungsi yang -
Setiap hal yang lokal scoped untuk fungsi ini masuk dalam stack frame.
Jadi itu berarti ketika saya mengatakan sesuatu seperti bar adalah fungsi,
Aku hanya akan mendeklarasikan integer dan kemudian kembali pointer ke integer yang.
Jadi bagaimana y tinggal?
[Mahasiswa] y tinggal di bar. >> [Bowden] Ya.
Di suatu tempat di alun-alun kecil memori adalah persegi littler yang memiliki y di dalamnya.
Ketika aku kembali & y, aku kembali pointer ke blok ini sedikit memori.
Tapi kemudian ketika kembali fungsi, stack frame yang akan muncul dari tumpukan.
Dan itulah mengapa itu disebut stack.
Ini seperti struktur data stack, jika Anda tahu apa itu.
Atau bahkan seperti tumpukan nampan selalu contoh,
utama akan pergi di bagian bawah, maka fungsi pertama Anda menelepon akan pergi di atas itu,
dan Anda tidak bisa kembali ke utama sampai Anda kembali dari semua fungsi yang telah disebut
yang telah ditempatkan di atasnya.
>> [Mahasiswa] Jadi, jika Anda tidak melakukan kembali & y, nilai yang dapat berubah tanpa pemberitahuan.
Ya, ini kan - >> [mahasiswa] Ini bisa ditimpa. >> Ya.
Ini benar-benar - Jika Anda mencoba dan -
Ini juga akan menjadi bar * int karena itu kembali pointer,
sehingga tipe kembali adalah * int.
Jika Anda mencoba untuk menggunakan nilai kembali dari fungsi ini, itu perilaku undefined
karena pointer yang menunjuk ke memori yang buruk. >> [Mahasiswa] Oke.
Jadi bagaimana jika, misalnya, Anda menyatakan int * y = malloc (sizeof (int))?
Itu lebih baik. Ya.
[Mahasiswa] Kami berbicara tentang bagaimana ketika kita tarik hal yang dapat kami recycle bin
mereka tidak benar-benar terhapus, kami hanya kehilangan pointer mereka.
Jadi dalam hal ini kita benar-benar menghapus nilai atau apakah masih ada di memori?
Untuk sebagian besar, itu akan tetap berada di sana.
Tapi katakanlah kita terjadi untuk memanggil beberapa fungsi lainnya, baz.
Baz akan mendapatkan stack frame sendiri di sini.
Ini akan menimpa semua hal ini,
dan kemudian jika Anda kemudian mencoba dan menggunakan pointer yang Anda punya sebelumnya,
itu tidak akan menjadi nilai yang sama.
Ini akan berubah hanya karena Anda disebut baz fungsi.
[Mahasiswa] Tapi kita tidak, akan kita masih mendapatkan 3?
[Bowden] Dalam semua kemungkinan, Anda akan.
Tapi Anda tidak bisa mengandalkan itu. C hanya mengatakan perilaku undefined.
>> [Mahasiswa] Oh, itu tidak. Oke.
Jadi, ketika Anda ingin kembali pointer, ini adalah di mana malloc datang digunakan.
Saya sedang menulis sebenarnya hanya kembali malloc (3 * sizeof (int)).
Kami akan pergi ke malloc lebih dalam satu detik, tetapi gagasan malloc adalah semua variabel lokal Anda
selalu pergi di stack.
Apa pun yang terjadi malloced di heap, dan akan selamanya dan selalu berada di heap
sampai Anda secara eksplisit membebaskannya.
Jadi ini berarti bahwa ketika Anda malloc sesuatu, itu akan bertahan hidup setelah kembali fungsi.
[Mahasiswa] Apakah itu bertahan hidup setelah program berhenti berjalan? >> No
Oke, jadi itu akan berada di sana sampai program adalah semua cara dilakukan berjalan. >> Ya.
Kita bisa pergi ke rincian tentang apa yang terjadi ketika program berhenti berjalan.
Anda mungkin perlu untuk mengingatkan saya, tapi itu adalah hal yang terpisah sama sekali.
[Mahasiswa] Jadi malloc menciptakan pointer? >> Ya.
Malloc - >> [mahasiswa] saya pikir malloc menunjuk blok memori yang pointer dapat digunakan.
[Bowden] saya ingin diagram itu lagi. >> [Mahasiswa] Jadi fungsi ini bekerja, meskipun?
[Mahasiswa] Ya, malloc menunjuk blok memori yang dapat Anda gunakan,
dan kemudian mengembalikan alamat dari blok pertama dari memori itu.
>> [Bowden] Ya. Jadi, ketika Anda malloc, Anda meraih beberapa blok memori
yang saat ini di heap.
Jika tumpukan terlalu kecil, maka tumpukan hanya akan tumbuh, dan tumbuh ke arah ini.
Jadi katakanlah tumpukan terlalu kecil.
Maka itu akan tumbuh sedikit dan kembali pointer ke blok ini yang hanya tumbuh.
Ketika Anda barang gratis, Anda membuat lebih banyak ruang di tumpukan,
sehingga kemudian kemudian menelepon untuk malloc dapat menggunakan kembali bahwa memori yang sebelumnya dibebaskan.
Hal penting tentang malloc dan bebas adalah bahwa hal itu memberi Anda kontrol penuh
selama masa blok-blok memori.
Variabel global selalu hidup.
Variabel lokal yang hidup dalam lingkup mereka.
Segera setelah Anda melewati penjepit keriting, variabel lokal yang mati.
Memori Malloced masih hidup ketika Anda ingin menjadi hidup
dan kemudian dilepaskan ketika Anda kirim akan dirilis.
Mereka sebenarnya hanya 3 jenis memori, benar-benar.
Ada manajemen memori otomatis, yang merupakan stack.
Hal-hal terjadi untuk Anda secara otomatis.
Ketika Anda mengatakan int x, memori dialokasikan untuk x int.
Ketika x keluar dari ruang lingkup, memori direklamasi untuk x.
Kemudian ada memori manajemen yang dinamis, yang adalah apa yang malloc,
yang adalah ketika Anda memiliki kontrol.
Anda dinamis memutuskan kapan memori harus dan tidak harus dialokasikan.
Dan kemudian ada statis, yang hanya berarti bahwa ia hidup selamanya,
yang adalah apa variabel global.
Mereka hanya selalu dalam memori.
>> Pertanyaan?
[Mahasiswa] Dapatkah Anda mendefinisikan blok hanya dengan menggunakan kurung kurawal
tetapi tidak harus memiliki jika pernyataan atau pernyataan sementara atau sesuatu seperti itu?
Anda dapat mendefinisikan blok seperti dalam fungsi, namun yang memiliki kurung kurawal juga.
[Mahasiswa] Jadi Anda tidak bisa hanya memiliki seperti sepasang acak kurung kurawal dalam kode Anda
yang memiliki variabel lokal? >> Ya, Anda bisa.
Di dalam bar int kita bisa memiliki {int y = 3;}.
Itu seharusnya di sini.
Tapi itu benar-benar mendefinisikan lingkup int y.
Setelah itu tanda kurung kurawal kedua, y tidak dapat digunakan lagi.
Anda hampir tidak pernah melakukan hal itu, sekalipun.
Mendapatkan kembali ke apa yang terjadi ketika sebuah program berakhir,
ada semacam kebohongan kesalahpahaman / setengah yang kita berikan untuk hanya membuat segalanya lebih mudah.
Kami memberitahu Anda bahwa ketika Anda mengalokasikan memori
Anda mengalokasikan beberapa potongan RAM untuk variabel tersebut.
Tapi kau tidak benar-benar langsung menyentuh RAM pernah di program Anda.
Jika Anda berpikir tentang itu, bagaimana saya menggambar -
Dan sebenarnya, jika Anda pergi melalui dalam GDB Anda akan melihat hal yang sama.
Terlepas dari berapa kali Anda menjalankan program atau program apa yang Anda jalankan,
stack selalu akan mulai -
Anda selalu akan melihat variabel sekitar sesuatu alamat oxbffff.
Ini biasanya di suatu tempat di wilayah itu.
Tapi bagaimana bisa 2 program mungkin memiliki pointer ke memori yang sama?
[Mahasiswa] Ada beberapa penunjukan sewenang-wenang di mana oxbfff seharusnya pada RAM
yang benar-benar bisa berada di tempat yang berbeda tergantung pada ketika fungsi dipanggil.
Ya. Istilah adalah virtual memory.
Idenya adalah bahwa setiap proses tunggal, setiap program tunggal yang berjalan pada komputer Anda
memiliki sendiri - mari kita asumsikan 32 bit - ruang alamat benar-benar independen.
Ini adalah ruang alamat.
Memiliki sendiri benar-benar independen 4 gigabyte untuk digunakan.
>> Jadi jika Anda menjalankan 2 program secara bersamaan, program ini melihat 4 gigabyte untuk dirinya sendiri,
Program ini melihat 4 gigabyte untuk dirinya sendiri,
dan itu tidak mungkin untuk program ini untuk dereference pointer
dan berakhir dengan memori dari program ini.
Dan apa memori virtual adalah pemetaan dari ruang alamat proses
hal-hal yang sebenarnya pada RAM.
Jadi terserah kepada sistem operasi Anda untuk mengetahui bahwa,
hey, saat ini pria dereferences oxbfff pointer, yang benar-benar berarti
bahwa ia ingin RAM byte 1000,
sedangkan jika program ini dereferences oxbfff, dia benar-benar ingin RAM byte 10000.
Mereka bisa sewenang-wenang berjauhan.
Ini bahkan benar hal-hal dalam ruang alamat proses tunggal.
Jadi seperti itu melihat semua 4 gigabyte untuk dirinya sendiri, tetapi katakanlah -
[Mahasiswa] Apakah setiap proses tunggal -
Katakanlah Anda memiliki komputer dengan hanya 4 gigabyte RAM.
Apakah setiap proses tunggal melihat 4 gigabyte seluruh? >> Ya.
Tetapi 4 gigabyte yang dilihatnya adalah dusta.
Hanya saja itu berpikir itu memiliki semua memori ini karena tidak tahu proses lain ada.
Ini hanya akan menggunakan memori sebanyak itu benar-benar membutuhkan.
Sistem operasi tidak akan memberikan RAM untuk proses ini
jika tidak menggunakan memori di seluruh wilayah.
Ini tidak akan memberikan memori untuk daerah itu.
Tapi gagasan adalah bahwa - Saya mencoba untuk memikirkan - Saya tidak bisa memikirkan analogi.
Analogi sulit.
Salah satu masalah memori virtual atau salah satu hal itu pemecahan
adalah bahwa proses harus benar-benar menyadari satu sama lain.
Dan sehingga Anda dapat menulis program apapun yang hanya dereferences pointer apapun,
seperti hanya menulis sebuah program yang mengatakan * (ox1234),
dan itulah dereferencing memori alamat 1234.
>> Tapi itu terserah sistem operasi untuk kemudian menerjemahkan apa artinya 1.234.
Jadi jika terjadi 1.234 menjadi alamat memori yang valid untuk proses ini,
seperti itu pada tumpukan atau sesuatu, maka ini akan mengembalikan nilai dari alamat memori
sejauh proses tahu.
Tetapi jika 1234 adalah bukan alamat yang valid, seperti terjadi pada tanah
dalam beberapa bagian kecil dari memori di sini yang berada di luar stack dan di luar tumpukan
dan Anda belum benar-benar digunakan itu, maka saat itulah Anda mendapatkan hal-hal seperti segfaults
karena Anda menyentuh memori bahwa Anda tidak harus menyentuh.
Hal ini juga benar -
Sebuah sistem 32-bit, 32 bit berarti Anda memiliki 32 bit untuk mendefinisikan alamat memori.
Ini sebabnya pointer adalah 8 byte karena 32 bit adalah 8 byte - atau 4 byte.
Pointer 4 byte.
Jadi, ketika Anda melihat pointer seperti oxbfffff, yaitu -
Dalam setiap program yang diberikan Anda hanya dapat membuat setiap pointer sewenang-wenang,
mana saja dari ox0 ke sapi 8 f's - FFFFFFFF.
[Mahasiswa] Bukankah kau bilang mereka 4 byte? >> Ya.
[Mahasiswa] Kemudian setiap byte akan memiliki - >> [Bowden] Heksadesimal.
Heksadesimal - 5, 6, 7, 8. Jadi pointer Anda akan selalu melihat di heksadesimal.
Hanya saja bagaimana kita mengklasifikasikan pointer.
Setiap 2 digit heksadesimal adalah 1 byte.
Jadi ada akan menjadi 8 digit heksadesimal untuk 4 byte.
Jadi setiap pointer tunggal pada sistem 32-bit akan menjadi 4 byte,
yang berarti bahwa dalam proses Anda, Anda dapat membuat setiap 4 byte sewenang-wenang
dan membuat pointer dari itu,
yang berarti bahwa sejauh itu sadar, ia dapat mengatasi suatu keseluruhan 2 ke 32 byte memori.
Meskipun tidak benar-benar memiliki akses ke,
bahkan jika komputer Anda hanya memiliki 512 megabyte, berpikir itu memiliki banyak memori.
Dan sistem operasi cukup pintar bahwa hal itu hanya akan mengalokasikan apa yang sebenarnya Anda butuhkan.
Ini tidak hanya pergi, oh, suatu proses baru: 4 gigs.
>> Ya. >> [Mahasiswa] Apa lembu artinya? Mengapa Anda menulis itu?
Ini hanya simbol untuk heksadesimal.
Ketika Anda melihat awal nomor dengan sapi, hal-hal berturut-turut adalah heksadesimal.
[Mahasiswa] Kau menjelaskan tentang apa yang terjadi ketika sebuah program berakhir. >> Ya.
Apa yang terjadi ketika sebuah program berakhir adalah sistem operasi
hanya menghapus pemetaan yang memiliki untuk memperoleh alamat tersebut, dan hanya itu.
Sistem operasi sekarang dapat hanya memberikan memori yang ke program lain untuk menggunakan.
[Mahasiswa] Oke.
Jadi, ketika Anda mengalokasikan sesuatu di tumpukan atau variabel stack atau global atau apa,
mereka semua hanya menghilang segera setelah program berakhir
karena sistem operasi sekarang bebas untuk memberikan memori yang ke proses lainnya.
[Mahasiswa] Meskipun ada mungkin masih ditulis dalam nilai-nilai? >> Ya.
Nilai-nilai yang mungkin masih ada.
Hanya saja itu akan sulit untuk mendapatkan mereka.
Ini jauh lebih sulit untuk mendapatkan mereka daripada itu adalah untuk mendapatkan file yang dihapus
karena jenis file yang dihapus dari duduk di sana untuk waktu yang lama dan hard drive adalah jauh lebih besar.
Jadi itu akan menimpa bagian yang berbeda dari memori
sebelum hal itu terjadi menimpa sepotong memori bahwa file yang digunakan untuk berada di.
Tapi memori utama, RAM, Anda siklus melalui banyak lebih cepat,
sehingga akan sangat cepat akan ditimpa.
Pertanyaan ini atau hal lain?
[Mahasiswa] Saya memiliki pertanyaan tentang topik yang berbeda. Oke >>.
Apakah ada yang memiliki pertanyaan tentang ini?
>> Oke. Berbeda topik. >> [Mahasiswa] Oke.
Aku akan melalui beberapa tes praktek,
dan dalam salah satu dari mereka itu berbicara tentang sizeof
dan nilai yang ia mengembalikan atau jenis variabel yang berbeda. >> Ya.
Dan mengatakan bahwa kedua int dan panjang baik kembali 4, jadi mereka berdua 4 byte panjang.
Apakah ada perbedaan antara int dan panjang, atau apakah hal yang sama?
Ya, ada perbedaan.
Standar C -
Saya mungkin akan mengacaukan.
Standar C adalah seperti apa C adalah, dokumentasi resmi C.
Ini adalah apa yang dikatakan.
Jadi standar C hanya mengatakan bahwa char akan selamanya dan selalu menjadi 1 byte.
Semuanya setelah itu - pendek selalu hanya didefinisikan sebagai lebih besar dari atau sama dengan char.
Ini mungkin benar-benar lebih besar daripada, tapi tidak positif.
Int hanya didefinisikan sebagai lebih besar dari atau sama dengan singkat.
Dan panjang hanya didefinisikan sebagai lebih besar dari atau sama dengan int.
Dan lama lebih besar dari atau sama dengan panjang.
Jadi satu-satunya standar C mendefinisikan adalah pemesanan relatif segalanya.
Jumlah sebenarnya dari memori yang mengambil hal-hal umumnya sampai dengan implementasi,
tapi itu cukup baik didefinisikan pada saat ini. >> [Mahasiswa] Oke.
Jadi celana pendek hampir selalu akan menjadi 2 byte.
Ints hampir selalu akan menjadi 4 byte.
Rindu Panjang hampir selalu akan menjadi 8 byte.
Dan merindukan, itu tergantung pada apakah Anda menggunakan 32-bit atau sistem 64-bit.
Jadi panjang akan sesuai dengan jenis sistem.
Jika Anda menggunakan sistem 32-bit seperti Appliance, itu akan menjadi 4 byte.
Jika Anda menggunakan 64-bit seperti banyak komputer baru-baru ini, itu akan menjadi 8 byte.
>> Ints hampir selalu 4 byte pada saat ini.
Rindu Panjang hampir selalu 8 byte.
Di masa lalu, ints digunakan untuk hanya 2 byte.
Tapi melihat bahwa ini benar-benar memenuhi semua hubungan yang lebih besar dari dan sama dengan.
Selama sempurna diizinkan untuk menjadi ukuran yang sama sebagai integer,
dan itu juga memungkinkan untuk menjadi ukuran yang sama sebagai lama.
Dan itu hanya begitu kebetulan bahwa pada 99,999% dari sistem, itu akan menjadi sama dengan
baik sebagai int atau lama. Itu hanya tergantung pada 32-bit atau 64-bit. >> [Mahasiswa] Oke.
Dalam mengapung, bagaimana titik desimal yang ditunjuk dalam hal bit?
Seperti sebagai biner? >> Ya.
Anda tidak perlu tahu bahwa untuk CS50.
Anda bahkan tidak belajar bahwa dalam 61.
Anda tidak belajar yang benar-benar dalam kursus apapun.
Ini hanya representasi.
Saya lupa jatah bit yang tepat.
Ide floating point adalah bahwa Anda mengalokasikan sejumlah tertentu dari bit untuk mewakili -
Pada dasarnya, segala sesuatu adalah dalam notasi ilmiah.
Jadi Anda mengalokasikan sejumlah tertentu dari bit untuk mewakili nomor sendiri, seperti 1,2345.
Saya tidak pernah dapat mewakili nomor dengan digit lebih dari 5.
Kemudian Anda juga mengalokasikan sejumlah bit tertentu sehingga cenderung menjadi seperti
Anda hanya bisa naik ke nomor tertentu, seperti itulah eksponen terbesar Anda dapat memiliki,
dan Anda hanya bisa pergi ke suatu eksponen tertentu,
seperti itulah eksponen terkecil Anda dapat memiliki.
>> Saya tidak ingat bit cara yang tepat ditugaskan untuk semua nilai-nilai ini,
namun sejumlah bit yang didedikasikan untuk 1,2345,
lain sejumlah bit yang didedikasikan untuk eksponen,
dan itu hanya mungkin untuk mewakili eksponen dengan ukuran tertentu.
[Mahasiswa] Dan ganda? Apakah itu seperti mengapung ekstra panjang? >> Ya.
Ini adalah hal yang sama seperti pelampung kecuali sekarang Anda menggunakan 8 byte bukan 4 byte.
Sekarang Anda akan dapat menggunakan 9 digit atau 10 digit,
dan ini akan bisa naik ke 300, bukan 100. >> [Mahasiswa] Oke.
Dan mengapung juga 4 byte. >> Ya.
Nah, sekali lagi, itu mungkin tergantung pada pelaksanaan keseluruhan umum,
namun mengapung adalah 4 byte, ganda adalah 8.
Ganda disebut ganda karena mereka dua kali lipat ukuran mengapung.
[Mahasiswa] Oke. Dan apakah ada ganda ganda? >> Tidak ada.
Saya pikir - >> [mahasiswa] Seperti rindu yang lama? >> Ya. Saya rasa tidak. Ya.
[Mahasiswa] Pada tes tahun lalu ada pertanyaan tentang fungsi utama
harus menjadi bagian dari program anda.
Jawabannya adalah bahwa hal itu tidak harus menjadi bagian dari program anda.
Dalam situasi apa? Itulah apa yang saya lihat.
[Bowden] Tampaknya - >> [mahasiswa] Situasi apa?
Apakah Anda memiliki masalah? >> [Mahasiswa] Ya, saya pasti bisa menariknya ke atas.
Tidak harus, secara teknis, tetapi pada dasarnya itu akan menjadi.
[Mahasiswa] Saya melihat satu di tahun yang berbeda itu.
Rasanya seperti Benar atau Salah: A berlaku - >> Oh, c file.?
. [Mahasiswa] Setiap file c harus memiliki - [keduanya berbicara sekaligus - dimengerti]
Oke. Jadi yang terpisah.
>> Sebuah file. C hanya perlu mengandung fungsi.
Anda dapat mengkompilasi sebuah file ke dalam kode mesin, biner, apa pun,
tanpa itu menjadi executable belum.
Sebuah eksekusi yang sah harus memiliki fungsi utama.
Anda dapat menulis 100 fungsi dalam 1 file tapi tidak main
dan kemudian kompilasi yang turun ke biner,
maka Anda menulis file lain yang hanya memiliki utama tetapi panggilan sekelompok fungsi-fungsi
dalam file biner di sini.
Dan jadi ketika Anda membuat executable, itulah yang linker tidak
adalah menggabungkan file-file biner 2 menjadi dieksekusi.
Jadi file. C tidak perlu memiliki fungsi utama sama sekali.
Dan pada basis kode besar Anda akan melihat ribuan file c. Dan 1 file utama.
Lebih banyak pertanyaan?
[Mahasiswa] Ada pertanyaan lain.
Dikatakan membuat adalah kompilator. Benar atau Salah?
Dan jawabannya adalah palsu, dan aku mengerti mengapa hal itu tidak seperti dentang.
Tapi apa yang kita sebut membuat jika itu bukan?
Membuat dasarnya hanya - Saya bisa melihat apa yang mereka sebut itu.
Tapi itu hanya menjalankan perintah.
Membuat.
Saya dapat menarik ini. Ya.
Oh, yeah. Buatlah juga melakukan hal itu.
Ini mengatakan tujuan membuat utilitas adalah untuk menentukan secara otomatis
yang potongan program yang besar perlu dikompilasi ulang
dan mengeluarkan perintah untuk mengkompilasi ulang mereka.
Anda dapat membuat membuat file yang benar-benar besar.
Membuat terlihat pada perangko waktu dari file dan, seperti kami katakan sebelumnya,
Anda dapat mengkompilasi file individual turun, dan itu tidak sampai Anda mendapatkan ke linker
bahwa mereka disatukan dalam dieksekusi.
Jadi jika Anda memiliki 10 file yang berbeda dan Anda membuat perubahan ke 1 dari mereka,
maka apa make yang akan dilakukan hanya mengkompilasi ulang bahwa 1 file
dan kemudian relink semuanya bersama-sama.
Tapi itu jauh lebih bodoh dari itu.
Terserah Anda untuk benar-benar mendefinisikan bahwa itulah yang harus dilakukan.
Ini secara default memiliki kemampuan untuk mengenali hal ini cap waktu,
tetapi Anda bisa menulis file make untuk melakukan apa pun.
Anda dapat menulis membuat file sehingga ketika Anda mengetik membuatnya hanya cd ke direktori lain.
Saya frustrasi karena saya taktik segalanya dalam Appliance saya
dan kemudian saya melihat PDF dari Mac.
>> Jadi saya pergi ke Finder dan saya bisa melakukan Go, Connect to Server,
dan server saya terhubung ke Appliance adalah saya, dan kemudian saya membuka PDF
yang akan dikompilasi oleh LaTeX.
Tapi aku frustrasi karena setiap kali saya butuhkan untuk menyegarkan PDF,
Aku harus menyalinnya ke direktori spesifik yang dapat mengakses
dan semakin menjengkelkan.
Jadi, bukannya saya menulis sebuah file make, yang Anda harus menentukan bagaimana membuat hal-hal.
Bagaimana Anda membuat dalam hal ini adalah PDF LaTeX.
Sama seperti file make lainnya - atau saya rasa Anda belum melihat file make,
tapi kita miliki dalam Appliance file make global yang hanya mengatakan,
jika Anda melakukan compile sebuah program C, gunakan dentang.
Dan jadi di sini dalam file make saya bahwa saya membuat saya katakan,
file ini Anda akan ingin mengkompilasi dengan PDF LaTeX.
Dan jadi LaTeX PDF yang melakukan compiling.
Membuat tidak kompilasi. Ini hanya menjalankan perintah-perintah dalam urutan saya ditentukan.
Sehingga berjalan LaTeX PDF, itu salinan ke direktori saya ingin disalin ke,
itu cd untuk direktori dan melakukan hal-hal lainnya,
tetapi semua itu tidak akan mengenali ketika perubahan file,
dan jika perubahan, maka akan menjalankan perintah yang seharusnya untuk menjalankan
ketika perubahan file. >> [Mahasiswa] Oke.
Saya tidak tahu di mana file make global bagi saya untuk check it out.
Pertanyaan lain? Apa pun dari masa lalu kuis? Setiap hal pointer?
Ada hal-hal yang halus dengan pointer seperti -
Aku tidak akan dapat menemukan pertanyaan kuis di atasnya -
tapi seperti hal semacam ini.
Pastikan Anda mengerti bahwa ketika saya mengatakan int * x * y -
Ini bukan apa-apa di sini, saya kira.
Tapi seperti * x * y, yaitu 2 variabel yang ada di stack.
Ketika saya mengatakan x = malloc (sizeof (int)), x adalah masih variabel pada stack,
malloc adalah beberapa blok atas di tumpukan, dan kita memiliki titik x ke tumpukan.
>> Jadi sesuatu di titik tumpukan ke tumpukan.
Setiap kali Anda malloc apapun, Anda pasti menyimpannya dalam pointer.
Jadi pointer yang ada di stack, blok malloced adalah di heap.
Banyak orang bingung dan mengatakan int * x = malloc, x adalah di heap.
Tidak Apa x menunjuk adalah di heap.
x itu sendiri adalah pada stack, kecuali untuk alasan apapun Anda telah x menjadi variabel global,
dalam hal ini akan terjadi di wilayah lain di memori.
Jadi mencatat, diagram ini kotak dan anak panah cukup umum untuk kuis.
Atau jika tidak pada kuis 0, itu akan berada di kuis 1.
Anda harus tahu semua ini, langkah-langkah dalam menyusun
karena Anda harus menjawab pertanyaan pada mereka. Ya.
[Mahasiswa] Bisakah kita pergi ke langkah-langkah - >> Tentu.
Sebelum langkah dan menyusun kita memiliki preprocessing,
kompilasi, perakitan, dan menghubungkan.
Preprocessing. Apa artinya itu lakukan?
Ini adalah langkah termudah dalam - baik, tidak seperti -
itu tidak berarti itu harus jelas, tetapi itu adalah langkah termudah.
Kalian bisa menerapkannya sendiri. Ya.
[Mahasiswa] Ambil apa yang Anda miliki dalam Anda termasuk seperti ini dan itu salinan dan kemudian juga mendefinisikan.
Tampaknya untuk hal-hal seperti # include dan # define,
dan hanya salinan dan pasta apa yang mereka benar-benar berarti.
Jadi ketika Anda mengatakan # include cs50.h, preprocessor adalah menyalin dan menyisipkan cs50.h
ke baris tersebut.
Ketika Anda mengatakan # define x menjadi 4, preprocessor melewati seluruh program
dan menggantikan semua contoh x dengan 4.
Jadi preprocessor mengambil file C valid dan output file C valid
mana hal-hal telah disalin dan disisipkan.
Jadi sekarang kompilasi. Apa artinya itu lakukan?
[Mahasiswa] It goes dari C ke biner.
>> [Bowden] Ia tidak pergi jauh-jauh ke biner.
[Mahasiswa] Untuk kode mesin itu? >> Ini bukan kode mesin.
[Mahasiswa] Majelis? >> Majelis.
Ini pergi ke Majelis sebelum pergi semua jalan ke kode C,
dan bahasa yang paling melakukan sesuatu seperti ini.
Pilih bahasa tingkat tinggi, dan jika Anda akan melakukan kompilasi,
kemungkinan untuk mengkompilasi dalam langkah-langkah.
Pertama itu akan mengkompilasi Python ke C, maka akan mengkompilasi C ke Majelis,
dan kemudian Majelis akan mendapatkan diterjemahkan ke biner.
Jadi kompilasi akan membawanya dari C ke Majelis.
Kata kompilasi biasanya berarti membawanya dari tingkat yang lebih tinggi
ke bahasa pemrograman tingkat yang lebih rendah.
Jadi ini adalah satu-satunya langkah dalam penyusunan mana Anda mulai dengan bahasa tingkat tinggi
dan berakhir dalam bahasa tingkat rendah, dan itulah sebabnya langkah ini disebut kompilasi.
[Mahasiswa] Selama kompilasi, mari kita mengatakan bahwa Anda telah melakukan # include cs50.h.
Akankah compiler mengkompilasi ulang yang cs50.h, seperti fungsi yang ada di sana,
dan menterjemahkannya menjadi kode Majelis juga,
atau akan copy dan paste sesuatu yang sudah pre-Majelis?
cs50.h akan cukup banyak tidak pernah berakhir di Majelis.
Hal-hal seperti prototipe fungsi dan hal-hal yang hanya untuk Anda untuk berhati-hati.
Ini menjamin bahwa kompilator dapat memeriksa hal-hal seperti Anda memanggil fungsi
dengan jenis pengembalian yang tepat dan argumen yang tepat dan barang-barang.
>> Jadi cs50.h akan preproses ke dalam file, dan kemudian ketika itu kompilasi
itu pada dasarnya dibuang setelah memastikan bahwa segala sesuatu sedang disebut dengan benar.
Tapi fungsi yang didefinisikan di perpustakaan CS50, yang terpisah dari cs50.h,
mereka tidak akan dikompilasi secara terpisah.
Yang benar-benar akan turun pada langkah menghubungkan, jadi kita akan mendapatkan bahwa dalam satu detik.
Tapi pertama-tama, apa yang perakitan?
[Mahasiswa] Majelis ke biner? >> Ya.
Perakitan.
Kami tidak menyebutnya kompilasi karena Majelis cukup banyak terjemahan murni biner.
Ada logika yang sangat sedikit pergi dari Majelis ke biner.
Ini seperti melihat ke atas dalam sebuah tabel, oh, kita memiliki instruksi ini;
yang sesuai dengan biner 01110.
Dan sehingga file yang merakit umumnya output yang. O file.
Dan o file. Adalah apa yang kita katakan sebelumnya,
bagaimana file tidak perlu memiliki fungsi utama.
File apapun dapat dikompilasi ke file o. Asalkan itu file C valid.
Hal ini dapat dikompilasi ke. O.
Sekarang, menghubungkan adalah apa yang sebenarnya membawa sekelompok file o dan. Membawa mereka ke dieksekusi.
Dan jadi apa menghubungkan lakukan adalah Anda bisa memikirkan perpustakaan CS50 sebagai file o..
Ini adalah file biner yang sudah dikompilasi.
Dan jadi ketika Anda mengkompilasi file Anda, Anda hello.c, yang menyerukan GetString,
hello.c akan dikompilasi ke hello.o,
hello.o sekarang dalam biner.
Menggunakan GetString, sehingga perlu untuk pergi ke cs50.o,
dan linker smooshes mereka bersama-sama dan salinan GetString ke dalam file ini
dan keluar dengan executable yang memiliki semua fungsi yang dibutuhkan.
Jadi cs50.o sebenarnya bukan file O, tapi itu cukup dekat bahwa tidak ada perbedaan mendasar.
Jadi menghubungkan hanya membawa sekelompok file bersama-sama
yang terpisah berisi semua fungsi saya harus menggunakan
dan menciptakan executable yang benar-benar akan berjalan.
>> Dan sehingga juga apa yang kita katakan sebelumnya
di mana Anda dapat memiliki 1000. file c, Anda kompilasi mereka semua ke file o,.
yang mungkin akan memakan waktu cukup lama, maka Anda mengubah 1. berkas c.
Anda hanya perlu mengkompilasi ulang bahwa 1. Berkas c dan kemudian segala sesuatu yang lain Relink,
menghubungkan semuanya kembali bersama-sama.
[Mahasiswa] Ketika kita menghubungkan kita menulis lcs50?
Ya, jadi-lcs50. Itu sinyal bendera ke linker yang harus Anda menghubungkan di perpustakaan itu.
Pertanyaan?
Apakah kita pergi selama biner selain itu 5 detik di kuliah pertama?
Saya rasa tidak.
Anda harus mengetahui semua Os besar yang kita telah melebihi,
dan Anda harus dapat, jika kita memberi Anda fungsi,
Anda harus dapat mengatakan itu O besar, kira-kira. Atau juga, big O kasar.
Jadi jika Anda melihat bersarang untuk loop perulangan atas jumlah yang sama dari hal-hal,
seperti int i, i <n, int j, j > [mahasiswa] n kuadrat. >> Cenderung menjadi n kuadrat.
Jika Anda telah tiga bersarang, itu cenderung menjadi n potong dadu.
Jadi hal semacam itu Anda harus dapat menunjukkan dengan segera.
Anda perlu tahu insertion sort dan bubble sort dan menggabungkan mengurutkan dan semua orang.
Lebih mudah untuk memahami mengapa mereka adalah mereka n kuadrat dan n log n dan semua itu
karena saya pikir ada pada kuis satu tahun di mana kita pada dasarnya memberi Anda
sebuah implementasi dari bubble sort dan berkata, "Apa waktu berjalan dari fungsi ini?"
Jadi jika Anda mengenalinya sebagai semacam gelembung, maka anda dapat langsung katakan n kuadrat.
Tetapi jika Anda hanya melihat itu, Anda bahkan tidak perlu untuk mewujudkan semacam gelembung itu;
Anda hanya bisa mengatakan ini adalah melakukan ini dan ini. Ini adalah n kuadrat.
[Mahasiswa] Apakah ada contoh yang sulit Anda bisa datang dengan,
seperti ide yang sama mencari tahu?
>> Saya tidak berpikir kita akan memberikan contoh-contoh yang sulit.
Hal bubble sort adalah tentang sekuat kita akan pergi,
dan bahkan, selama Anda memahami bahwa Anda iterasi atas array
untuk setiap elemen dalam array, yang akan menjadi sesuatu yang n kuadrat.
Ada pertanyaan umum, seperti di sini kita memiliki - Oh.
Beberapa hari yang lalu, Doug mengaku, "Saya telah menemukan sebuah algoritma yang dapat mengurutkan array
"Nomor n dalam O (log n) waktu!"
Jadi bagaimana kita tahu itu mustahil?
[Respon siswa terdengar] >> Ya.
Paling tidak, Anda harus menyentuh setiap elemen dalam array,
sehingga tidak mungkin untuk mengurutkan array -
Jika semuanya adalah dalam rangka unsorted, maka Anda akan menyentuh segala sesuatu di array,
sehingga tidak mungkin untuk melakukannya dalam waktu kurang dari O n.
[Mahasiswa] Anda menunjukkan kepada kita bahwa contoh mampu melakukannya di O n
jika Anda menggunakan banyak memori. >> Ya.
Dan ltu - aku lupa apa ltu - Apakah menghitung sort?
Hmm. Itu adalah algoritma sorting integer.
Saya sedang mencari nama khusus untuk ini yang saya tidak bisa ingat minggu lalu.
Ya. Ini adalah jenis dari jenis yang dapat mencapai hal-hal besar di O n.
Tetapi ada keterbatasan, seperti Anda hanya dapat menggunakan bilangan bulat sampai jumlah tertentu.
Ditambah jika Anda mencoba untuk mengurutkan ltu sesuatu -
Jika array adalah 012, -12, 151, 4 juta,
maka elemen tunggal akan benar-benar merusak seluruh penyortiran.
>> Pertanyaan?
[Mahasiswa] Jika Anda memiliki fungsi rekursif dan itu hanya membuat panggilan rekursif
dalam pernyataan kembali, itulah ekor rekursif,
dan sehingga akan tidak menggunakan memori lebih selama runtime
atau setidaknya akan menggunakan memori sebanding sebagai solusi iteratif?
[Bowden] Ya.
Ini mungkin akan menjadi agak lambat, tapi tidak benar-benar.
Tail rekursif cukup bagus.
Melihat kembali pada frame stack, katakanlah kita memiliki utama
dan kami memiliki bar int (int x) atau sesuatu.
Ini bukan fungsi rekursif yang sempurna, tapi kembali bar (x - 1).
Jadi jelas, ini cacat. Anda perlu kasus dasar dan barang-barang.
Tapi ide di sini adalah bahwa ini adalah ekor rekursif,
yang berarti ketika bar utama panggilan itu akan mendapatkan stack frame nya.
Dalam stack frame ada akan menjadi blok kecil memori
yang sesuai dengan argumen x nya.
Dan jadi mari kita katakan utama terjadi untuk memanggil bar (100);
Jadi x akan mulai keluar sebagai 100.
Jika compiler mengakui bahwa ini adalah fungsi rekursif ekor,
maka ketika bar membuat panggilan rekursif untuk bar,
alih-alih membuat stack frame baru, yang mana tumpukan mulai tumbuh sebagian besar,
akhirnya akan berjalan ke tumpukan dan kemudian Anda mendapatkan segfaults
karena memori mulai bertabrakan.
>> Jadi daripada membuat stack frame sendiri, dapat mewujudkan,
hey, saya tidak pernah benar-benar perlu untuk kembali ke stack frame,
jadi aku malah hanya akan mengganti argumen ini dengan 99 dan kemudian mulai bar di seluruh.
Dan kemudian akan melakukannya lagi dan itu akan mencapai bar kembali (x - 1),
dan bukannya membuat stack frame baru, itu hanya akan menggantikan argumen saat ini dengan 98
dan kemudian melompat kembali ke awal dari bar.
Mereka operasi, menggantikan bahwa nilai 1 pada stack dan melompat kembali ke awal,
cukup efisien.
Jadi tidak hanya ini penggunaan memori yang sama sebagai fungsi terpisah yang iteratif
karena Anda hanya menggunakan 1 stack frame, tetapi Anda tidak menderita kerugian
karena harus memanggil fungsi-fungsi.
Memanggil fungsi dapat menjadi agak mahal karena harus melakukan semua konfigurasi ini
dan teardown dan semua barang-barang ini.
Jadi ini rekursi ekor baik.
[Mahasiswa] Mengapa tidak menciptakan langkah baru?
Karena menyadari itu tidak perlu.
Panggilan ke bar hanya mengembalikan panggilan rekursif.
Jadi tidak perlu melakukan apa-apa dengan nilai kembali.
Ini hanya akan segera mengembalikannya.
Jadi itu hanya akan menggantikan argumen sendiri dan memulai dari awal.
Dan juga, jika Anda tidak memiliki versi rekursif ekor,
maka Anda mendapatkan semua bar di mana ketika bar ini kembali
itu harus mengembalikan nilai untuk yang satu ini, maka bar segera kembali
dan mengembalikan nilainya ke satu ini, maka itu hanya akan segera kembali
dan mengembalikan nilainya ke satu ini.
Jadi Anda menghemat ini bermunculan semua hal off dari stack
karena nilai kembali hanya akan melewati sepanjang jalan kembali pula.
Jadi mengapa tidak hanya mengganti argumen kami dengan argumen diperbarui dan memulai lagi?
Jika fungsi ini tidak rekursif ekor, jika Anda melakukan sesuatu seperti -
[Mahasiswa] jika bar (x + 1). >> Ya.
>> Jadi jika Anda memasukkannya ke dalam kondisi, maka Anda melakukan sesuatu dengan nilai kembali.
Atau bahkan jika Anda hanya melakukan pengembalian 2 * bar (x - 1).
Jadi sekarang bar (x - 1) perlu untuk kembali dalam rangka untuk itu untuk menghitung 2 kali nilai,
jadi sekarang itu tidak perlu stack frame terpisah sendiri,
dan sekarang, tidak peduli seberapa keras Anda mencoba, Anda akan perlu -
Ini bukan rekursif ekor.
[Mahasiswa] Apakah saya mencoba untuk membawa rekursi untuk bertujuan untuk rekursi ekor -
[Bowden] Dalam dunia yang ideal, tetapi dalam CS50 Anda tidak perlu.
Dalam rangka untuk mendapatkan rekursi ekor, pada umumnya, Anda membuat sebuah argumen tambahan
di mana bar akan mengambil x int ke y
dan y sesuai dengan hal utama yang Anda ingin kembali.
Jadi ini Anda akan kembali bar (x - 1), 2 * y.
Jadi itu hanya tingkat tinggi bagaimana Anda mengubah sesuatu menjadi ekor rekursif.
Namun argumen tambahan -
Dan kemudian pada akhirnya ketika Anda mencapai kasus dasar Anda, Anda hanya mengembalikan y
karena Anda telah terakumulasi sepanjang waktu nilai pengembalian yang Anda inginkan.
Anda jenis telah melakukannya iteratif tetapi menggunakan panggilan rekursif.
Pertanyaan?
[Mahasiswa] Mungkin tentang aritmatika pointer, seperti ketika menggunakan string. Tentu >>.
Pointer aritmatika.
Bila menggunakan string itu mudah karena string adalah bintang char,
karakter yang selamanya dan selalu satu byte,
dan sebagainya aritmatika pointer setara dengan aritmatika biasa saat Anda sedang berhadapan dengan string.
Mari kita hanya mengatakan char * s = "halo".
Jadi kita memiliki blok dalam memori.
Perlu 6 byte karena Anda selalu membutuhkan terminator nol.
Dan char * s akan menunjuk ke awal array ini.
Jadi s menunjuk ada.
Sekarang, hal ini pada dasarnya adalah bagaimana Array setiap bekerja,
terlepas dari apakah itu kembali oleh malloc atau apakah itu di stack.
Array Setiap dasarnya adalah pointer ke awal array,
dan kemudian setiap operasi array, pengindeksan apapun, hanya masuk ke array yang offset tertentu.
>> Jadi ketika saya mengatakan sesuatu seperti s [3], ini akan s dan menghitung 3 chars masuk
Jadi s [3], kita memiliki 0, 1, 2, 3, maka s [3] akan merujuk ke l ini.
[Mahasiswa] Dan kita bisa mencapai nilai yang sama dengan melakukan s + 3 dan kemudian Bintang kurung?
Ya.
Hal ini setara dengan * (s + 3);
dan itu selamanya dan selalu setara tidak peduli apa yang Anda lakukan.
Anda tidak perlu menggunakan sintaks braket.
Anda selalu dapat menggunakan * (s + 3) sintaks.
Orang-orang cenderung menyukai sintaks braket, meskipun.
[Mahasiswa] Jadi semua array yang sebenarnya hanya pointer.
Ada perbedaan sedikit ketika saya mengatakan int x [4]; >> [mahasiswa] Apakah yang menciptakan memori?
[Bowden] Itu akan membuat 4 ints di stack, sehingga 16 byte keseluruhan.
Ini akan membuat 16 byte pada stack.
x tidak disimpan di mana saja.
Ini hanyalah simbol mengacu pada awal hal.
Karena Anda menyatakan array dalam fungsi ini,
apa compiler akan lakukan adalah hanya mengganti semua contoh variabel x
dengan mana itu terjadi memilih untuk menempatkan 16 byte.
Hal ini tidak bisa melakukan itu dengan char * s karena s merupakan pointer yang sebenarnya.
Hal ini bebas untuk kemudian menunjuk ke hal-hal lain.
x adalah sebuah konstanta. Anda tidak dapat memiliki sebuah titik ke array yang berbeda. >> [Mahasiswa] Oke.
Tapi ide ini, pengindeksan ini, adalah sama terlepas dari apakah itu array tradisional
atau jika itu adalah pointer ke sesuatu atau jika itu adalah pointer ke array malloced.
Dan pada kenyataannya, itu adalah supaya setara bahwa juga hal yang sama.
Ini sebenarnya hanya menerjemahkan apa yang ada di dalam tanda kurung dan apa yang tersisa dari kurung,
menambahkan mereka bersama-sama, dan dereferences.
Jadi ini sama validnya dengan * (s + 3) atau s [3].
[Mahasiswa] Dapatkah Anda memiliki pointer yang menunjuk ke 2-dimensi array?
>> Lebih sulit. Secara tradisional, tidak.
Sebuah array 2-dimensi hanyalah sebuah array 1 dimensi dengan beberapa sintaks yang nyaman
karena ketika saya mengatakan int x [3] [3], ini benar-benar hanya 1 array dengan nilai 9.
Dan jadi ketika saya indeks, compiler tahu apa yang saya maksud.
Jika saya mengatakan x [1] [2], ia tahu aku ingin pergi ke baris kedua, sehingga akan melewati 3 pertama,
dan kemudian ingin hal kedua itu, jadi itu akan mendapatkan satu ini.
Tapi itu masih hanya satu array dimensi.
Dan jadi jika saya ingin menetapkan pointer ke array,
Saya akan mengatakan int * p = x;
Jenis x adalah hanya -
Ini jenis mengatakan kasar x karena hanya simbol dan itu bukan sebuah variabel yang sebenarnya,
tapi itu hanya sebuah * int.
x adalah hanya pointer ke awal ini. >> [Mahasiswa] Oke.
Dan jadi saya tidak akan dapat mengakses [1] [2].
Saya pikir ada sintaks khusus untuk menyatakan pointer,
sesuatu yang konyol seperti int (* p [-. sesuatu yang benar-benar konyol saya bahkan tidak tahu.
Tapi ada sintaks untuk menyatakan pointer seperti dengan tanda kurung dan hal.
Mungkin tidak bahkan membiarkan Anda melakukan itu.
Aku bisa melihat kembali pada sesuatu yang akan mengatakan yang sebenarnya.
Aku akan mencarinya nanti, jika ada sintaks untuk titik. Tapi Anda tidak akan pernah melihatnya.
Dan bahkan sintaks begitu kuno bahwa jika Anda menggunakannya, orang akan bingung.
Array multidimensi cukup langka seperti itu.
Anda cukup banyak -
Nah, jika Anda melakukan hal-hal matriks itu tidak akan menjadi langka,
tapi di C Anda jarang akan menggunakan array multidimensi.
Ya. >> [Mahasiswa] Katakanlah Anda memiliki array yang sangat panjang.
>> Jadi dalam memori virtual akan muncul untuk menjadi semua berturut-turut,
seperti elemen yang tepat di samping satu sama lain,
tetapi dalam memori fisik, apakah mungkin untuk itu harus berpisah? >> Ya.
Bagaimana maya bekerja memori itu hanya memisahkan -
Unit alokasi adalah halaman, yang cenderung menjadi 4 kilobyte,
dan jadi ketika proses berkata, hey, saya ingin menggunakan memori ini,
sistem operasi akan mengalokasikan 4 kilobyte untuk itu blok sedikit memori.
Bahkan jika Anda hanya menggunakan satu byte kecil tunggal di seluruh blok memori,
sistem operasi akan memberikannya 4 kilobyte penuh.
Jadi apa artinya ini adalah aku bisa - katakanlah ini adalah stack saya.
Tumpukan ini dapat dipisahkan. Saya tumpukan bisa megabyte dan megabyte.
Saya tumpukan bisa sangat besar.
Tapi tumpukan itu sendiri harus dipecah menjadi halaman individual,
yang jika kita melihat di sini mari kita mengatakan ini adalah RAM kami,
jika saya memiliki 2 gigabyte RAM, ini adalah alamat 0 aktual seperti byte zeroth RAM saya,
dan ini adalah 2 gigabyte sepanjang jalan ke sini.
Jadi halaman ini mungkin sesuai dengan blok ini di sini.
Halaman ini mungkin sesuai dengan blok ini di sini.
Yang satu ini mungkin sesuai dengan yang satu ini di sini.
Jadi sistem operasi bebas untuk menetapkan memori fisik
untuk setiap halaman individu secara sewenang-wenang.
Dan itu berarti bahwa jika perbatasan ini terjadi mengangkangkan array,
array terjadi ditinggalkan ini dan kanan dari urutan halaman,
maka array yang akan dibagi dalam memori fisik.
Dan kemudian ketika Anda berhenti program, ketika proses berakhir,
pemetaan ini bisa dihapus dan kemudian itu gratis untuk menggunakan blok kecil untuk hal-hal lainnya.
Lebih banyak pertanyaan?
[Mahasiswa] The aritmetik pointer. >> Oh yeah.
String yang mudah, tapi melihat sesuatu seperti ints,
sehingga kembali ke int x [4];
Apakah ini adalah array atau apakah itu adalah pointer ke array malloced dari 4 bilangan bulat,
itu akan diperlakukan dengan cara yang sama.
[Mahasiswa] Jadi array di heap?
[Bowden] Array tidak di heap. >> [Mahasiswa] Oh.
>> [Bowden] Jenis array cenderung di stack
kecuali Anda menyatakan itu di - mengabaikan variabel global. Jangan menggunakan variabel global.
Dalam fungsi saya katakan int x [4];
Ini akan membuat blok 4-bulat pada stack untuk array ini.
Tapi ini malloc (4 * sizeof (int)); akan pergi di heap.
Tapi setelah titik ini saya dapat menggunakan x dan p dalam cukup banyak cara yang sama,
selain pengecualian saya katakan sebelumnya tentang Anda dapat menetapkan kembali p.
Secara teknis, ukuran mereka agak berbeda, tapi itu sama sekali tidak relevan.
Anda tidak pernah benar-benar menggunakan ukuran mereka.
P saya bisa mengatakan p [3] = 2, atau x [3] = 2;
Anda dapat menggunakannya dalam cara yang sama persis.
Jadi pointer aritmatika sekarang - Ya.
[Mahasiswa] Apakah Anda tidak perlu melakukan * p jika Anda memiliki tanda kurung?
Tanda kurung adalah dereference implisit. Oke >>.
Sebenarnya, juga apa yang Anda katakan dengan Anda bisa mendapatkan array multidimensi
dengan pointer, apa yang dapat Anda lakukan adalah sesuatu seperti, katakanlah, int ** pp = malloc (sizeof (int *) * 5);
Saya hanya akan menulis semuanya pertama.
Aku tidak ingin yang satu.
Oke.
Apa yang saya lakukan di sini adalah - Yang harus pp [i].
Jadi pp adalah pointer ke pointer.
Anda mallocing pp untuk menunjuk ke array dari 5 int.
Jadi dalam memori Anda miliki di pp stack
Itu akan menunjuk ke array dari 5 blok yang semuanya sendiri pointer.
Dan kemudian ketika saya malloc di sini, saya malloc bahwa masing-masing individu pointer
harus menunjuk ke blok yang terpisah dari 4 byte di heap.
Jadi ini menunjukkan 4 byte.
Dan ini salah satu poin ke 4 byte yang berbeda.
>> Dan mereka semua menunjuk ke mereka sendiri 4 byte.
Ini memberi saya cara melakukan sesuatu multidimensi.
Saya bisa mengatakan pp [3] [4], tetapi sekarang ini bukan hal yang sama dengan array multidimensi
karena array multidimensi itu diterjemahkan [3] [4] menjadi satu offset ke array x.
Ini p dereferences, mengakses indeks ketiga, maka dereferences bahwa
dan akses - 4 akan valid - indeks kedua.
Sedangkan ketika kita memiliki int x [3] [4] sebelumnya sebagai array multidimensi
dan ketika Anda double braket itu benar-benar hanya dereference tunggal,
Anda mengikuti pointer tunggal dan kemudian offset,
ini benar-benar referensi 2D.
Anda mengikuti 2 pointer terpisah.
Jadi ini juga secara teknis memungkinkan Anda untuk memiliki array multidimensi
di mana setiap array individu ukuran yang berbeda.
Jadi saya pikir array multidimensi bergerigi adalah apa yang disebut
karena benar-benar hal pertama yang bisa menunjukkan sesuatu yang memiliki 10 elemen,
hal kedua bisa menunjukkan sesuatu yang memiliki 100 elemen.
[Mahasiswa] Apakah ada batasan untuk jumlah pointer Anda dapat memiliki
menunjuk ke pointer lain? >> No
Anda dapat memiliki int ***** p.
Kembali ke aritmatika pointer - >> [mahasiswa] Oh. >> Ya.
[Mahasiswa] Jika saya memiliki int *** p dan kemudian saya melakukan dereferencing dan saya katakan * p sama dengan nilai ini,
yang hanya akan melakukan 1 tingkat dereferencing? >> Ya.
Jadi jika saya ingin mengakses hal yang pointer terakhir adalah menunjuk -
Kemudian Anda melakukan p ***. Oke >>.
Jadi ini adalah p poin ke 1 blok, menunjuk ke blok lain, menunjuk ke blok lain.
Kemudian jika Anda lakukan * p = sesuatu yang lain, maka Anda mengubah ini
sekarang menunjuk ke blok yang berbeda. Oke >>.
>> [Bowden] Dan jika ini yang malloced, maka Anda sekarang telah bocor memori
kecuali jika Anda kebetulan memiliki referensi yang berbeda dari
karena Anda tidak bisa kembali ke orang-orang yang baru saja Anda membuang.
Pointer aritmatika.
int x [4], akan mengalokasikan sebuah array dari 4 bilangan bulat
di mana x akan menunjuk ke awal array.
Jadi ketika saya mengatakan sesuatu seperti x [1], saya ingin hal itu berarti pergi ke integer kedua dalam array,
yang akan menjadi satu ini.
Tapi sungguh, itu 4 byte ke array bilangan bulat karena ini membutuhkan 4 byte.
Jadi offset 1 benar-benar berarti offset 1
kali ukuran apapun jenis array.
Ini adalah sebuah array bilangan bulat, sehingga tahu untuk melakukan 1 kali ukuran int ketika ingin mengimbangi.
Sintaks lainnya. Ingatlah bahwa ini setara dengan * (x + 1);
Ketika saya mengatakan pointer + 1, apa yang mengembalikan adalah alamat yang pointer yang menyimpan
ditambah 1 kali ukuran dari jenis pointer.
Jadi, jika x = ox100, maka x + 1 = ox104.
Dan Anda dapat penyalahgunaan ini dan mengatakan sesuatu seperti char * c = (char *) x;
dan sekarang c akan menjadi alamat yang sama sebagai x.
c akan menjadi sama dengan ox100,
tapi c + 1 akan menjadi sama dengan ox101
sejak aritmatika pointer tergantung pada jenis pointer yang Anda menambah.
Jadi c + 1, terlihat pada c, itu adalah pointer char, sehingga akan menambah 1 kali ukuran char,
yang selalu akan menjadi 1, sehingga Anda mendapatkan 101,
sedangkan jika saya melakukan x, yang juga masih 100, x + 1 akan menjadi 104.
[Mahasiswa] Dapatkah Anda menggunakan c + + dalam rangka memajukan pointer Anda dengan 1?
Ya, Anda bisa.
Anda tidak bisa melakukan itu dengan x karena x adalah hanya simbol, itu adalah konstan, Anda tidak dapat mengubah x.
>> Tapi c terjadi hanya menjadi pointer, sehingga c + + benar-benar berlaku dan akan increment 1.
Jika c adalah hanya sebuah * int, maka c + + akan 104.
+ + Tidak aritmetik pointer seperti c + 1 akan melakukan aritmetik pointer.
Ini sebenarnya bagaimana banyak hal-hal seperti gabungan semacam -
Alih-alih membuat salinan dari hal-hal, Anda malah bisa lewat -
Seperti jika saya ingin melewati setengah dari array - mari kita menghapus beberapa ini.
Katakanlah saya ingin lulus sisi array ke dalam fungsi.
Apa yang akan saya lolos ke fungsi itu?
Jika saya lulus x, saya lewat alamat ini.
Tapi aku ingin melewati ini alamat tertentu. Jadi apa yang harus saya lulus?
[Mahasiswa] Pointer + 2?
[Bowden] Jadi x + 2. Ya.
Itu akan menjadi alamat ini.
Anda juga akan sangat sering melihatnya sebagai x [2] dan kemudian alamat itu.
Jadi, Anda perlu untuk mengambil alamat karena braket adalah dereference implisit.
x [2] mengacu pada nilai yang ada di kotak ini, dan kemudian Anda ingin alamat kotak itu,
sehingga Anda katakan & x [2].
Jadi itulah bagaimana sesuatu di semacam gabungan di mana Anda ingin melewati setengah daftar untuk sesuatu
Anda benar-benar hanya lulus & x [2], dan sekarang sejauh panggilan rekursif yang bersangkutan,
array baru saya mulai ada.
Terakhir menit pertanyaan.
[Mahasiswa] Jika kita tidak menempatkan sebuah ampersand atau - apa yang disebut? >> Star?
[Mahasiswa] Star. >> Teknis, operator dereference, tapi - >> [mahasiswa] dereference.
>> Jika kita tidak menempatkan bintang atau ampersand, apa yang terjadi jika saya hanya mengatakan y = x dan x adalah pointer?
Apa jenis y? >> [Mahasiswa] Aku hanya akan mengatakan pointer itu 2.
Jadi jika Anda hanya mengatakan y = x, sekarang x dan titik y ke hal yang sama. >> [Mahasiswa] Point untuk hal yang sama.
Dan jika x adalah pointer int? >> Ini akan mengeluh karena Anda tidak dapat menetapkan pointer.
[Mahasiswa] Oke.
Ingat bahwa pointer, meskipun kita menarik mereka sebagai anak panah,
benar-benar semua toko mereka - int * x - x benar-benar menyimpan semua adalah sesuatu seperti ox100,
yang kita terjadi untuk mewakili sebagai menunjuk ke blok disimpan pada 100.
Jadi ketika saya mengatakan int * y = x; Aku hanya menyalin ox100 ke y,
yang kita hanya akan mewakili sebagai y, juga menunjuk ke ox100.
Dan jika saya mengatakan int i = (int) x; maka saya akan menyimpan apapun nilai ox100 adalah
di dalamnya, tapi sekarang itu akan ditafsirkan sebagai integer bukan pointer.
Tapi Anda perlu pemain atau yang lain itu akan mengeluh.
[Mahasiswa] Jadi maksudmu untuk membuang -
Apakah akan pengecoran int int x atau casting y?
[Bowden] Apa?
[Mahasiswa] Oke. Setelah tanda kurung yang ada akan menjadi x atau ay sana?
>> [Bowden] Entah. x dan y adalah setara. >> [Mahasiswa] Oke.
Karena mereka berdua pointer. >> Ya.
[Mahasiswa] Jadi akan menyimpan 100 heksadesimal dalam bentuk integer? >> [Bowden] Ya.
Tapi tidak nilai apa pun menunjuk ke.
[Bowden] Ya. >> [Mahasiswa] Jadi hanya alamat dalam bentuk integer. Oke.
[Bowden] Jika Anda ingin untuk beberapa alasan aneh,
Anda secara eksklusif bisa berurusan dengan pointer dan tidak pernah berurusan dengan bilangan bulat
dan hanya menjadi seperti int * x = 0.
Kemudian Anda akan mendapatkan benar-benar bingung sekali aritmatika pointer mulai terjadi.
Jadi angka-angka yang mereka menyimpan yang berarti.
Ini hanya bagaimana Anda akhirnya menginterpretasikannya.
Jadi aku bebas untuk menyalin ox100 dari * int ke int,
dan aku bebas untuk menetapkan - Anda mungkin akan dimarahi karena tidak pengecoran -
Aku bebas untuk menetapkan sesuatu seperti (int *) ox1234 ke dalam * int sewenang-wenang.
Jadi ox123 sama validnya alamat memori seperti & y.
& Y terjadi kembali sesuatu yang cukup banyak ox123.
[Mahasiswa] Apakah itu cara yang sangat keren untuk pergi dari heksadesimal ke bentuk desimal,
seperti jika Anda memiliki pointer dan Anda melemparkan sebagai int?
[Bowden] Anda dapat benar-benar hanya mencetak menggunakan seperti printf.
Katakanlah saya memiliki int y = 100.
Jadi printf (% d \ n - seperti yang Anda sudah harus tahu - cetak bahwa sebagai integer, x%.
Kami hanya akan mencetaknya sebagai heksadesimal.
Jadi pointer tidak disimpan sebagai heksadesimal,
dan integer tidak disimpan sebagai desimal.
Semuanya disimpan sebagai biner.
Hanya saja kita cenderung untuk menunjukkan pointer sebagai heksadesimal
karena kita memikirkan hal-hal dalam 4-byte blok,
dan alamat memori cenderung akrab.
Kita seperti, jika dimulai dengan bf, maka akan terjadi pada stack.
Jadi itu hanya penafsiran kita pointer sebagai heksadesimal.
Oke. Setiap pertanyaan terakhir?
>> Aku akan berada di sini sebentar setelah jika Anda memiliki apa-apa lagi.
Dan itulah akhir dari itu.
>> [Mahasiswa] Yay! [Tepuk tangan]
>> [CS50.TV]