[Powered by Google Translate] [Penunjuk] [Rob Bowden] [Universiti Harvard] [Ini adalah CS50] [CS50.TV] Mari kita bercakap tentang petunjuk. Sehingga kini, kita telah sentiasa hanya dirujuk kepada perkara-perkara dalam ingatan jelas dengan nama. Kami telah berkata int n = 42; dan kemudian apabila kita mahu menggunakan n berubah-ubah, kita hanya memanggilnya dengan nama kita memberi dengan menulis sesuatu seperti n * 2. Tetapi pembolehubah yang mesti hidup tempat dalam ingatan. Apabila anda mahu menggunakan nilai yang kini disimpan di dalam n, atau mengemaskini nilai yang n memegang, program anda perlu tahu di mana dalam memori untuk melihat bagi n. Jika dalam memori kehidupan yang berubah-ubah dipanggil alamat. Ia seperti alamat rumah. Saya boleh mencari rumah seseorang selagi saya tahu alamat rumah mereka, dan program komputer boleh mencari pembolehubah selagi ia tahu alamat ingatan. Apa petunjuk menyediakan adalah cara yang langsung berurusan dengan alamat-alamat ingatan. Banyak kuasa dalam C datang daripada mampu untuk memanipulasi ingatan seperti ini. Tetapi dengan kuasa yang besar datang tanggungjawab yang besar. Penunjuk boleh digunakan berbahaya cukup bahawa banyak bahasa pengaturcaraan menyembunyikan petunjuk sepenuhnya. Jadi, mengapakah C memberi kita petunjuk kemudian? Seperti yang anda tahu, hujah untuk fungsi C sentiasa disalin ke parameter fungsi. Jadi, memanggil sesuatu seperti swap pada beberapa pembolehubah x dan y tidak boleh bertukar ganti nilai x dan y dalam fungsi memanggil, walaupun yang mungkin berguna. Seperti yang kita akan lihat nanti, swap menulis semula untuk mengambil petunjuk untuk lokasi yang perlu ditukar membolehkan ia mempengaruhi pembolehubah pemanggil. Mari berjalan melalui contoh yang agak mudah penunjuk boleh lakukan. Katakan kita mempunyai int n = 4; int * pointer_to_n = & n. Whoa! Sedikit sintaks baru untuk menampung. Pertama, mari kita mentafsir ini & n. Ingatlah bahawa segala-galanya dalam ingatan mempunyai alamat beberapa. #: Glib dipanggil "alamat" pengendali. Jadi, & n merujuk kepada alamat dalam ingatan di mana n disimpan. Sekarang, kita menyimpan alamat ini dalam pembolehubah baru, pointer_to_n. Apakah jenis pembolehubah baru ini? Asterisk adalah sebahagian daripada jenis pembolehubah, dan kita akan membaca jenis * int. * Int bermakna bahawa pointer_to_n adalah pembolehubah yang menyimpan alamat integer. Kita tahu bahawa & n * int sejak n adalah integer, dan kami mengambil alamat n. * Int adalah satu contoh jenis penunjuk. Sebaik sahaja anda mula melihat asterisk dalam jenis, anda tahu bahawa anda sedang berurusan dengan penunjuk. Sama seperti kita boleh mengisytiharkan pembolehubah sebagai int x dan char y, kita boleh mengatakan int * z dan char * w. Int * dan * char hanya jenis baru untuk kita gunakan. Lokasi * boleh pergi mana-mana sahaja sebelum nama pembolehubah. Jadi, kedua-dua int * pointer_to_n - * seterusnya untuk int, seperti yang kita ada di sini - dan int * pointer_to_n * sebelah pointer_to_n adalah sah. Tetapi di sini, saya akan letakkan * sebelah int. Ia tidak kira yang anda suka, hanya menjadi konsisten. Mari kita lukiskan gambarajah ini. Kita mula-mula mempunyai n pembolehubah, yang kita akan menarik sebagai kotak sedikit memori. Untuk contoh ini, mari kita mengatakan bahawa kotak ini terletak di 100 alamat. Dalam kotak ini, kita menyimpan nilai 4. Sekarang, kita mempunyai pembolehubah baru, pointer_to_n. Ia mempunyai kotak sendiri dalam ingatan, yang kita akan katakan adalah pada 200 alamat. Dalam kotak ini, kita menyimpan alamat n, mana kita sebelum ini berkata adalah 100. Kerap dalam rajah, anda akan melihat ini ditunjukkan sebagai panah literal meninggalkan kotak pointer_to_n menunjuk ke kotak yang menyimpan n. Kini, apa yang boleh kita sebenarnya melakukan dengan pointer_to_n? Nah, jika kita mengatakan sesuatu seperti * pointer_to_n = 8, ini adalah berlainan bagi penggunaan asterisk yang benar-benar berasingan daripada penggunaan asterisk dalam mengisytiharkan pembolehubah jenis penunjuk. Di sini, asterisk dipanggil pengendali dereference. Dalam rajah kami, apa * pointer_to_n = 8 cara, pergi kepada pointer_to_n kotak mengandungi, ikut anak panah, dan kemudian memberikan kepada kotak pada akhir arrow nilai 8. Ini bermakna bahawa selepas baris ini, jika kita cuba untuk digunakan n ia akan mempunyai nilai 8. 'Penunjuk' perkataan yang digunakan dalam banyak konteks yang berbeza. Di sini, kita akan cuba untuk menjadi konsisten. Sejenis penunjuk adalah sesuatu seperti * int. Dalam video ini, penunjuk hanya akan digunakan untuk bermakna nilai dengan jenis penunjuk, seperti pointer_to_n yang mempunyai jenis int *. Anywhere kita digunakan untuk hanya mengatakan n, kita kini boleh sebaliknya mengatakan pointer_to_n *, dan segala-galanya akan bekerja sama dengan baik. Mari kita berjalan melalui satu lagi contoh mudah. Katakan kita mempunyai int n = 14; int * pointer = &n; n + +, dan (* penunjuk) + +. Baris pertama mencipta kotak baru dalam ingatan dilabel n. Masa ini, kami tidak akan label kotak dengan alamat yang jelas, tetapi ia masih mempunyai satu. Dalam kotak, kami menyimpan nombor 14. Barisan seterusnya mewujudkan penunjuk kedua kotak yang dilabel. Dan di dalam kotak ini, kita menyimpan penunjuk kepada kotak yang dilabel n. Jadi, mari kita lukiskan anak panah dari penunjuk kepada n. Sekarang, n + + kenaikan nilai dalam kotak yang dilabel n, jadi kita pergi 14-15. Akhirnya, (* penunjuk) + + pergi kepada penunjuk kotak yang dilabel, dereferences nilai di dalam kotak, yang bermaksud mengikuti anak panah untuk mana ia mata, dan kenaikan nilai yang disimpan di sana, jadi kita pergi 15-16. Dan itu sahaja. N kini menyimpan nombor 16 setelah telah incremented dua kali - sekali terus dengan menggunakan nama n berubah-ubah, dan lain-lain melalui pointer_to_n. Kuiz cepat. Apa yang anda fikir ia bermakna jika saya cuba untuk mengatakan sesuatu seperti && n? Nah, mari kita menulis semula ini sebagai & (& n) yang melakukan perkara yang sama. (& N) mengembalikan alamat n ubah dalam ingatan. Tetapi kemudian maka #: glib luar cuba untuk kembali alamat alamat. Itulah seperti cuba untuk melakukan & 2. Ia tidak masuk akal untuk mendapatkan alamat hanya beberapa nombor kerana ia tidak disimpan dalam ingatan. Menggunakan dua ampersands dalam berturut-turut tidak pernah idea yang tepat. Tetapi sekarang, apakah ia bermakna jika saya cuba untuk mengatakan int ** double_pointer = & penunjuk? Sekarang, saya mewujudkan kotak baru yang dilabel double_pointer, dan di dalam kotak itu saya menyimpan alamat penunjuk, yang bermakna saya lukiskan anak panah dari kotak double_pointer kotak penunjuk. Notis jenis double_pointer, ** int. N adalah integer, penunjuk disimpan alamat n, dan sebagainya ia mempunyai jenis int *. Sekarang, double_pointer menyimpan alamat penunjuk, jadi ia mempunyai jenis int **. Jadi, apa yang kita fikir cara ini - ** Double_pointer = 23? Notis bahawa saya kini dereferencing dua kali. Hanya ikut rajah kotak dan anak panah kita sudah ditubuhkan. Pertama, kita pergi ke kotak yang dilabel double_pointer. * Pertama bermaksud mengikuti anak panah sekali. Sekarang, kita berada pada penunjuk kotak yang dilabel. Bintang kedua mengatakan mengikuti anak panah lagi, dan kini kita berada pada kotak berlabel n, dan kita menetapkan nilai kotak ini kepada 23. Perhatikan bahawa 'alamat' dereference dan pengendali adalah songsangan antara satu sama lain. Ini membolehkan saya untuk melakukan sesuatu seperti * & * & n = 42. Walaupun kerja-kerja ini, anda tidak perlu melakukan sesuatu seperti ini dalam amalan. Apa yang kita sebenarnya lakukan di sini? #: Glib pertama dimenangi alamat n ubah. Kemudian, kita mempunyai operator dereference, yang bermaksud kita akan alamat itu dalam ingatan, jadi kita kembali di n. Sekarang, kita merebut alamat n lagi dan segera dereference, jadi kita kembali di n dan kedai 42. Jadi, setiap sepasang * & hanya membatalkan keluar. Terdapat penunjuk khas dipanggil penunjuk nol. Ini adalah penunjuk bahawa kita tidak harus dereference. Penunjuk sedemikian adalah penting kerana ia memberikan kita cara untuk membezakan antara penunjuk yang harus dan tidak harus dereferenced. Jika anda cuba untuk dereference penunjuk nol, biasanya program anda akan berlanggar dengan kesalahan segmentasi, yang anda mungkin telah dilihat sebelum ini. Jadi, mari kita mengatakan kita mempunyai kod int * x = null; * x = 4. Dalam contoh ini, ia mungkin kelihatan jelas bahawa kita sedang melakukan sesuatu yang buruk, tetapi ingat bahawa null benar-benar boleh menjadi nilai yang dikembalikan dari panggilan ke fungsi seperti malloc, jika malloc tidak dapat memperuntukkan memori yang diminta oleh pengguna. Atas sebab ini, jika sebaliknya kita menetapkan nilai x dari panggilan malloc, seperti dalam int * x = malloc (sizeof (int)), maka kita harus sentiasa jelas memeriksa untuk melihat jika null telah dipulangkan. Jika (x == null); / / uhoh! pulangan; lagi yang kita boleh terus dan mengatakan * x = 4. Jadi, sekali lagi, mengapa perlu kita pernah menggunakan petunjuk? Mari kita melihat contoh program di mana kita perlu menggunakan petunjuk - swap fungsi mudah. Katakan saya mempunyai dua integer, int x = 4, dan int y = 15; dan saya ingin menulis satu fungsi yang dipanggil swap yang boleh saya gunakan seperti begitu: swap (x, y). Selepas baris ini, nilai-nilai di dalam x ubah harus 15, dan nilai dalam pemboleh ubah y perlu 4. Nilai di dalam x dan y telah bertukar. Tanpa petunjuk, kita mungkin cuba sesuatu seperti swap terbatal (int a, int b); int tmp = b; b = a; a = tmp. Tetapi, adakah anda notis masalah dengan ini? Ingat bahawa nilai yang disimpan dalam pembolehubah adalah hanya salinan nilai x, dan nilai dalam b disalin dari y. Sebarang perubahan yang dibuat kepada a dan b tidak akan dapat dilihat dalam x dan y. Jadi, manakala nilai a dan b betul bertukar, x dan y tidak berubah sama sekali. Sekarang, mari kita mengubah fungsi swap supaya hujah adalah petunjuk untuk pembolehubah ia perlu menukar, suka jadi: swap terbatal (int *, int * b); int tmp = * b; * b = * a; * a = tmp. Ingatlah bahawa hujah swap kini petunjuk, dan sebagainya kita perlu lulus alamat x dan y dalam panggilan untuk menukar, suka jadi: swap (& x, & y). Ini kini betul swap nilai x dan y. Mari kita lukiskan gambarajah kotak dan anak panah untuk melihat mengapa kerja-kerja ini. Kita mulakan dengan dua kotak dalam ingatan, x dan y. Dalam kotak bagi x ialah nombor 4, dan di dalam kotak untuk y kita mempunyai 15. Sekarang, dalam panggilan kepada fungsi swap, kita mempunyai dua lagi kotak bagi hujah a dan b; mata kepada kotak untuk x, dan mata b kotak untuk y. Sebuah kotak baru dicipta untuk tmp pembolehubah, dan di dalamnya kita menyimpan hasil dereferencing b, yang bermaksud 'mengikuti anak panah dari kotak yang dilabel b.' Jadi, kita menyimpan 15 dalam tmp. Kemudian, kita mengikuti anak panah di b dan menyimpan di sini hasil daripada dereferencing satu, yang 4 nilai. Akhirnya, kita ikut anak panah di kedai dan apa yang sedang di dalam tmp, yang adalah 15. Perhatikan bahawa kotak yang dilabel x dan y betul bertukar nilai. Apabila kita belajar lebih lanjut mengenai malloc dan pengurusan ingatan dinamik, kita akan melihat bahawa kita tidak mempunyai pilihan tetapi untuk menggunakan petunjuk. Berjalan melalui rajah kotak dan anak panah untuk mana-mana program boleh membantu anda memikirkan apa yang program ini adalah benar-benar melakukan. Nama saya adalah Rob Bowden, dan ini adalah CS50. [CS50.TV] Ini adalah berlainan bagi penggunaan asterisk - bleah, saya benci perkataan itu. Anywhere kita digunakan untuk hanya mengatakan n, kini kita boleh mengatakan pointer_to_n - tidak anda can't - * pointer_to_n.