[Powered by Google Translate] [Phần 5 - thoải mái hơn] [Rob Bowden - Đại học Harvard] [Đây là CS50. - CS50.TV] Giống như tôi đã nói trong email của tôi, có rất nhiều điều bạn có thể sử dụng khác hơn so với các thiết bị để thực sự làm các bài tập. Chúng tôi đề nghị bạn làm điều đó trong thiết bị chỉ vì sau đó chúng ta có thể dễ dàng giúp bạn và chúng tôi biết làm thế nào tất cả mọi thứ sẽ làm việc. Tuy nhiên, như một ví dụ về nơi bạn có thể làm những điều nếu nói, bạn không có quyền truy cập một thiết bị hoặc bạn muốn làm việc trong tầng hầm của Trung tâm Khoa học - mà thực sự họ có thiết bị - nếu bạn muốn làm việc bất cứ nơi nào. Một ví dụ có bạn thấy / nghe nói của SSH? SSH là về cơ bản giống như kết nối đến một cái gì đó. Trên thực tế, ngay bây giờ tôi SSHed vào thiết bị. Tôi không bao giờ làm việc trực tiếp trong thiết bị. Đây là thiết bị, và nếu bạn nhìn xuống ở đây bạn thấy địa chỉ IP này. Tôi không bao giờ làm việc trong thiết bị chính nó; Tôi luôn luôn đi qua một cửa sổ / cửa sổ thiết bị đầu cuối iTerm2. Bạn có thể SSH đến địa chỉ IP, ssh jharvard@192.168.129.128. Tôi nhớ rằng số lượng rất dễ dàng bởi vì nó là một mô hình tốt đẹp như vậy. Nhưng điều đó sẽ yêu cầu mật khẩu của tôi, và bây giờ tôi đang ở trong thiết bị. Về cơ bản, vào thời điểm này, nếu bạn đã mở ra một thiết bị đầu cuối bên trong của thiết bị chính nó, giao diện này, tuy nhiên bạn sẽ sử dụng nó, là chính xác giống như giao diện, tôi đang sử dụng ở đây nhưng bây giờ bạn đang SSHed. Bạn không cần phải SSH vào thiết bị. Một ví dụ về một nơi khác, bạn có thể SSH đến là tôi khá chắc chắn rằng bạn có theo mặc định - Oh. Lớn hơn. Tất cả các bạn cần phải có tài khoản FAS mặc định trên các máy chủ của FAS. Đối với tôi, tôi sẽ SSH đến rbowden@nice.fas.harvard.edu. Nó sẽ hỏi bạn là lần đầu tiên, và bạn nói có. Mật khẩu của tôi là chỉ cần đi để được mật khẩu của tôi FAS. Và vì vậy bây giờ, tôi đang SSHed đến các máy chủ tốt đẹp, và tôi có thể làm bất cứ điều gì tôi muốn ở đây. Rất nhiều các lớp học bạn có thể mất, như 124, sẽ có bạn tải lên các công cụ ở đây để thực sự gửi bộ vấn đề của bạn. Nhưng nói bạn không có quyền truy cập vào thiết bị của bạn. Sau đó, bạn có thể làm điều gì đó, giống như ở đây, nó sẽ nói - Đây chỉ là phần câu hỏi của chúng tôi. Nó sẽ yêu cầu bạn làm điều này trong thiết bị. Thay vào đó tôi sẽ chỉ làm điều đó trên máy chủ. Tôi sẽ phải giải nén mà. Vấn đề là có được rằng bạn đang sử dụng bằng cách sử dụng một cái gì đó như gedit hoặc bất cứ điều gì bên trong của thiết bị. Bạn sẽ không phải có mà trên máy chủ FAS. Đó là tất cả chỉ là giao diện này văn bản. Vì vậy, bạn có thể hoặc là một, cố gắng tìm hiểu một trình soạn thảo văn bản mà không có. Họ có Nano. Nano thường là khá dễ dàng để sử dụng. Bạn có thể sử dụng mũi tên của bạn và gõ bình thường. Vì vậy, đó không phải là khó. Nếu bạn muốn nhận được thực sự ưa thích, bạn có thể sử dụng Emacs, mà tôi có lẽ không nên đã mở ra bởi vì tôi thậm chí không biết làm thế nào để đóng Emacs. Kiểm soát X, Control C? Yeah. Hoặc bạn có thể sử dụng Vim, đó là những gì tôi sử dụng. Và do đó, những lựa chọn của bạn. Nếu bạn không muốn làm điều đó, bạn cũng có thể, nếu bạn nhìn vào manual.cs50.net - Oh. Trên một máy tính, bạn có thể SSH bằng cách sử dụng PuTTY, mà bạn sẽ phải tải về một cách riêng biệt. Trên máy Mac, bạn có thể chỉ bằng cách đầu cuối sử dụng mặc định hoặc bạn có thể tải về iTerm2, mà là giống như một Terminal tốt đẹp ưa thích,. Nếu bạn đi đến manual.cs50.net, bạn sẽ thấy một liên kết để Notepad + +, mà là những gì bạn có thể sử dụng trên một máy tính. Nó cho phép bạn SFTP từ Notepad + +, đó là cơ bản SSH. Điều này sẽ cho phép bạn làm là chỉnh sửa các tập tin của bạn tại địa phương, và sau đó bất cứ khi nào bạn muốn lưu chúng, nó sẽ tiết kiệm nice.fas sau đó bạn có thể chạy chúng. Và tương đương trên máy Mac sẽ được TextWrangler. Vì vậy, nó cho phép bạn làm điều tương tự. Nó cho phép bạn chỉnh sửa các tập tin địa phương và lưu chúng vào nice.fas, sau đó bạn có thể chạy chúng. Vì vậy, nếu bạn đang bị mắc kẹt mà không có một thiết bị, bạn có các tùy chọn vẫn còn làm bộ vấn đề của bạn. Một vấn đề là có được rằng bạn sẽ không có thư viện CS50 vì nice.fas không theo mặc định có điều đó. Bạn có thể tải về thư viện CS50 - Tôi không nghĩ rằng tôi cần phải có vào thời điểm này. Bạn có thể tải về các thư viện CS50 và sao chép nó để nice.fas, Tôi nghĩ rằng vào thời điểm này, chúng tôi không sử dụng nó nữa nào. Hoặc nếu chúng ta làm, bạn có thể cho thời gian được thay thế nó với triển khai thực hiện các chức năng trong thư viện CS50 anyway. Vì vậy, đó không phải là rằng có rất nhiều hạn chế. Và đó là điều đó. Tôi sẽ quay lại vào thiết bị ngay bây giờ, chúng tôi sẽ làm tất cả mọi thứ trong thiết bị. Nhìn vào phần câu hỏi của chúng tôi, ngay từ đầu, như tôi đã nói trong email của tôi, chúng ta phải nói về con đường ngắn bạn đã dự để xem. Chúng tôi có chuyển hướng & Ống và ba câu hỏi này. Để dòng làm chức năng như printf viết theo mặc định? Vì vậy, dòng suối. Một dòng suối là gì? Dòng A là cơ bản giống như nó chỉ là một số Nó thậm chí không một nguồn 1 và 0. Các dòng nó yêu cầu ở đây là tiêu chuẩn ra. Và do đó, tiêu chuẩn ra là một dòng suối mà khi bạn viết thư cho nó, nó xuất hiện trên màn hình. Tiêu chuẩn ra, theo dòng suối, nó có nghĩa là bạn chỉ cần viết 1 và số 0 cho nó, và đầu kia của ra tiêu chuẩn chỉ cần đọc từ dòng suối đó. Nó chỉ là một chuỗi của 1 và 0. Bạn có thể viết dòng suối hoặc bạn có thể đọc từ các dòng suối tùy thuộc vào dòng thực sự là. Hai dòng mặc định khác là tiêu chuẩn trong lỗi tiêu chuẩn. Tiêu chuẩn trong bất cứ khi nào bạn GetString, nó chờ đợi cho bạn các công cụ đầu vào. Vì vậy, nó chờ đợi cho bạn, nó thực sự chờ đợi vào tiêu chuẩn trong, mà thực sự là những gì bạn nhận được khi bạn gõ vào bàn phím. Bạn gõ vào tiêu chuẩn. Lỗi tiêu chuẩn về cơ bản là tương đương với tiêu chuẩn ra, nhưng đó là chuyên ngành trong đó khi bạn in ra lỗi tiêu chuẩn, bạn đang nghĩ đến chỉ in thông báo lỗi đó để bạn có thể phân biệt giữa các tin nhắn thường xuyên in ra màn hình so với thông báo lỗi tùy thuộc vào việc họ đã đi ra tiêu chuẩn hay sai số chuẩn. Các tập tin quá. Ra tiêu chuẩn, tiêu chuẩn, và lỗi tiêu chuẩn chỉ là dòng đặc biệt, nhưng thực sự tập tin bất kỳ, khi bạn mở một tập tin, nó sẽ trở thành một dòng các byte nơi mà bạn chỉ có thể đọc từ dòng suối chảy. Bạn, cho hầu hết các phần, chỉ có thể nghĩ về một tập tin như một dòng các byte. Vì vậy, những gì dòng họ viết theo mặc định? Tiêu chuẩn ra ngoài. Sự khác biệt giữa> và >> là gì? Có ai xem video trước? Okay. > Sẽ là làm thế nào bạn chuyển hướng vào các tập tin, và >> cũng sẽ chuyển hướng đầu ra vào các tập tin, nhưng nó thay vì để phụ thêm vào tập tin. Ví dụ, hãy nói rằng tôi xảy ra để có dict ngay tại đây, và các công cụ duy nhất bên trong của dict là con mèo, con mèo, con chó, con chó, cá. Một lệnh mà bạn có tại các dòng lệnh là con mèo, mà chỉ là sẽ in trong một tập tin. Vì vậy, khi tôi nói dict con mèo, nó sẽ in con mèo, con mèo, con chó, con chó, cá. Đó là con mèo tất cả. Điều đó có nghĩa rằng nó được in tiêu chuẩn ra con mèo, con mèo, con chó, con chó, cá. Nếu tôi thay vì muốn chuyển hướng vào một tập tin, tôi có thể sử dụng> và chuyển hướng nó vào tập tin bất cứ điều gì. Tôi sẽ gọi các tập tin tập tin. Vì vậy, bây giờ nếu tôi ls, tôi sẽ thấy tôi có một tập tin mới được gọi là tập tin. Và nếu tôi mở nó lên, nó sẽ có chính xác những gì con mèo đặt tại dòng lệnh. Vì vậy, bây giờ nếu tôi làm điều đó một lần nữa, sau đó nó sẽ chuyển hướng đầu ra vào một tập tin, và tôi sẽ có cùng một điều chính xác. Vì vậy, về mặt kỹ thuật, nó hoàn toàn overrode những gì chúng ta có. Và chúng ta sẽ thấy nếu tôi thay đổi dict, tôi đã diễn ra chó. Bây giờ nếu chúng ta mèo dict vào tập tin một lần nữa, chúng ta sẽ có phiên bản mới với loại bỏ con chó. Vì vậy, nó hoàn toàn sẽ ghi đè nó. Thay vào đó, nếu chúng ta sử dụng >>, nó sẽ nối thêm tập tin. Bây giờ, mở tập tin, chúng ta thấy chúng ta có cùng một điều in hai lần bởi vì nó đã có một lần, sau đó chúng tôi nối với bản gốc. Vì vậy, đó là những gì> và >>. Có một trong những tiếp theo yêu cầu - Nó không hỏi về nó. Một trong những khác mà chúng ta có là <, mà nếu chuyển hướng ra tiêu chuẩn, bạn làm 2> chuyển hướng lỗi tiêu chuẩn. Vì vậy, nếu một cái gì đó đã đi sai số chuẩn, nó sẽ không có được đặt thành txt2. Nhưng hãy chú ý nếu tôi làm 2>, sau đó nó vẫn còn in Xin chào, Rob! vào dòng lệnh bởi vì tôi chỉ chuyển hướng lỗi tiêu chuẩn, tôi không chuyển hướng tiêu chuẩn. Ra lỗi tiêu chuẩn và tiêu chuẩn khác nhau. Nếu bạn muốn thực sự viết ra lỗi tiêu chuẩn, sau đó tôi có thể thay đổi điều này fprintf thiết bị lỗi chuẩn. Vì vậy, printf, theo mặc định, bản in để ra tiêu chuẩn. Nếu tôi muốn in ra lỗi tiêu chuẩn bằng tay, sau đó tôi phải sử dụng fprintf và chỉ định những gì tôi muốn in. Nếu thay vào đó tôi đã làm thiết bị xuất chuẩn fprintf, thì đó là về cơ bản tương đương với printf. Nhưng fprintf lỗi tiêu chuẩn. Vì vậy, bây giờ, nếu tôi chuyển hướng vào txt2, Xin chào, Rob! vẫn nhận được in tại dòng lệnh kể từ khi nó nhận được in ra lỗi tiêu chuẩn và tôi chỉ chuyển hướng tiêu chuẩn. Nếu bây giờ tôi chuyển hướng lỗi tiêu chuẩn, bây giờ nó không được in, và txt2 là có được Xin chào, Rob! Vì vậy, bây giờ, bạn có thể in lỗi thực tế của bạn ra lỗi tiêu chuẩn và in các thông điệp thường xuyên của bạn để ra tiêu chuẩn. Và do đó, khi bạn chạy chương trình của bạn, bạn có thể chạy nó như là / hello. Loại này với 2> để chương trình của bạn sẽ chạy bình thường, nhưng bất kỳ thông báo lỗi mà bạn nhận được bạn có thể kiểm tra sau này trong nhật ký lỗi của bạn, để sai sót, và sau đó nhìn sau và tập tin lỗi của bạn sẽ có bất kỳ lỗi nào xảy ra. Câu hỏi? Người cuối cùng là đường ống, mà bạn có thể nghĩ là các tiêu chuẩn của một lệnh và làm cho nó trở thành tiêu chuẩn trong các lệnh tiếp theo. Một ví dụ ở đây là tiếng vang là một dòng lệnh điều là chỉ cần đi để echo bất cứ điều gì tôi đặt như là đối số của nó. Tôi sẽ không đặt dấu ngoặc kép. Echo blah, blah, blah là chỉ cần đi in blah, blah, blah. Trước đây, khi tôi nói rằng tôi đã phải đặt Rob vào một tập tin txt bởi vì tôi chỉ có thể chuyển hướng các file txt, thay vào đó, / Tôi echo Rob và sau đó ống vào / hello, đó cũng sẽ làm cùng một loại điều. Điều này đang ra của lệnh này, echo Rob, và sử dụng nó như là đầu vào cho / hello. Bạn có thể nghĩ về nó như chuyển hướng đầu tiên vang Rob vào một tập tin và sau đó nhập vào / hello tập tin đó đã được đưa ra cả. Nhưng phải mất các tập tin tạm thời của hình ảnh. Câu hỏi về điều đó? Câu hỏi tiếp theo sẽ liên quan đến điều này. Đường ống dẫn bạn có thể sử dụng để tìm số lượng các tên duy nhất trong một tập tin gọi là names.txt? Các lệnh chúng ta sẽ muốn sử dụng ở đây là duy nhất, do đó, uniq, và sau đó wc. Bạn có thể làm uniq người đàn ông để thực sự nhìn vào những gì mà thực hiện, và nó chỉ sẽ lọc các dòng kề nhau tương ứng từ đầu vào. Và người đàn ông wc là sẽ in từ, dòng mới, và số byte cho mỗi tập tin. Và cuối cùng chúng ta sẽ muốn sử dụng là sắp xếp, đó là sẽ chỉ cần sắp xếp các dòng file txt. Nếu tôi làm cho một số tập tin txt, names.txt, và đó là Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, những gì tôi muốn làm ở đây là tìm số lượng các tên duy nhất trong tập tin này. Vì vậy, câu trả lời nên được? >> [Sinh viên] 4. >> Yeah. Nó nên được 4 kể từ Rob, Tommy, Joseph, RJ là tên chỉ có duy nhất trong tập tin này. Bước đầu tiên, nếu tôi chỉ làm số lượng từ trên names.txt, điều này thực sự nói cho tôi tất cả mọi thứ. Điều này thực sự in ấn - Hãy xem, người đàn ông wc - dòng mới, từ, và đếm byte. Nếu tôi chỉ quan tâm về dòng, sau đó tôi chỉ có thể làm names.txt wc-l. Vì vậy, đó là bước 1. Nhưng tôi không muốn wc-l names.txt vì names.txt chỉ chứa tất cả các tên, và tôi muốn lọc ra bất kỳ những người không phải duy nhất. Vì vậy, nếu tôi làm uniq names.txt, điều đó không hoàn toàn cho tôi những gì tôi muốn bởi vì tên nhân đôi vẫn còn ở đó. Tại sao vậy? Tại sao uniq không làm những gì tôi muốn? [Sinh viên] Bản sao không phải là không nghe được] >> Yeah. Ghi trang người đàn ông cho uniq nói dòng phù hợp với bộ lọc liền kề. Chúng không phải là lân cận, do đó, nó sẽ không lọc chúng. Nếu tôi sắp xếp chúng đầu tiên, sắp xếp names.txt sẽ đặt tất cả các dòng trùng lặp với nhau. Vì vậy, bây giờ loại names.txt là. Tôi sẽ muốn sử dụng như là đầu vào uniq, | uniq. Điều đó mang lại cho tôi Joseph, RJ, Rob, Tommy, và tôi muốn sử dụng như là đầu vào wc-l, đó là sẽ cung cấp cho tôi 4. Như nó nói ở đây, bạn có thể sử dụng những đường ống dẫn? Bạn có thể làm rất nhiều thứ như bằng cách sử dụng một loạt các lệnh nơi bạn sử dụng đầu ra của một lệnh như là đầu vào cho lệnh tiếp theo. Bạn có thể làm rất nhiều thứ, rất nhiều thứ thông minh. Câu hỏi? Okay. Đó là nó cho các đường ống và chuyển hướng. Bây giờ chúng ta đi vào những thứ thực tế, các công cụ mã hóa. Bên trong của PDF này, bạn sẽ thấy lệnh này, và bạn sẽ muốn chạy lệnh này trong thiết bị của bạn. wget là lệnh chỉ nhận được một cái gì đó từ Internet, về cơ bản, wget và URL này. Nếu bạn đã đi đến URL này trong trình duyệt của bạn, nó sẽ tải về tập tin đó. Tôi chỉ cần nhấp vào nó, do đó, nó tải về các tập tin cho tôi. Nhưng viết wget điều mà bên trong của các thiết bị đầu cuối là chỉ cần đi để tải nó vào thiết bị đầu cuối của bạn. Tôi có section5.zip, và bạn sẽ muốn giải nén section5.zip, đó là sẽ cung cấp cho bạn một thư mục gọi là section5, đó là sẽ có tất cả các tập tin chúng ta sẽ được sử dụng ngày hôm nay bên trong của nó. Như tên tập tin của các chương trình này cho thấy, họ là một lỗi bit, Nhiệm vụ của bạn là để tìm ra lý do tại sao bằng cách sử dụng gdb. Tất cả mọi người có họ tải về / biết làm thế nào để có được họ tải về vào thiết bị của họ? Okay. Chạy ./buggy1, nó sẽ nói lỗi Segmentation (core dumped), bất cứ lúc nào bạn nhận được một segfault, đó là một điều xấu. Trong trường hợp nào để bạn có được một segfault? [Sinh viên] dereferencing một con trỏ null. >> Yeah. Vì vậy, đó là một ví dụ. Dereferencing một con trỏ null bạn sẽ nhận được một segfault. Gì một segfault có nghĩa là bạn đang chạm vào bộ nhớ, bạn không nên chạm vào. Vì vậy, dereferencing một con trỏ null được chạm vào địa chỉ 0, và về cơ bản, tất cả các máy tính ngày nay nói rằng địa chỉ 0 là bộ nhớ bạn không nên chạm vào. Vì vậy, đó là lý do tại sao dereferencing kết quả một con trỏ null trong một segfault. Khi bạn xảy ra để không khởi tạo một con trỏ, sau đó nó có một giá trị rác, và do đó, khi bạn cố gắng để tới đích của nó, trong khả năng tất cả các bạn đang chạm vào bộ nhớ đó là trong giữa hư không. Nếu bạn xảy ra để có được may mắn và giá trị rác xảy ra để trỏ đến một nơi nào đó trên stack hoặc một cái gì đó, sau đó khi bạn dereference con trỏ mà bạn đã không được khởi tạo, không có gì sẽ đi sai. Nhưng nếu nó chỉ đến, nói, một nơi nào đó giữa stack và heap, hoặc nó chỉ chỉ để một nơi nào đó đã không được sử dụng bởi chương trình của bạn, sau đó bạn đang chạm vào bộ nhớ bạn không nên chạm vào và bạn segfault. Khi bạn viết một hàm đệ quy và nó recurses quá nhiều lần và ngăn xếp của bạn trở nên quá lớn và va chạm ngăn xếp vào những điều rằng nó không nên va chạm với, bạn đang chạm vào bộ nhớ, bạn không nên chạm vào, vì vậy bạn segfault. Đó là những gì một segfault. Đó cũng là lý do tương tự nếu bạn có một chuỗi như thế - chúng ta hãy quay trở lại với các chương trình trước. Trong hello.c-Tôi chỉ cần đi để làm một cái gì đó khác. char * s = "hello thế giới"; Nếu tôi sử dụng * s = một cái gì đó hoặc s [0] = 'X'; do đó hãy hello, / hello, tại sao mà segfault? Tại sao điều này segfault? Những gì bạn sẽ mong đợi xảy ra? Nếu tôi đã làm printf ("% s \ n", s); những gì bạn mong chờ để được in? [Sinh viên] X hello. >> Yeah. Vấn đề là khi bạn khai báo một chuỗi như thế này, s là một con trỏ sẽ đi trên stack, và những gì s được trỏ đến chuỗi này được chứa trong bộ nhớ chỉ đọc. Vì vậy, chỉ cần bộ nhớ chỉ đọc tên, bạn sẽ nhận được ý tưởng rằng nếu bạn cố gắng thay đổi những gì trong bộ nhớ chỉ đọc, bạn đang làm một cái gì đó bạn không nên làm với bộ nhớ và bạn segfault. Điều này thực sự là một sự khác biệt lớn giữa char * s và char s []. Vì vậy, char s [], chuỗi này sẽ được đưa vào ngăn xếp, và ngăn xếp không phải là chỉ đọc, có nghĩa rằng điều này nên làm việc hoàn toàn tốt đẹp. Và nó. Hãy nhớ rằng khi tôi làm char * s = "hello thế giới!", Chính nó là trên stack nhưng s điểm đến một nơi khác, và rằng ở một nơi khác sẽ xảy ra là chỉ đọc. Nhưng char s [] chỉ là một cái gì đó trên stack. Vì vậy, đó là một ví dụ về một segfault xảy ra. Chúng ta đã thấy mà ./buggy1 dẫn đến một segfault. Về lý thuyết, bạn không nên nhìn vào buggy1.c ngay lập tức. Thay vào đó, chúng ta sẽ nhìn vào nó thông qua gdb. Chú ý rằng khi bạn nhận được Segmentation fault (core dumped), bạn nhận được tập tin này trên cốt lõi ở đây được gọi là. Nếu chúng ta ls-l, chúng ta sẽ thấy cốt lõi mà thường là một tập tin khá lớn. Đây là số lượng byte của tập tin, do đó, nó có vẻ như nó là 250-kilobyte. Lý do cho điều này là những gì các bãi chứa lõi thực sự là là khi chương trình của bạn bị treo, nhà nước của bộ nhớ chương trình của bạn chỉ được sao chép và dán vào tập tin này. Nó được bán phá giá vào tập tin đó. Chương trình này, trong khi nó đã được chạy, xảy ra để có một cách sử dụng bộ nhớ của khoảng 250 kilobyte, và vì vậy đó là những gì đã đổ vào tập tin này. Bây giờ bạn có thể nhìn vào tập tin đó nếu chúng ta làm gdb buggy1 cốt lõi. Chúng tôi chỉ có thể làm gdb buggy1, và sẽ chỉ bắt đầu gdb thường xuyên, sử dụng buggy1 là tập tin đầu vào của nó. Nhưng nếu bạn làm gdb buggy1 cốt lõi, sau đó nó đặc biệt sẽ bắt đầu lên gdb bằng cách nhìn vào tập tin đó cốt lõi. Và bạn nói buggy1 gdb phương tiện biết rằng tập tin lõi đến từ chương trình buggy1. Vì vậy, gdb buggy1 cốt lõi là sẽ mang lại cho chúng tôi ngay lập tức nơi mà chương trình đã xảy ra chấm dứt. Chúng ta thấy ở đây Chương trình kết thúc với 11 tín hiệu, phân đoạn lỗi. Chúng tôi tình cờ thấy một dòng, lắp ráp, mà có lẽ không phải là rất hữu ích. Nhưng nếu bạn gõ bt hoặc backtrace, đó sẽ là chức năng cung cấp cho chúng tôi danh sách các khung stack hiện tại của chúng tôi. Vì vậy, backtrace Có vẻ như chúng ta chỉ có hai khung stack. Đầu tiên là ngăn xếp khung chính của chúng tôi, và thứ hai là khung stack cho chức năng này mà chúng tôi xảy ra được trong, mà hình như chúng ta chỉ có mã lắp ráp cho. Vì vậy, chúng ta hãy quay trở lại vào chức năng chính của chúng tôi, và để làm điều đó chúng ta có thể làm frame 1, và tôi nghĩ rằng chúng tôi cũng có thể làm, nhưng tôi gần như không bao giờ làm - hoặc lên. Yeah. Lên và xuống. Up mang đến cho bạn một khung ngăn xếp, xuống mang đến cho bạn một khung stack. Tôi có xu hướng không bao giờ sử dụng. Tôi chỉ nói cụ thể 1 frame, được đi đến khung có nhãn 1. Frame 1 là sẽ mang lại cho chúng tôi vào khung chính stack, và nó nói ngay tại đây các dòng mã chúng tôi xảy ra được. Nếu chúng ta muốn thêm một vài dòng mã, chúng ta có thể nói danh sách, và đó sẽ cung cấp cho chúng tôi tất cả các dòng của mã xung quanh nó. Dòng segfaulted tại 6: if (strcmp ("CS50 đá", argv [1]) == 0). Nếu nó không phải là rõ ràng nhưng bạn có thể nhận được trực tiếp từ đây chỉ bằng cách suy nghĩ lý do tại sao nó segfaulted. Nhưng chúng tôi có thể mang nó một bước xa hơn và nói, "Tại sao argv [1] segfault?" Hãy in argv [1], và nó trông giống như 0x0 của nó, đó là con trỏ null. Chúng tôi đang strcmping CS50 đá và vô giá trị, và đó là sẽ segfault. Và tại sao argv [1] null? [Sinh viên] Bởi vì chúng tôi không cung cấp cho bất kỳ đối số dòng lệnh. Yeah. Chúng tôi không cung cấp cho bất kỳ đối số dòng lệnh. Vì vậy, ./buggy1 được chỉ sẽ có argv [0] là ./buggy1. Nó sẽ không có một argv [1], vì vậy đó là sẽ segfault. Nhưng nếu thay vì đó, tôi làm chỉ CS50, nó sẽ nói Bạn nhận được một D bởi vì đó là những gì nó phải làm. Nhìn vào buggy1.c, đó là nghĩa vụ in "Bạn nhận được một D" - Nếu argv [1] không "CS50 đá", "Bạn nhận được một D", khác "Bạn nhận được một A!" Vì vậy, nếu chúng ta muốn một, chúng tôi cần điều này để so sánh là đúng, điều đó có nghĩa là nó so sánh là 0. Vì vậy, argv [1] cần phải được "CS50 đá". Nếu bạn muốn làm điều đó trên dòng lệnh, bạn cần phải sử dụng \ để thoát khỏi không gian. Vì vậy, CS50 \ đá và Bạn nhận được một A! Nếu bạn không làm dấu gạch chéo ngược, tại sao điều này không làm việc? [Sinh viên] Đây là hai đối số khác nhau. >> Yeah. Argv [1] là có được CS50, và argv [2] là có được đá. Okay. Bây giờ ./buggy2 sẽ segfault một lần nữa. Thay vì mở nó với các tập tin cốt lõi của nó, chúng tôi sẽ chỉ mở ra buggy2 trực tiếp, do đó, gdb buggy2. Bây giờ nếu chúng ta chỉ cần chạy chương trình của chúng tôi, sau đó nó sẽ nói Chương trình đã nhận được tín hiệu SIGSEGV, đó là segfault tín hiệu, và đây là nơi mà nó đã xảy ra xảy ra. Nhìn vào backtrace của chúng tôi, chúng tôi thấy rằng chúng tôi đã trong oh_no chức năng, được gọi là dinky chức năng, được gọi là bởi binky chức năng, được gọi là chính. Chúng tôi cũng có thể nhìn thấy các đối số cho các chức năng này. Đối số để dinky và binky 1. Nếu chúng ta liệt kê các chức năng oh_no, chúng ta thấy rằng oh_no chỉ làm char ** s = NULL; * S = "BOOM"; Tại sao mà có thất bại? [Sinh viên] Bạn có thể không tới đích của con trỏ null? >> Yeah. Đây chỉ là nói s là NULL, bất kể nếu điều đó xảy ra là một char **, đó, tùy thuộc vào cách bạn giải thích nó, nó có thể là một con trỏ đến một con trỏ đến một chuỗi hoặc một mảng của chuỗi. Đó là s là NULL, do đó * s dereferencing một con trỏ null, và do đó, điều này sẽ sụp đổ. Đây là một trong những cách nhanh nhất bạn có thể có thể segfault. Nó chỉ cần khai báo một con trỏ null, và ngay lập tức segfaulting. Đó là những gì oh_no làm. Nếu chúng ta đi lên một khung hình, sau đó chúng tôi đang đi để có được vào các chức năng mà được gọi là oh_no. Tôi cần để làm điều đó xuống. Nếu bạn không nhập một câu lệnh và bạn chỉ cần nhấn Enter lần nữa, nó sẽ chỉ lặp lại các lệnh trước đó mà bạn chạy. Chúng tôi đang trong frame 1. Liệt kê khung này, chúng ta thấy ở đây là chức năng của chúng tôi. Bạn có thể nhấn danh sách một lần nữa, hoặc bạn có thể làm danh sách 20 và nó sẽ liệt kê nhiều hơn. Dinky chức năng nói rằng nếu tôi là 1, sau đó đi đến chức năng oh_no, khác đi đến chức năng Slinky. Và chúng ta biết i là 1 bởi vì chúng tôi xảy ra để xem ở đây dinky đã được gọi với các đối số 1. Hoặc bạn chỉ có thể làm print i và nó sẽ nói i là 1. Chúng tôi đang trong dinky, và nếu chúng ta đi lên một khung, chúng ta biết chúng ta sẽ kết thúc trong binky. Up. Bây giờ chúng tôi đang trong binky. Danh sách chức năng này - danh sách từ trước khi một nửa cắt cho tôi đi - nó bắt đầu như thể tôi là 0, sau đó chúng ta sẽ gọi nó oh_no, khác gọi dinky. Chúng tôi biết tôi đã được 1, do đó nó được gọi là dinky. Và bây giờ chúng tôi đang trở lại trong chính, và chính là chỉ cần đi int i = rand () 3%; Đó chỉ là sẽ cung cấp cho bạn một số ngẫu nhiên đó là là 0, 1, hoặc 2. Nó sẽ gọi binky với con số đó, và nó sẽ trở về 0. Nhìn này, chỉ cần đi bộ qua các chương trình bằng tay mà không cần chạy nó ngay lập tức, bạn sẽ thiết lập một điểm break ở chính, có nghĩa là khi chúng ta chạy chương trình chương trình của bạn chạy cho đến khi nó chạm một điểm break. Vì vậy, chạy chương trình, nó sẽ chạy và sau đó nó sẽ được tung ra chức năng chính và ngừng chạy. Bây giờ chúng ta đang ở trong chính, và bước tiếp theo sẽ đưa chúng ta đến dòng tiếp theo của mã. Bạn có thể làm bước tiếp theo. Đánh tiếp theo, bây giờ tôi đã được thiết lập để rand ()% 3, vì vậy chúng ta có thể in giá trị của i, và nó sẽ nói i là 1. Bây giờ nó không thành vấn đề cho dù chúng tôi sử dụng tiếp theo hoặc bước. Tôi đoán nó quan trọng trước đó, nhưng chúng tôi muốn sử dụng tiếp theo. Nếu chúng ta sử dụng bước, chúng ta bước vào chức năng, nhiệm vụ, có nghĩa là nhìn vào điều thực tế đang xảy ra bên trong của binky. Nếu chúng ta sử dụng tiếp theo, sau đó nó có nghĩa là đi qua các chức năng và chỉ cần đi vào dòng tiếp theo của mã trong chức năng chính của chúng tôi. Ở ngay trên dòng này, tôi đã được ở nơi mà nó nói rand ()% 3; nếu tôi đã làm bước, nó sẽ đi vào thực hiện của rand và nhìn vào những gì đang xảy ra ở đó, và tôi có thể bước qua chức năng rand. Nhưng tôi không quan tâm về các chức năng rand. Tôi chỉ muốn đi đến dòng tiếp theo của mã trong main, vì vậy tôi sử dụng tiếp theo. Nhưng bây giờ tôi quan tâm về các chức năng binky, vì vậy tôi muốn bước vào đó. Bây giờ tôi đang trong binky. Dòng đầu tiên của mã sẽ nói if (i == 0), tôi đi một bước, chúng ta thấy chúng ta kết thúc tại dinky. Nếu chúng ta điều danh sách, chúng tôi thấy rằng nó kiểm tra là i = 0. tôi không phải là bằng 0, do đó, nó đến điều kiện khác, đó là sẽ gọi dinky (i). Bạn có thể bị lẫn lộn. Nếu bạn chỉ cần nhìn vào những dòng trực tiếp, bạn có thể nghĩ rằng nếu (i == 0), okay, sau đó tôi đã tiến một bước và bây giờ tôi tại dinky (i), bạn có thể nghĩ rằng phải có nghĩa là i = 0 hoặc một cái gì đó. Nó chỉ có nghĩa là nó biết nó có thể dính trực tiếp đến dinky dòng (i). Bởi vì tôi không phải là 0, bước tiếp theo là sẽ không kết thúc tại người nào khác. Khác không phải là một dòng nó sẽ dừng lại ở. Nó chỉ là để đi đến dòng kế tiếp, nó thực sự có thể thực hiện, đó là dinky (i). Bước vào dinky (i), chúng ta thấy if (i == 1). Chúng tôi không biết i = 1, do đó, khi chúng ta bước, chúng ta biết chúng ta sẽ kết thúc trong oh_no bởi vì i = 1 gọi oh_no chức năng, mà bạn có thể bước vào, đó là sẽ thiết lập char ** s = NULL và ngay lập tức "bùng nổ". Và sau đó thực sự nhìn vào việc thực hiện các buggy2, này, tôi chỉ nhận được một số ngẫu nhiên - 0, 1, hoặc 2 - gọi binky, nếu tôi là 0 nó gọi oh_no, khác nó gọi dinky, mà đi kèm ở đây. Nếu tôi là 1, gọi oh_no, khác gọi Slinky, đến đây, nếu tôi là 2, gọi oh_no. Tôi thậm chí không nghĩ rằng có một cách - Có ai nhìn thấy một cách để làm cho một chương trình mà sẽ không segfault? Bởi vì trừ khi tôi đang thiếu một cái gì đó, nếu tôi là 0, bạn ngay lập tức sẽ segfault, khác bạn đi đến một chức năng mà nếu tôi là 1 bạn segfault, khác bạn đi đến một chức năng mà nếu tôi là 2 bạn segfault. Vì vậy, không có vấn đề gì bạn làm, bạn segfault. Tôi đoán một cách để sửa chữa nó sẽ là thay vì làm char ** s = NULL, bạn có thể malloc không gian cho chuỗi đó. Chúng tôi có thể làm malloc (sizeof) - sizeof những gì? [Sinh viên] (char) * 5? >> Điều này có vẻ phải không? Tôi giả định này sẽ làm việc nếu tôi thực sự chạy nó, nhưng nó không phải là những gì tôi đang tìm kiếm. Nhìn vào các loại s. Hãy thêm int *, do đó, int * x. Tôi sẽ làm malloc (sizeof (int)). Hoặc nếu tôi muốn có một mảng của 5, tôi sẽ làm (sizeof (int) * 5); Điều gì sẽ xảy ra nếu tôi có một ** int? Tôi sẽ malloc? [Sinh viên] Kích thước của con trỏ. >> Yeah. (Sizeof (int *)); Cùng một điều ở đây. Tôi muốn (sizeof (char *)); Điều này sẽ phân bổ không gian cho con trỏ trỏ "BOOM". Tôi không cần phải phân bổ không gian cho "BOOM" bởi vì điều này về cơ bản là tương đương với những gì tôi đã nói trước char * x = "BOOM". "Bùng nổ" đã tồn tại. Nó xảy ra tồn tại trong khu vực chỉ đọc của bộ nhớ. Nhưng nó đã tồn tại, có nghĩa là dòng mã này, nếu s là một char **, * s là một char * và bạn đang thiết lập này char * để trỏ đến "bùng nổ". Nếu tôi muốn sao chép "BOOM" vào s, sau đó tôi sẽ cần phải phân bổ không gian cho s. Tôi sẽ làm * s = malloc (sizeof (char) * 5); Tại sao 5? Tại sao không 4? Nó trông giống như "BOOM" là 4 ký tự. >> [Sinh viên] ký tự null. Yeah. Tất cả các chuỗi của bạn sẽ cần các ký tự null. Bây giờ tôi có thể làm một cái gì đó như strcat chức năng sao chép một chuỗi là gì? [Sinh viên] CPY? >> Strcpy. người đàn ông strcpy. Vì vậy, strcpy hoặc strncpy. strncpy là một chút an toàn hơn kể từ khi bạn có thể xác định chính xác có bao nhiêu nhân vật, nhưng ở đây nó không quan trọng bởi vì chúng tôi biết. Vì vậy, strcpy và tìm trong các đối số. Đối số đầu tiên là điểm đến của chúng tôi. Số thứ hai là nguồn của chúng tôi. Chúng tôi sẽ sao chép vào * điểm đến của chúng tôi s con trỏ "BOOM". Tại sao bạn có thể muốn làm điều này với một strcpy thay vì chỉ là những gì chúng tôi đã có trước khi * s = "bùng nổ"? Có một lý do bạn có thể muốn làm điều này, nhưng lý do đó là những gì? [Sinh viên] Nếu bạn muốn thay đổi một cái gì đó trong "BOOM". >> Yeah. Bây giờ tôi có thể làm một cái gì đó như s [0] = 'X'; vì s điểm vào đống và không gian trên heap là được trỏ đến là một con trỏ đến nhiều không gian hơn trên heap, được lưu trữ "BOOM". Vì vậy, bản sao của "BOOM" này đang được lưu trữ trong heap. Về mặt kỹ thuật có hai bản sao của "bùng nổ" trong chương trình của chúng tôi. Có một trong những đầu tiên đó chỉ là do hằng số này "BOOM" chuỗi, và bản sao thứ hai của "BOOM", strcpy tạo ra các bản sao của "bùng nổ". Tuy nhiên, các bản sao của "BOOM" được lưu trữ trên heap, và đống bạn đang miễn phí để thay đổi. Heap là không chỉ đọc, do đó có nghĩa là s [0] sẽ cho phép bạn thay đổi giá trị của "BOOM". Nó sẽ cho phép bạn thay đổi các ký tự. Câu hỏi? Okay. Chuyển sang buggy3, chúng ta hãy gdb buggy3. Chúng tôi chỉ cần chạy nó và chúng ta thấy chúng tôi có được một segfault. Nếu chúng ta backtrace, chỉ có hai chức năng. Nếu chúng ta đi vào chức năng chính của chúng tôi, chúng tôi thấy rằng chúng tôi segfaulted tại dòng này. Vì vậy, chỉ cần nhìn vào dòng này, for (int line = 0; fgets công cụ này không NULL không bằng nhau; dòng + +). Khung trước của chúng tôi được gọi là _IO_fgets. Bạn sẽ thấy rằng rất nhiều với tích hợp chức năng C, khi bạn nhận được segfault, sẽ có tên chức năng thực sự khó hiểu như thế này _IO_fgets. Nhưng điều đó đang xảy ra liên quan đến cuộc gọi này fgets. Một nơi nào đó bên trong ở đây, chúng tôi được segfaulting. Nếu chúng ta nhìn vào các đối số fgets, chúng ta có thể in đệm. Hãy in như là một - Ồ, không. In không phải là đi làm việc chính xác như tôi muốn nó. Chúng ta hãy nhìn vào chương trình thực tế. Đệm là một mảng ký tự. Đó là một mảng ký tự 128 ký tự. Vì vậy, khi tôi nói đệm in, nó sẽ in 128 ký tự, mà tôi đoán là gì được mong đợi. Những gì tôi đang tìm kiếm là in địa chỉ của bộ đệm, nhưng điều đó không thực sự cho tôi rất nhiều. Vì vậy, khi tôi xảy ra để nói lên ở đây x đệm, nó cho thấy tôi 0xbffff090, , nếu bạn nhớ từ trước đó hoặc một số điểm, Oxbffff có xu hướng trở thành khu vực stack-ish. Ngăn xếp có xu hướng để bắt đầu một nơi nào đó dưới 0xc000. Chỉ cần nhìn thấy địa chỉ này, tôi biết rằng bộ đệm đang xảy ra trên stack. Khởi động lại chương trình của tôi, chạy, lên, đệm, chúng ta đã thấy là chuỗi các ký tự này có khá nhiều vô nghĩa. Sau đó, in tập tin, tập tin trông giống như những gì? [Sinh viên] Null. >> Yeah. Tập tin là một * FILE loại, do đó, nó là một con trỏ, và giá trị của con trỏ đó là null. Vì vậy, fgets sẽ cố gắng để đọc từ con trỏ đó một cách gián tiếp, nhưng để có thể truy cập vào con trỏ, nó phải tới đích của nó. Hoặc, để truy cập vào những gì nó nên được trỏ đến, nó dereferences nó. Vì vậy, nó dereferencing một con trỏ null và nó segfaults. Tôi đã có thể khởi động lại nó ở đó. Nếu chúng ta phá vỡ tại điểm chính của chúng tôi và chạy, dòng đầu tiên của mã là char * filename = "nonexistent.txt"; Điều đó sẽ cho một gợi ý khá lớn là lý do tại sao chương trình này không thành công. Gõ tiếp theo mang lại cho tôi đến dòng kế tiếp, nơi tôi mở tập tin này, và sau đó tôi ngay lập tức nhận được vào đường dây của chúng tôi, một khi tôi nhấn tiếp theo, nó sẽ segfault. Có ai muốn ném ra một lý do tại sao chúng tôi có thể được segfaulting? [Sinh viên] File không tồn tại. >> Yeah. Điều này được coi là một gợi ý rằng bất cứ khi nào bạn mở một tập tin, bạn cần kiểm tra rằng tập tin thực sự tồn tại. Vì vậy, ở đây, "nonexistent.txt"; Khi chúng tôi fopen tên tập tin để đọc, sau đó chúng tôi cần phải nói (file == NULL) và nói printf ("File không tồn tại!" hoặc tốt hơn nữa - filename); trả lại 1; Vì vậy, bây giờ chúng tôi kiểm tra để xem nếu nó là NULL trước khi thực sự tiếp tục và cố gắng để đọc từ tập tin đó. Chúng ta có thể làm lại nó chỉ để thấy rằng các công trình. Tôi có ý định để bao gồm một dòng mới. Vì vậy, bây giờ nonexistent.txt không tồn tại. Bạn nên luôn luôn kiểm tra cho các loại điều này. Bạn nên luôn luôn kiểm tra xem nếu fopen trả về NULL. Bạn nên luôn luôn kiểm tra để chắc chắn rằng malloc không trở về NULL, hoặc người nào khác bạn segfault. Bây giờ buggy4.c. Chạy. Tôi đoán này được chờ đợi đầu vào hoặc Looping có thể vô hạn. Có, nó là vòng lặp vô hạn. Vì vậy, buggy4. Dường như chúng ta đang vô hạn vòng lặp. Chúng tôi có thể phá vỡ chính, chạy chương trình của chúng tôi. Gdb, miễn là chữ viết tắt mà bạn sử dụng là rõ ràng hoặc chữ viết tắt đặc biệt mà họ cung cấp cho bạn, sau đó bạn có thể sử dụng n để sử dụng thay vì phải gõ tiếp theo tất cả các cách. Và bây giờ tôi đã trúng n một lần, tôi chỉ có thể nhấn Enter để tiếp tục đi tiếp theo thay vì phải nhấn n Enter, n Enter, n Nhập. Có vẻ như tôi là trong một số loại cho vòng lặp đó là thiết lập mảng [i] đến 0. Có vẻ như tôi không bao giờ phá vỡ của vòng lặp for. Nếu tôi in tôi, vì vậy tôi là 2, sau đó tôi sẽ đi tiếp theo. Tôi sẽ in i, i là 3, sau đó tôi sẽ đi tiếp theo. Tôi sẽ in i và i là 3. Tiếp theo, in i, i là 4. Trên thực tế, in ấn sizeof (array), do đó, kích thước của mảng là 20. Nhưng có vẻ như có một số lệnh gdb đặc biệt để đi cho đến khi một cái gì đó sẽ xảy ra. Nó giống như thiết lập một điều kiện về giá trị của biến. Nhưng tôi không nhớ nó là gì. Vì vậy, nếu chúng ta tiếp tục đi - Bạn đang nói gì vậy? Những gì bạn đã mang đến? [Sinh viên] hiển thị tôi thêm - >> Vâng. Vì vậy, hiển thị tôi có thể giúp. Nếu chúng ta chỉ hiển thị i, nó sẽ đưa ra ở đây những gì giá trị của i là vì vậy tôi không phải in nó ra mỗi lần. Nếu chúng ta chỉ tiếp tục đi tiếp theo, chúng ta thấy 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Một cái gì đó khủng khiếp sai, và tôi đang được đặt lại là 0. Nhìn vào buggy4.c, chúng ta thấy tất cả những điều đó xảy ra là int array [5]; (i = 0; i <= sizeof (array); i + +) mảng [i] = 0; Gì chúng ta thấy đó là sai ở đây? Là một gợi ý, khi tôi đã làm gdb buggy4 - hãy phá vỡ chính, chạy - Tôi đã in sizeof (array) chỉ để xem những gì điều kiện là nơi cuối cùng tôi đã thoát ra khỏi. Tôi đang ở đâu? Tôi đã chạy? Tôi đã không tuyên bố. Vì vậy, in sizeof (array) và là 20, trong đó dự kiến ​​kể từ mảng của tôi là có kích thước 5 và nó gồm 5 số nguyên, do đó, toàn bộ điều nên được 5 * sizeof (int) byte, sizeof (int) có xu hướng là 4. Vì vậy, sizeof (array) là 20. Điều này nên được? [Sinh viên] Phân theo sizeof (int). >> Yeah, / sizeof (int). Dường như vẫn còn có một vấn đề ở đây. Tôi nghĩ rằng điều này chỉ nên được < vì nó khá nhiều luôn luôn > [Bowden]. Khi chúng tôi đang đi xa hơn vào cuối mảng của chúng tôi, bằng cách nào đó mà không gian mà chúng ta đang trọng là trọng giá trị của i. Và như vậy nếu chúng ta nhìn vào buggy4, phá vỡ chính, chạy, hãy in địa chỉ của tôi. Có vẻ như đó là bffff124. Bây giờ chúng ta hãy in địa chỉ của mảng [0]. 110. [1] 114. [2], 118. 11c, 120. array [5] là bfff124. Vì vậy, array [5] có cùng một địa chỉ như tôi, có nghĩa là mảng đó [5] là tôi. Nếu họ có cùng một địa chỉ, họ là những điều tương tự. Vì vậy, khi chúng tôi đặt array [5] đến 0, chúng tôi đang thiết lập i đến 0. Và nếu bạn nghĩ về điều này trong các điều khoản của ngăn xếp, int i được khai báo đầu tiên, có nghĩa là tôi nhận được một số không gian trên stack. Sau đó, array [5] được giao, do đó sau đó 20 byte được cấp phát trên stack. Vì vậy, tôi được phân bổ đầu tiên, sau đó 20 byte này được phân bổ. Vì vậy, tôi sẽ xảy ra ngay trước khi mảng, và vì cách, giống như tôi đã nói tuần trước, nơi mà kỹ thuật ngăn xếp phát triển xuống, khi bạn chỉ mục vào một mảng, chúng tôi được đảm bảo rằng vị trí 0 trong mảng luôn luôn xảy ra trước khi vị trí đầu tiên trong mảng. Đây là loại như thế nào tôi vẽ nó tuần trước. Chú ý rằng ở phía dưới, chúng tôi có địa chỉ 0 và ở đầu, chúng tôi có địa chỉ Max. Ngăn xếp luôn phát triển. Hãy nói rằng chúng tôi phân bổ i. Chúng tôi phân bổ số nguyên i, có nghĩa là chúng ta hãy chỉ nói lên ở đây số nguyên i được phân bổ. Sau đó, chúng tôi phân bổ mảng của chúng tôi gồm 5 số nguyên, có nghĩa là nằm bên dưới, kể từ khi ngăn xếp được phát triển, 5 số nguyên được phân bổ. Nhưng vì mảng làm việc như thế nào, chúng tôi đảm bảo rằng vị trí đầu tiên trong mảng luôn luôn có một địa chỉ ít hơn điều thứ hai trong mảng. Vì vậy, vị trí 0 mảng luôn luôn có thể xảy ra đầu tiên trong bộ nhớ, trong khi mảng vị trí số 1 đã xảy ra sau đó và một mảng vị trí thứ 2 có thể xảy ra sau đó, điều đó có nghĩa rằng 0 vị trí mảng sẽ xảy ra ở đâu đó xuống đây, mảng vị trí 1 sẽ xảy ra ở trên đó bởi vì di chuyển lên có nghĩa là địa chỉ cao hơn kể từ khi địa chỉ tối đa là ở đây. Vì vậy, array [0] xuống đây, mảng [1] lên ở đây, array [2] lên ở đây, mảng [3] ở đây. Chú ý trước khi chúng tôi phân bổ số nguyên i tất cả các con đường lên đây, khi chúng ta tiến xa hơn và sâu hơn vào mảng của chúng tôi, chúng tôi đang nhận được gần hơn và gần gũi hơn với số nguyên của chúng tôi i. Nó chỉ như vậy sẽ xảy ra rằng mảng [5], đó là một vị trí vượt ra ngoài mảng của chúng tôi, chính xác nơi mà số nguyên i xảy ra được phân bổ. Vì vậy, đó là điểm mà chúng tôi tình cờ được nhấn không gian trên stack được phân bổ cho số nguyên i, và chúng ta đang cài đặt mà đến 0. Đó là làm thế nào mà các công trình. Câu hỏi? Yeah. [Sinh viên] Không bao giờ tâm trí. Okay. [Sinh viên] Làm thế nào để bạn tránh những loại lỗi? Những loại lỗi? Không sử dụng C là ngôn ngữ lập trình của bạn. Sử dụng một ngôn ngữ đó có mảng giới hạn kiểm tra. Miễn là bạn đang cẩn thận, bạn chỉ cần để tránh đi qua các giới hạn của mảng của bạn. [Sinh viên] Vì vậy, ở đây khi chúng tôi đi qua các giới hạn của mảng của bạn Bowden] Đó là nơi mà mọi thứ bắt đầu đi sai. >> [Sinh viên] Oh, okay. Miễn là bạn ở trong cấp phát bộ nhớ cho mảng của bạn, bạn đã đúng. Nhưng C không có kiểm tra lỗi. Nếu tôi làm mảng [1000], nó sẽ sẵn sàng chỉ cần thay đổi bất cứ điều gì xảy ra - Nó đi vào đầu của mảng, sau đó nó đi 1000 vị trí sau và đặt nó là 0. Nó không làm bất kỳ kiểm tra rằng oh, điều này không thực sự có 1000 điều trong đó. 1000 là cách xa hơn những gì tôi nên được thay đổi, trong khi Java hoặc một cái gì đó bạn sẽ nhận được mảng giới hạn chỉ số hoặc chỉ số giới hạn ngoại lệ. Đó là lý do tại sao rất nhiều ngôn ngữ cấp cao hơn có những việc này mà nếu bạn đi vượt ra ngoài giới hạn của mảng, bạn không do đó, bạn không thể thay đổi mọi thứ từ bên dưới bạn và sau đó mọi thứ đi tồi tệ hơn nhiều hơn là chỉ nhận một ngoại lệ nói rằng bạn đã đi vượt ra ngoài cuối của mảng. [Sinh viên] Và vì vậy nên chúng ta chỉ cần thay đổi <= chỉ > [Bowden] Yeah. Nó nên được > [Sinh viên] Right. Câu hỏi nhiều hơn? Okay. [Sinh viên] Tôi có một câu hỏi. >> Yeah. [Sinh viên] biến mảng thực tế là gì? [Bowden] Giống như những gì là mảng? Mảng chính nó là một biểu tượng. Nó chỉ là địa chỉ của sự bắt đầu của 20 byte mà chúng tôi đang tham khảo. Bạn có thể nghĩ về nó như một con trỏ, nhưng nó là một con trỏ hằng. Ngay sau khi mọi thứ có được biên dịch, biến mảng không tồn tại nữa. [Sinh viên] Vì vậy, làm thế nào để tìm thấy kích thước của mảng? Kích thước của mảng đề cập đến kích thước của khối đó mà biểu tượng mà đề cập đến. Khi tôi làm một cái gì đó giống như printf ("% p \ n", array); chúng ta hãy chạy nó. Tôi đã làm gì chỉ cần làm sai? Mảng 'mảng' tuyên bố đây. Oh, ở đây. Clang là thông minh, và nó xảy ra để thông báo rằng tôi tuyên bố các mảng như 5 yếu tố nhưng tôi lập chỉ mục vào vị trí 1000. Nó có thể làm điều đó bởi vì đây chỉ là hằng số. Nó chỉ có thể đi cho đến nay nhận thấy rằng tôi sẽ vượt ra ngoài giới hạn của mảng. Nhưng hãy chú ý trước khi chúng tôi đã có tôi là không chính xác, nó không thể xác định có bao nhiêu giá trị tôi có thể đưa vào, vì vậy nó không thể xác định rằng tôi đã được đi xa hơn vào cuối mảng. Đó chỉ là Clang là thông minh. Nhưng bây giờ làm cho buggy4. Vì vậy, những gì khác tôi làm sai? Ngầm tuyên bố thư viện chức năng 'printf'. Tôi sẽ muốn # include. Okay. Bây giờ chạy buggy4. In giá trị của mảng như tôi đã làm ở đây, in nó như một con trỏ in cái gì đó trông như thế này - bfb8805c - đó là một số địa chỉ đó là trong khu vực ngăn xếp-ish. Mảng chính nó như một con trỏ, nhưng nó không phải là một con trỏ thực tế, kể từ khi một con trỏ thường xuyên, chúng ta có thể thay đổi. Mảng là chỉ là một số không đổi. 20 khối bộ nhớ bắt đầu tại địa chỉ 0xbfb8805c. Vì vậy, bfb8805c thông qua địa chỉ này +20- hoặc tôi đoán -20 - là tất cả các cấp phát bộ nhớ cho mảng này. Array, biến chính nó là không lưu trữ bất cứ nơi nào. Khi bạn đang biên soạn, biên dịch - tay sóng tại - nhưng trình biên dịch sẽ chỉ sử dụng nó biết mảng được. Nó biết nơi mảng đó bắt đầu, và do đó, nó có thể luôn luôn chỉ làm những việc về đối tượng dời từ đầu đó. Nó không cần một biến riêng của mình để đại diện cho mảng. Nhưng khi tôi làm một cái gì đó giống như int * array = p; p là một con trỏ chỉ vào mảng đó, và bây giờ p thực sự tồn tại trên stack. Tôi là miễn phí để thay đổi p. Tôi có thể làm p = malloc. Vì vậy, ban đầu nó chỉ vào mảng, bây giờ nó chỉ vào một số không gian trên heap. Tôi không thể làm mảng = malloc. Nếu Clang là thông minh, nó sẽ hét vào mặt tôi ngay off the bat. Trên thực tế, tôi khá chắc chắn gcc sẽ làm điều này quá. Vì vậy, array loại 'int [5]' là không thể chuyển nhượng. Bạn không thể chỉ định một cái gì đó để một loại mảng vì mảng chỉ là một hằng số. Đó là một biểu tượng đó tài liệu tham khảo 20 byte. Tôi không thể thay đổi nó. [Sinh viên] mà là kích thước của mảng được lưu trữ? [Bowden không được lưu trữ bất cứ nơi nào. Đó là khi nó được biên dịch. Vì vậy, nơi kích thước của mảng được lưu trữ? Bạn chỉ có thể sử dụng sizeof (array) bên trong chức năng rằng các mảng được khai báo. Vì vậy, nếu tôi làm một số chức năng, foo, và tôi làm (int array []) printf ("% d \ n", sizeof (array)); và sau đó xuống đây tôi gọi foo (array); bên trong của chức năng này - chúng ta hãy chạy nó. Đây là Clang thông minh một lần nữa. Nó nói với tôi rằng sizeof trên tham số của hàm mảng sẽ trở lại kích thước của '* int'. Đây sẽ là một lỗi, nếu nó không phải là những gì tôi muốn xảy ra. Hãy thực sự tắt Werror. Cảnh báo. Cảnh báo là tốt. Nó vẫn sẽ biên dịch miễn là nó có một cảnh báo. . / A.out sẽ in 4. Những cảnh báo đã được tạo ra là một chỉ báo rõ ràng về những gì đã đi sai. Mảng int này là chỉ cần đi in sizeof (int *). Ngay cả nếu tôi đặt array [5] ở đây, nó vẫn chỉ là sẽ in sizeof (int *). Vì vậy, ngay sau khi bạn vượt qua nó vào một hàm, sự khác biệt giữa các mảng và con trỏ là không tồn tại. Điều này xảy ra là một mảng đã được khai báo trên stack, nhưng ngay sau khi chúng tôi vượt qua giá trị đó, 0xbf blah, blah, blah vào chức năng này, sau đó con trỏ này trỏ đến mảng đó trên stack. Vì vậy, đó có nghĩa là sizeof chỉ được áp dụng trong các chức năng mà mảng này được tuyên bố, có nghĩa là khi bạn đang biên dịch chức năng này, khi Clang thông qua chức năng này, nó thấy mảng là một mảng int có kích thước 5. Vì vậy, sau đó nó thấy sizeof (array). Vâng, đó là 20. Đó thực sự là như thế nào sizeof về cơ bản làm việc cho hầu hết các trường hợp. Sizeof là không phải là một chức năng, nó là một nhà điều hành. Bạn không gọi hàm sizeof. Sizeof (int), trình biên dịch sẽ dịch đến 4. Đã nhận nó? Okay. [Sinh viên] Vì vậy, sự khác biệt giữa sizeof (array) chính và trong foo là gì? Điều này là bởi vì chúng ta đang nói sizeof (array), mà là của kiểu int *, trong khi mảng xuống đây không phải là kiểu int *, nó là một mảng int. [Sinh viên] Vì vậy, nếu bạn đã có các tham số trong mảng [] thay vì mảng int * đó có nghĩa là bạn vẫn có thể thay đổi mảng, vì bây giờ nó là một con trỏ? [Bowden] Như thế này? >> [Sinh viên] Yeah. Bạn có thể thay đổi mảng trong phạm vi chức năng bây giờ? [Bowden] Bạn có thể thay đổi mảng trong cả hai trường hợp. Trong cả hai trường hợp, bạn được tự do để nói array [4] = 0. [Sinh viên] Nhưng bạn có thể làm cho điểm mảng đến cái gì khác? [Bowden] Oh. Yeah. Trong cả hai trường hợp - >> [sinh viên] Yeah. Bowden] Sự khác biệt giữa mảng [] và một mảng int *, không có. Bạn cũng có thể nhận được một số mảng đa chiều ở đây đối với một số cú pháp thuận tiện, nhưng nó vẫn chỉ là một con trỏ. Điều này có nghĩa rằng tôi là miễn phí để làm mảng = malloc (sizeof (int)), và bây giờ chỉ ở một nơi khác. Nhưng giống như cách làm việc này mãi mãi và luôn luôn, thay đổi mảng này bằng cách làm cho nó trỏ đến cái gì khác không thay đổi mảng này ở đây vì nó là một bản sao của các đối số, nó không phải là một con trỏ đến đối số đó. Và trên thực tế, cũng giống như dấu hiệu cho thấy rằng nó chính xác như nhau - chúng tôi đã nhìn thấy những gì in mảng in ấn - điều gì sẽ xảy ra nếu chúng tôi in địa chỉ của mảng hoặc địa chỉ của địa chỉ của mảng hoặc của những người? Hãy bỏ qua một trong những điều này. Okay. Điều này là tốt. Nó bây giờ chạy / a.out.. In ấn mảng, sau đó in địa chỉ của mảng, điều tương tự. Mảng chỉ không tồn tại. Nó biết khi bạn đang in mảng, bạn đang in ấn các biểu tượng mà đề cập đến 20 byte. In địa chỉ của mảng, mảng không tồn tại. Nó không có một địa chỉ, do đó, nó chỉ in địa chỉ của 20 byte. Ngay sau khi bạn biên dịch xuống, như trong buggy4 biên soạn của bạn / a.out. mảng là không tồn tại. Con trỏ tồn tại. Mảng không. Các khối của bộ nhớ đại diện cho mảng vẫn còn tồn tại, nhưng các mảng biến và các biến của loại đó không tồn tại. Đó là giống như những khác biệt chính giữa các mảng và con trỏ ngay sau khi bạn thực hiện cuộc gọi chức năng, không có sự khác biệt. Nhưng bên trong các chức năng mà các mảng chính nó được khai báo, sizeof công trình khác nhau kể từ khi bạn đang in kích thước của các khối thay vì kích thước của các loại, và bạn không thể thay đổi nó vì nó là một biểu tượng. In ấn điều và địa chỉ của điều in cùng một điều. Và đó là khá nhiều đó. [Sinh viên] bạn có thể nói rằng một lần nữa? Tôi có thể đã bị mất một cái gì đó. In ấn và mảng và địa chỉ của mảng in cùng một điều, trong khi đó nếu bạn in một con trỏ so với địa chỉ của con trỏ, một trong những điều in địa chỉ của những gì bạn đang trỏ đến, in địa chỉ của con trỏ trên stack. Bạn có thể thay đổi một con trỏ, bạn không thể thay đổi một biểu tượng mảng. Và sizeof con trỏ sẽ in kích thước của kiểu con trỏ. Vì vậy, int * p sizeof (p) sẽ in 4, nhưng int array [5] in sizeof (array) sẽ in 20. [Sinh viên] Vì vậy, int array [5] sẽ in 20? >>. Đó là lý do tại sao bên trong của buggy4 khi nó được sử dụng là sizeof (array) điều này đã làm i <20, không phải là những gì chúng ta muốn. Chúng tôi muốn i <5. >> [Sinh viên] Okay. [Bowden] Và sau đó ngay khi bạn bắt đầu đi qua trong các chức năng, nếu chúng ta đã làm int * p = array; bên trong chức năng này, chúng tôi về cơ bản có thể sử dụng p và mảng chính xác trong cùng một cách, ngoại trừ vấn đề sizeof và vấn đề thay đổi. Nhưng p [0] = 1; giống như nói array [0] = 1; Và ngay khi chúng tôi nói foo (array); foo (p); bên trong foo chức năng, đây là các cuộc gọi cùng một hai lần. Không có sự khác biệt giữa hai cuộc gọi. Mọi người đều tốt về điều đó? Okay. Chúng tôi có 10 phút. Chúng tôi sẽ cố gắng để có được thông qua chương trình này Typer Hacker, trang web này, mà ra đến năm ngoái hoặc một cái gì đó. Nó chỉ là nghĩa vụ phải được như bạn gõ ngẫu nhiên và nó in ra - Bất kỳ tập tin nó sẽ xảy ra đã tải là những gì nó trông giống như bạn đang gõ. Nó trông giống như một số loại hệ điều hành mã. Đó là những gì chúng tôi muốn thực hiện. Bạn cần phải có một file thực thi nhị phân có tên là hacker_typer mà mất trong một đối số duy nhất, các tập tin loại "tin tặc". Chạy thực thi nên xóa màn hình và sau đó in ra một trong những nhân vật từ các tập tin thông qua trong mỗi lần người dùng nhấn một phím. Vì vậy, bất cứ điều gì bạn bấm phím, nên vứt bỏ và thay vào đó in một ký tự từ tập tin đó là tham số. Tôi khá nhiều sẽ cho bạn biết những điều chúng ta sẽ cần phải biết. Nhưng chúng tôi muốn kiểm tra thư viện termios. Tôi đã không bao giờ được sử dụng thư viện này trong toàn bộ cuộc sống của tôi, vì vậy nó có mục đích rất tối thiểu. Nhưng điều này là có được các thư viện, chúng ta có thể sử dụng để ném đi những nhân vật bạn nhấn khi bạn đang gõ vào tiêu chuẩn. Vì vậy, hacker_typer.c, và chúng tôi sẽ muốn # include. Nhìn vào trang người đàn ông cho termios - tôi đoán thiết bị đầu cuối của hệ điều hành hoặc một cái gì đó - Tôi không biết làm thế nào để đọc nó. Nhìn này, nó nói để bao gồm 2 file này, vì vậy chúng tôi sẽ làm điều đó. Điều đầu tiên đầu tiên, chúng tôi muốn để có trong một đối số duy nhất, đó là các tập tin, chúng tôi sẽ mở ra. Vì vậy, tôi phải làm gì muốn làm gì? Làm thế nào để kiểm tra xem tôi có một đối số duy nhất? [Sinh viên] Nếu argc bằng nó. >> [Bowden] Yeah. Vì vậy, nếu (argc = 2) printf ("sử dụng:% s [file để mở]"). Vì vậy, bây giờ nếu tôi chạy mà không cần cung cấp một số thứ hai - oh, tôi cần dòng mới - bạn sẽ thấy nó nói sử dụng: / hacker_typer. và sau đó đối số thứ hai nên được các tập tin tôi muốn mở. Bây giờ tôi phải làm gì? Tôi muốn đọc từ tập tin này. Làm thế nào để đọc từ một tập tin? [Sinh viên] Bạn mở nó đầu tiên. >> Yeah. Vì vậy, fopen Fopen trông như thế nào? [Sinh viên] Filename. >> [Bowden] ảnh Tên ảnh là có được argv [1]. [Sinh viên] Và sau đó những gì bạn muốn làm gì với nó, vì vậy - >> [Bowden] Yeah. Vì vậy, nếu bạn không nhớ, bạn chỉ có thể làm người đàn ông fopen, nơi mà nó sẽ là một const char * path, nơi đường dẫn là tên tập tin, const char * mode. Nếu bạn xảy ra để không nhớ những gì chế độ, sau đó bạn có thể tìm cho chế độ. Bên trong trang người đàn ông, nhân vật dấu gạch chéo là những gì bạn có thể sử dụng để tìm kiếm cho những thứ. Vì vậy, tôi gõ / chế độ tìm kiếm cho chế độ. n và N là những gì bạn có thể sử dụng để chu kỳ thông qua các trận đấu tìm kiếm. Ở đây nó nói các điểm chế độ đối số cho một chuỗi bắt đầu với một trong các trình tự sau đây. Vì vậy, r, Mở tập tin văn bản để đọc. Đó là những gì chúng tôi muốn làm. Để đọc, và tôi muốn để lưu trữ. Điều là có được một tập tin *. Bây giờ tôi muốn làm những gì? Hãy cho tôi một giây. Okay. Bây giờ tôi muốn làm những gì? [Sinh viên] Kiểm tra nếu nó là NULL. >> [Bowden] Yeah. Bất cứ khi nào bạn mở một tập tin, hãy chắc chắn rằng bạn đang thành công có thể để mở nó. Bây giờ tôi muốn làm những thứ termios nơi tôi muốn lần đầu tiên đọc các thiết lập hiện tại của tôi và lưu những người thành một cái gì đó, sau đó tôi muốn thay đổi các thiết lập của tôi vứt bỏ bất kỳ nhân vật mà tôi gõ, và sau đó tôi muốn để cập nhật các thiết lập. Và sau đó ở phần cuối của chương trình, tôi muốn thay đổi trở lại các thiết lập ban đầu của tôi. Vì vậy, các cấu trúc sẽ có termios loại, và tôi sẽ muốn hai của những người. Người đầu tiên là sẽ là current_settings của tôi, và sau đó họ sẽ là hacker_settings của tôi. Đầu tiên, tôi sẽ muốn lưu các thiết lập hiện tại của tôi, sau đó tôi sẽ muốn cập nhật hacker_settings, và rồi con đường ở phần cuối của chương trình của tôi, tôi muốn trở lại các thiết lập hiện hành. Vì vậy, tiết kiệm thiết lập hiện tại, cách làm việc, termios người đàn ông chúng tôi. Chúng tôi thấy rằng chúng tôi có này tcsetattr int, int tcgetattr. Tôi vượt qua trong một struct termios con trỏ của nó. Cách này sẽ xem xét - Tôi đã đã quên những gì chức năng được gọi là. Sao chép và dán. Vì vậy, tcgetattr, sau đó tôi muốn vượt qua trong cấu trúc mà tôi đang lưu thông tin, đó là sẽ là current_settings, và tham số đầu tiên là mô tả tập tin cho điều tôi muốn lưu các thuộc tính của. Mô tả tập tin là giống như bất kỳ thời điểm nào bạn mở một tập tin, nó được một mô tả tập tin. Khi tôi fopen argv [1], nó được một mô tả tập tin mà bạn đang tham khảo bất cứ khi nào bạn muốn đọc hoặc viết thư cho nó. Đó không phải là mô tả tập tin tôi muốn sử dụng ở đây. Có ba mô tả tập tin mà bạn có theo mặc định, là tiêu chuẩn, tiêu chuẩn ra, và sai số chuẩn. Theo mặc định, tôi nghĩ rằng nó là tiêu chuẩn trong là 0, tiêu chuẩn ra 1, và lỗi tiêu chuẩn là 2. Vì vậy, tôi phải làm gì muốn thay đổi các thiết lập của? Tôi muốn thay đổi các thiết lập của bất cứ khi nào tôi nhấn một nhân vật, Tôi muốn nó ném rằng nhân vật thay vì in ấn nó vào màn hình. Dòng tiêu chuẩn, ra tiêu chuẩn, hoặc lỗi tiêu chuẩn - đáp ứng điều khi tôi gõ vào bàn phím? >> [Sinh viên] Standard. >> Vâng. Vì vậy, tôi có thể làm 0 hoặc tôi có thể làm stdin. Tôi nhận được current_settings của tiêu chuẩn. Bây giờ tôi muốn để cập nhật các thiết lập, do đó, đầu tiên tôi sẽ sao chép vào hacker_settings những gì current_settings của tôi. Và làm thế nào để cấu trúc làm việc là nó sẽ chỉ sao chép. Điều này bản sao tất cả các lĩnh vực, như bạn mong chờ. Bây giờ tôi muốn để cập nhật một số lĩnh vực. Nhìn vào termios, bạn sẽ phải đọc qua rất nhiều này chỉ để xem những gì bạn sẽ muốn tìm, nhưng những lá cờ bạn sẽ muốn tìm echo, như vậy ECHO Echo ký tự đầu vào. Trước tiên tôi muốn thiết lập - I've đã quên những gì các lĩnh vực. Đây là những gì cấu trúc trông giống như. Vì vậy, các chế độ nhập, tôi nghĩ rằng chúng tôi muốn thay đổi. Chúng tôi sẽ xem xét các giải pháp để đảm bảo rằng đó là những gì chúng ta muốn thay đổi. Chúng tôi muốn thay đổi lflag để ngăn chặn cần phải xem xét thông qua tất cả những điều này. Chúng tôi muốn thay đổi chế độ địa phương. Bạn sẽ phải đọc thông qua toàn bộ điều này để hiểu, nơi tất cả mọi thứ thuộc về mà chúng ta muốn thay đổi. Tuy nhiên, bên trong các chế độ địa phương, nơi chúng tôi sẽ muốn thay đổi điều đó. Vì vậy, hacker_settings.cc_lmode là những gì nó được gọi là. c_lflag. Đây là nơi mà chúng tôi nhận được vào các nhà khai thác Bitwise. Chúng tôi đang loại ra khỏi thời gian, nhưng chúng tôi sẽ đi qua nó thực sự nhanh chóng. Đây là nơi mà chúng tôi nhận được vào nhà khai thác Bitwise, mà tôi nghĩ rằng tôi đã nói một thời gian dài trước đây rằng bất cứ khi nào bạn bắt đầu giao dịch với cờ, bạn sẽ được sử dụng Bitwise nhà điều hành rất nhiều. Mỗi bit trong lá cờ tương ứng với một số loại hành vi. Vì vậy, ở đây, lá cờ này có một loạt các thứ khác nhau, nơi mà tất cả chúng có nghĩa là một cái gì đó khác nhau. Nhưng những gì tôi muốn làm là chỉ cần tắt các bit tương ứng với ECHO. Vì vậy, để biến mà off I & = ¬ ECHO. Thực ra, tôi nghĩ rằng đó là như tECHO hoặc một cái gì đó. Tôi chỉ cần đi để kiểm tra một lần nữa. Tôi có thể termios. Nó chỉ ECHO. ECHO là có được một chút. ¬ ECHO sẽ có nghĩa là tất cả các bit được thiết lập để 1, có nghĩa là tất cả các lá cờ được thiết lập để thực trừ cho bit ECHO. Bằng cách kết thúc cờ địa phương của tôi với điều này, nó có nghĩa là tất cả các lá cờ hiện đang được thiết lập để thực vẫn sẽ được thiết lập là true. Nếu cờ ECHO của tôi được thiết lập là true, sau đó điều này cần thiết phải được thiết lập để sai trên lá cờ ECHO. Vì vậy, dòng mã này chỉ cần tắt cờ ECHO. Các dòng khác của mã, tôi sẽ chỉ sao chép chúng trong sự quan tâm của thời gian và sau đó giải thích chúng. Trong giải pháp này, ông nói 0. Đây có thể là tốt hơn một cách rõ ràng nói stdin. Chú ý rằng tôi cũng làm ECHO | icanon đây. Icanon đề cập đến một cái gì đó riêng biệt, mà có nghĩa là chế độ kinh điển. Chế độ phương tiện kinh điển là thông thường khi bạn đang gõ dòng lệnh, tiêu chuẩn trong không xử lý bất cứ điều gì cho đến khi bạn nhấn xuống dòng. Vì vậy, khi bạn GetString, bạn gõ một bó của những điều, sau đó bạn nhấn xuống dòng. Đó là khi nó được gửi đến tiêu chuẩn. Đó là mặc định. Khi tôi tắt chế độ kinh điển, bây giờ tất cả các nhân vật duy nhất bạn nhấn là những gì được chế biến, mà thường là loại xấu bởi vì nó làm chậm để xử lý những việc này, đó là lý do tại sao nó rất tốt để đệm vào dây chuyền toàn bộ. Nhưng tôi muốn mỗi nhân vật để được xử lý vì tôi không muốn nó chờ đợi cho tôi để nhấn xuống dòng trước khi nó xử lý tất cả các ký tự đã gõ. Tắt chế độ kinh điển. Công cụ này chỉ có nghĩa là khi nó thực sự xử lý nhân vật. Điều này có nghĩa là xử lý chúng ngay lập tức, ngay sau khi tôi gõ chúng, xử lý chúng. Và đây là chức năng được cập nhật cài đặt của tôi cho các tiêu chuẩn, và các phương tiện TCSA làm điều đó ngay bây giờ. Các tùy chọn khác được chờ đợi cho đến khi tất cả mọi thứ hiện tại trên suối được xử lý. Điều đó không thực sự quan trọng. Chỉ cần thay đổi các thiết lập của tôi ngay bây giờ để được bất cứ điều gì hiện đang trong hacker_typer_settings. Tôi đoán tôi gọi nó là hacker_settings, vì vậy hãy thay đổi điều đó. Thay đổi tất cả mọi thứ để hacker_settings. Bây giờ ở phần cuối của chương trình của chúng tôi, chúng tôi sẽ muốn trở lại những gì hiện đang trong normal_settings, đó là sẽ chỉ cần nhìn như & normal_settings. Thấy rằng chúng tôi không thay đổi bất kỳ normal_settings của tôi kể từ khi ban đầu nhận được nó. Sau đó, để chỉ cần thay đổi chúng trở lại, tôi vượt qua chúng trở lại ở cuối. Đây là bản cập nhật. Okay. Bây giờ, bên đây tôi sẽ chỉ giải thích các mã trong sự quan tâm của thời gian. Nó không phải là nhiều mã. Chúng tôi nhìn thấy chúng tôi đọc một ký tự từ tập tin. Chúng tôi gọi nó là f. Bây giờ bạn có thể người đàn ông fgetc, nhưng như thế nào fgetc là sẽ làm việc chỉ là nó sẽ trở lại nhân vật mà bạn chỉ cần đọc hoặc EOF, tương ứng với kết thúc của tập tin hoặc một số lỗi xảy ra. Chúng tôi vòng lặp, tiếp tục đọc một ký tự từ tập tin duy nhất, cho đến khi chúng tôi đã chạy ra khỏi các ký tự để đọc. Và trong khi chúng tôi đang làm điều đó, chúng tôi chờ đợi vào một nhân vật duy nhất từ ​​tiêu chuẩn. Mỗi khi bạn gõ một cái gì đó tại dòng lệnh, đọc một ký tự từ tiêu chuẩn. Sau đó, putchar là chỉ cần đi để đưa các char chúng ta đọc từ các tập tin để ra tiêu chuẩn. Bạn có thể người đàn ông putchar, nhưng nó chỉ cần đặt tiêu chuẩn, nó in rằng nhân vật. Bạn cũng có thể chỉ cần làm printf ("% c", c); cùng một ý tưởng. Điều đó sẽ làm phần lớn công việc của chúng tôi. Điều cuối cùng chúng ta sẽ muốn làm là chỉ cần fclose tập tin của chúng tôi. Nếu bạn không fclose, đó là một rò rỉ bộ nhớ. Chúng tôi muốn fclose tập tin chúng tôi ban đầu được mở, và tôi nghĩ rằng đó là nó. Nếu chúng ta thực hiện điều đó, tôi đã có vấn đề. Hãy xem. Nó đã khiếu nại? Dự kiến ​​'int' nhưng đối số là loại 'struct _IO_FILE *'. Chúng ta sẽ thấy rằng các công trình. Chỉ được phép trong C99. Augh. Được rồi, hacker_typer. Bây giờ chúng tôi nhận được mô tả hữu ích hơn. Vì vậy, sử dụng định danh không khai báo 'normal_settings'. Tôi đã không gọi nó normal_settings. Tôi gọi nó là current_settings. Vì vậy, hãy thay đổi tất cả những điều đó. Bây giờ đi qua đối số. Tôi sẽ làm cho này 0 cho bây giờ. Okay. / Hacker_typer. Cp.c. Tôi cũng không xóa màn hình lúc đầu. Nhưng bạn có thể nhìn lại các thiết lập vấn đề cuối cùng để xem làm thế nào bạn xóa màn hình. Nó chỉ cần in một số ký tự trong khi điều này đang làm những gì tôi muốn làm. Okay. Và suy nghĩ về lý do tại sao điều này cần thiết để là 0 thay vì stdin, cần được # xác định 0, này là phàn nàn rằng - Trước khi tôi nói rằng có mô tả tập tin, nhưng sau đó bạn cũng có FILE *, một mô tả tập tin chỉ là một số nguyên duy nhất, trong khi một * FILE có một bó toàn bộ công cụ liên kết với nó. Lý do chúng tôi cần phải nói 0 thay vì stdin được stdin đó là một * FILE mà điểm đến điều đó là tham khảo mô tả tập tin 0. Vì vậy, ngay cả ở đây khi tôi làm fopen (argv [1], tôi nhận được một * FILE. Tuy nhiên, ở một nơi nào đó trong đó * FILE là một điều tương ứng với mô tả tập tin cho tập tin đó. Nếu bạn nhìn vào trang người đàn ông mở, vì vậy tôi nghĩ rằng bạn sẽ phải làm mở man 3 - nope - mở man 2 -. Nếu bạn nhìn vào trang để mở, mở là như fopen cấp dưới, và nó trở về mô tả tập tin thực tế. fopen hiện một loạt các công cụ trên đầu trang của mở, thay vì trả lại chỉ là mô tả tập tin trả về toàn bộ tập tin con trỏ trong số đó là ít mô tả tập tin của chúng tôi. Vì vậy, tiêu chuẩn đề cập đến điều * FILE, trong khi đó 0 là để chỉ các tiêu chuẩn mô tả tập tin của riêng mình. Câu hỏi? [Cười] Blew thông qua đó. Được rồi. Chúng tôi đang thực hiện. [Cười] [CS50.TV]