[Powered by Google Translate] [Phần 4 - thoải mái hơn] [Rob Bowden - Đại học Harvard] [Đây là CS50. - CS50.TV] Chúng tôi có một bài kiểm tra vào ngày mai, trong trường hợp bạn không biết điều đó. Đó là cơ bản tất cả mọi thứ bạn có thể đã thấy trong lớp học hoặc đã thấy trong lớp. Điều đó bao gồm con trỏ, mặc dù họ là một chủ đề rất gần đây. Bạn ít nhất phải hiểu được mức độ cao của họ. Bất cứ điều gì đã được đi qua trong lớp bạn nên hiểu cho bài kiểm tra. Vì vậy, nếu bạn có câu hỏi về họ, bạn có thể yêu cầu chúng ngay bây giờ. Nhưng điều này là có được một phiên rất sinh viên dẫn nơi bạn đặt câu hỏi, do đó, hy vọng mọi người có thắc mắc. Có ai có câu hỏi? Vâng. >> [Sinh viên] Bạn có thể đi trên con trỏ một lần nữa? Tôi sẽ đi qua con trỏ. Tất cả các biến của bạn nhất thiết phải sống trong bộ nhớ, nhưng thông thường bạn không lo lắng về điều đó và bạn chỉ cần nói x + 2 và y + 3 và trình biên dịch sẽ tìm ra nơi mà những điều đang sống cho bạn. Một khi bạn đang làm việc với con trỏ, bây giờ bạn một cách rõ ràng bằng cách sử dụng những địa chỉ bộ nhớ. Vì vậy, một biến duy nhất sẽ chỉ bao giờ sống tại một địa chỉ duy nhất tại bất kỳ thời gian nhất định. Nếu chúng ta muốn khai báo một con trỏ, các loại là những gì sẽ trông giống như? Tôi muốn khai báo một con trỏ p. Các loại trông như thế nào? [Sinh viên] int * p. >> Yeah. Vì vậy, int * p. Và làm cách nào để làm cho nó trỏ đến x? >> [Sinh viên] ký hiệu. Bowden] Vì vậy, ký hiệu là nghĩa đen được gọi là địa chỉ của nhà điều hành. Vì vậy, khi tôi nói & x nó nhận được địa chỉ bộ nhớ của biến x. Vì vậy, bây giờ tôi có con trỏ p, và bất cứ nơi nào trong mã của tôi, tôi có thể sử dụng * p hoặc tôi có thể sử dụng x và nó sẽ được chính xác cùng một điều. (* P). Này đang làm gì? Ngôi sao có nghĩa là gì? [Sinh viên] Nó có nghĩa là một giá trị tại thời điểm đó. >> Yeah. Vì vậy, nếu chúng ta nhìn vào nó, nó có thể rất hữu ích để vẽ sơ đồ nơi này là một hộp nhỏ của bộ nhớ cho x, xảy ra để có giá trị 4, sau đó chúng tôi có một hộp nhỏ của bộ nhớ cho p, và như vậy p điểm x, vì vậy chúng tôi rút ra một mũi tên từ p đến x. Vì vậy, khi chúng ta nói * p chúng tôi đang nói hộp là p. Star là theo các mũi tên và sau đó làm bất cứ điều gì bạn muốn với hộp đó. Vì vậy, tôi có thể nói * p = 7, mà sẽ đi đến hộp đó là x và sự thay đổi đó đến 7. Hoặc tôi có thể nói int z = * p * 2; Đó là khó hiểu bởi vì ngôi sao, ngôi sao. Ngôi sao một dereferencing p, ngôi sao khác được nhân với 2. Chú ý tôi có thể có chỉ là tốt thay thế p * x. Bạn có thể sử dụng chúng trong cùng một cách. Và rồi sau đó tôi có thể có điểm p là một điều hoàn toàn mới. Tôi chỉ có thể nói p = &z; Vì vậy, bây giờ p không có điểm lâu hơn để x, nó trỏ đến z. Và bất cứ lúc nào tôi làm * p nó giống như làm z. Vì vậy, điều hữu ích về việc này là khi chúng tôi bắt đầu nhận được vào các chức năng. Đó là loại useless để khai báo một con trỏ trỏ đến một cái gì đó và sau đó bạn chỉ cần dereferencing nó khi bạn có thể sử dụng các biến ban đầu để bắt đầu với. Nhưng khi bạn nhận được vào các chức năng - vì vậy hãy nói rằng chúng tôi có một số chức năng, int foo, mà phải mất một con trỏ và chỉ * p = 6; Giống như chúng ta đã thấy trước đây với vùng trao đổi, bạn không có thể làm một trao đổi hiệu quả và chức năng riêng biệt bằng cách chỉ cần đi qua các số nguyên bởi vì tất cả mọi thứ trong C luôn luôn đi qua giá trị. Ngay cả khi bạn đang đi qua con trỏ bạn đang đi qua bởi giá trị. Nó chỉ như vậy sẽ xảy ra rằng những giá trị địa chỉ bộ nhớ. Vì vậy, khi tôi nói foo (p); tôi đang đi qua con trỏ vào foo chức năng và sau đó foo * p = 6; Vì vậy, bên trong là chức năng, * p vẫn còn tương đương với x, nhưng tôi không thể sử dụng bên trong của chức năng đó bởi vì nó không scoped trong phạm vi chức năng đó. Vì vậy, * p = 6 là cách duy nhất tôi có thể truy cập vào một biến địa phương từ chức năng khác. Hoặc, tốt, con trỏ là cách duy nhất tôi có thể truy cập vào một biến địa phương từ chức năng khác. [Sinh viên] Chúng ta hãy nói rằng bạn muốn quay trở lại một con trỏ. Làm thế nào chính xác để bạn làm điều đó? [Bowden] Quay trở lại một con trỏ như trong một cái gì đó như int y = 3, trở lại y? >> [Sinh viên] Yeah. [Bowden] Okay. Bạn không bao giờ nên làm điều này. Điều này là xấu. Tôi nghĩ rằng tôi thấy trong các slide bài giảng, bạn bắt đầu nhìn thấy sơ đồ này toàn bộ bộ nhớ ở đây bạn đã có địa chỉ bộ nhớ 0 và xuống ở đây bạn có địa chỉ bộ nhớ 4 đồng biểu diễn hoặc 2 cho 32. Vì vậy, sau đó bạn đã có một số công cụ và một số công cụ và sau đó bạn phải ngăn xếp của bạn và bạn đã có đống bạn, mà bạn chỉ mới bắt đầu học tập về, lớn lên. [Sinh viên] Không phải là đống trên ngăn xếp? Yeah. Heap là trên đầu trang, không phải là nó? >> [Sinh viên] À, anh ấy đặt 0 trên đầu trang. [Sinh viên] Oh, ông đặt 0 trên đầu trang. >> [Sinh viên] Oh, okay. Disclaimer: Bất cứ nơi nào với CS50 bạn đang đi để xem nó theo cách này. >> [Sinh viên] Okay. Nó chỉ là khi lần đầu bạn nhìn thấy ngăn xếp, giống như khi bạn nghĩ về một chồng bạn nghĩ xếp những thứ trên đầu trang của một người khác. Vì vậy, chúng ta có xu hướng để lật xung quanh để ngăn xếp được lớn lên như một chồng bình thường thay vì ngăn xếp trễ xuống. >> [Sinh viên] Đừng đống kỹ thuật phát triển quá, mặc dù? Nó phụ thuộc vào những gì bạn có nghĩa là lớn lên. Stack và heap luôn luôn phát triển theo hướng ngược nhau. Một stack là luôn luôn lớn lên trong ý nghĩa rằng nó lớn lên đối với các địa chỉ bộ nhớ cao hơn, và heap được phát triển trong đó nó đang phát triển đối với các địa chỉ bộ nhớ thấp hơn. Vì vậy, trên cùng là 0 và phía dưới là địa chỉ bộ nhớ cao. Họ cả hai đang phát triển, trong việc phản đối hướng. [Sinh viên] Tôi chỉ có nghĩa là bởi vì bạn nói bạn đặt ngăn xếp ở phía dưới vì nó có vẻ trực quan hơn bởi vì các ngăn xếp để bắt đầu ở phía trên cùng của một đống, heap là trên bản thân nó quá, vì vậy Đó - >> Vâng. Bạn cũng có suy nghĩ của đống như lớn lên và lớn hơn, nhưng ngăn xếp nhiều hơn như vậy. Vì vậy, ngăn xếp là một trong những loại chúng ta muốn hiển thị lớn lên. Tuy nhiên, ở khắp mọi nơi bạn nhìn nếu không sẽ hiển thị địa chỉ 0 ở đầu và địa chỉ bộ nhớ cao nhất ở phía dưới, do đó, điều này là quan điểm thông thường của bạn bộ nhớ. Bạn có một câu hỏi? [Sinh viên] Bạn có thể cho chúng tôi biết thêm về heap? Yeah. Tôi sẽ nhận được trong một giây. Đầu tiên, sẽ trở lại lý do tại sao trở về & y là một điều xấu, trên stack bạn có một loạt các khung stack đại diện cho tất cả các chức năng đã được gọi là. Vì vậy, bỏ qua những điều trước đây, đỉnh của ngăn xếp của bạn luôn luôn có được các chức năng chính vì đó là chức năng đầu tiên được gọi là. Và sau đó khi bạn gọi một chức năng, ngăn xếp sẽ phát triển. Vì vậy, nếu tôi gọi một số chức năng, foo, và nó được khung stack riêng của mình, nó có thể gọi một số chức năng, quầy bar, nó được khung stack riêng của mình. Và quầy bar có thể được đệ quy và nó có thể gọi chính nó, và do đó cuộc gọi thứ hai đến một quán bar để có được khung stack riêng của mình. Và do đó, những gì diễn ra trong các khung stack là tất cả các biến địa phương và tất cả các đối số chức năng mà - Bất kỳ điều tại địa phương scoped để chức năng này trong các khung stack. Vì vậy, điều đó có nghĩa là khi tôi nói một cái gì đó giống như thanh là một chức năng, Tôi chỉ cần đi để khai báo một số nguyên và sau đó trở về một con trỏ đến số nguyên. Vì vậy, nơi y sống? [Sinh viên] y sống trong quán bar. >> [Bowden] Yeah. Nơi nào đó trong hình vuông ít bộ nhớ này là một hình vuông Littler có y trong đó. Khi tôi trở về và y, tôi trở lại một con trỏ đến khối này ít bộ nhớ. Nhưng sau đó khi trở về chức năng, khung stack của nó được lấy ra khỏi stack. Và đó là lý do tại sao nó được gọi là chồng. Nó giống như cấu trúc dữ liệu ngăn xếp, nếu bạn biết đó là gì. Hoặc thậm chí như một chồng các khay luôn luôn là ví dụ, chính là sẽ đi về phía dưới, sau đó chức năng đầu tiên bạn gọi là sẽ đi trên đó, và bạn không thể lấy lại chính cho đến khi bạn trở về từ tất cả các chức năng đã được gọi là đã được đặt trên đầu trang của nó. [Sinh viên] Vì vậy, nếu bạn đã làm trở lại y &, giá trị đó có thể thay đổi mà không cần thông báo. Có, it's - >> [sinh viên] Nó có thể được ghi đè. >> Yeah. Nó hoàn toàn - Nếu bạn cố gắng và - Điều này cũng sẽ là một thanh int * bởi vì nó trở về một con trỏ, do đó, kiểu trả về của nó là int *. Nếu bạn cố gắng sử dụng giá trị trả lại chức năng này, đó là xác định hành vi vì rằng con trỏ chỉ vào bộ nhớ xấu. >> [Sinh viên] Okay. Vì vậy, điều gì sẽ xảy ra nếu, ví dụ, bạn tuyên bố int * y = malloc (sizeof (int))? Đó là tốt hơn. Vâng. [Sinh viên] Chúng tôi đã nói chuyện về làm thế nào khi chúng ta kéo mọi thứ vào thùng rác của chúng tôi họ đang không thực sự bị xóa, chúng tôi chỉ mất con trỏ của họ. Vì vậy, trong trường hợp này chúng ta thực sự xóa các giá trị hoặc là nó vẫn còn đó trong bộ nhớ? Đối với hầu hết các phần, nó sẽ vẫn có mặt ở đó. Nhưng hãy nói chúng tôi xảy ra để gọi một số chức năng khác, baz. Baz là sẽ nhận được khung stack riêng của mình trên đây. Nó sẽ được ghi đè lên tất cả các công cụ này, và sau đó nếu sau đó bạn cố gắng và sử dụng con trỏ mà bạn có trước khi, nó sẽ không phải là cùng một giá trị. Nó sẽ thay đổi chỉ bởi vì bạn được gọi là baz chức năng. [Sinh viên] Nhưng nếu chúng ta không, chúng tôi vẫn có được 3? [Bowden] Trong tất cả các khả năng, bạn sẽ. Nhưng bạn không thể dựa vào đó. C chỉ nói hành vi không xác định. [Sinh viên] Oh, nó. Okay. Vì vậy, khi bạn muốn trả về một con trỏ, đây là nơi malloc đến trong sử dụng. Tôi đang viết thực sự chỉ cần trả lại malloc (3 * sizeof (int)). Chúng tôi sẽ đi qua malloc hơn trong một giây, nhưng ý tưởng của malloc là tất cả các biến địa phương của bạn luôn luôn đi trên stack. Bất cứ điều gì malloced đi trên heap, và nó mãi mãi và sẽ luôn luôn được trên heap cho đến khi bạn giải phóng nó một cách rõ ràng. Vì vậy, điều này có nghĩa là khi bạn malloc một cái gì đó, nó sẽ tồn tại sau khi trở về chức năng. [Sinh viên] nó sẽ tồn tại sau khi chương trình ngừng chạy? >> Số Được rồi, do đó, nó sẽ có mặt ở đó cho đến khi chương trình là tất cả các cách thực hiện hoạt động. >>. Chúng ta có thể đi qua các chi tiết về những gì sẽ xảy ra khi chương trình dừng chạy. Bạn có thể cần nhắc nhở tôi, nhưng đó là một điều riêng biệt hoàn toàn. [Sinh viên] Vì vậy, malloc tạo ra một con trỏ? >> Yeah. Malloc - >> [sinh viên] Tôi nghĩ rằng malloc chỉ định một khối bộ nhớ rằng một con trỏ có thể sử dụng. [Bowden] Tôi muốn rằng sơ đồ một lần nữa. >> [Sinh viên] Vì vậy, chức năng này hoạt động, mặc dù không? [Sinh viên] Yeah, malloc chỉ định một khối bộ nhớ mà bạn có thể sử dụng, và sau đó nó sẽ trả về địa chỉ của khối đầu tiên của bộ nhớ. [Bowden] Yeah. Vì vậy, khi bạn malloc, bạn lấy một số khối của bộ nhớ hiện đang trong heap. Nếu heap là quá nhỏ, sau đó các đống chỉ là đi để phát triển, và nó phát triển theo hướng này. Vì vậy, hãy nói rằng heap là quá nhỏ. Sau đó, nó về để phát triển một chút và trả về một con trỏ trỏ tới khối này chỉ tăng trưởng. Khi bạn công cụ miễn phí, bạn đang làm cho phòng trong đống này, như vậy thì sau đó gọi đến malloc có thể sử dụng lại bộ nhớ mà trước đó bạn đã giải phóng. Điều quan trọng về malloc và free là nó cho phép bạn kiểm soát hoàn toàn trong suốt cuộc đời của các khối bộ nhớ. Biến toàn cầu luôn luôn sống. Các biến địa phương sống trong phạm vi của họ. Ngay sau khi bạn đi qua một cú đúp xoăn, các biến địa phương đã chết. Malloced bộ nhớ là còn sống khi bạn muốn nó được sống và sau đó được phát hành khi bạn nói với nó sẽ được phát hành. Đó là thực sự là 3 loại duy nhất của bộ nhớ, thực sự. Có tự động quản lý bộ nhớ, đó là ngăn xếp. Sự việc xảy ra cho bạn tự động. Khi bạn nói x int, bộ nhớ được phân bổ cho x int. Khi x đi ra khỏi phạm vi, bộ nhớ được khai hoang cho x. Sau đó có quản lý bộ nhớ động, đó là những gì malloc, đó là khi bạn có kiểm soát. Bạn quyết định động khi bộ nhớ nên và không nên được phân bổ. Và sau đó là tĩnh, mà chỉ có nghĩa là nó tồn tại mãi mãi, đó là biến toàn cầu. Họ chỉ cần luôn luôn trong bộ nhớ. Câu hỏi? [Sinh viên] Bạn có thể xác định một khối chỉ bằng cách sử dụng dấu ngoặc nhọn nhưng không có để có một nếu tuyên bố hoặc một tuyên bố bất cứ điều gì trong khi hoặc như thế? Bạn có thể xác định một khối như trong một chức năng, nhưng mà có dấu ngoặc nhọn quá. [Sinh viên] Vì vậy, bạn có thể không chỉ có giống như một cặp ngẫu nhiên của các dấu ngoặc nhọn trong mã của bạn có các biến địa phương? >> Có, bạn có thể. Bên trong của thanh int chúng ta có thể có {int y = 3;}. Đó là nghĩa vụ ngay tại đây. Nhưng điều đó hoàn toàn xác định phạm vi của int y. Sau khi là nẹp xoăn thứ hai, y không thể được sử dụng nữa. Bạn hầu như không bao giờ làm điều đó, mặc dù. Trở lại với những gì sẽ xảy ra khi một chương trình kết thúc, có loại một quan niệm sai lầm / nửa lời nói dối mà chúng tôi cung cấp để làm cho mọi việc dễ dàng hơn. Chúng tôi nói với bạn rằng khi bạn cấp phát bộ nhớ bạn đang phân bổ một số đoạn của bộ nhớ RAM cho biến đó. Nhưng bạn không thực sự tiếp xúc trực tiếp bộ nhớ RAM bao giờ hết trong chương trình của bạn. Nếu bạn nghĩ về nó, làm thế nào tôi vẽ Và trên thực tế, nếu bạn đi qua trong GDB, bạn sẽ thấy điều tương tự. Bất kể bao nhiêu lần bạn chạy chương trình của bạn hoặc những gì chương trình bạn đang chạy, stack là luôn luôn đi để bắt đầu - bạn sẽ luôn luôn thấy các biến xung quanh một cái gì đó oxbffff địa chỉ. Nó thường là một nơi nào đó trong khu vực đó. Nhưng làm thế nào có thể 2 chương trình có thể có con trỏ cùng một bộ nhớ? [Sinh viên] Có một số chỉ định bất kỳ nơi oxbfff là vụ phải được trên RAM mà thực sự có thể được ở những nơi khác nhau tùy thuộc vào khi chức năng này được gọi là. Yeah. Thuật ngữ này là bộ nhớ ảo. Ý tưởng là tất cả các quá trình duy nhất, tất cả các chương trình đang chạy trên máy tính của bạn có riêng của mình - chúng ta hãy giả sử 32 bit hoàn toàn độc lập không gian địa chỉ. Đây là không gian địa chỉ. Nó có riêng của mình hoàn toàn độc lập 4 gigabyte sử dụng. Vì vậy, nếu bạn chạy đồng thời 2 chương trình, chương trình này thấy 4 gigabyte với chính nó, chương trình này thấy 4 gigabyte với chính nó, và nó không thể cho chương trình này để dereference một con trỏ và kết thúc với bộ nhớ từ chương trình này. Và những gì bộ nhớ ảo là một ánh xạ từ một quá trình không gian địa chỉ những điều thực tế về bộ nhớ RAM. Vì vậy, nó là hệ điều hành của bạn để biết rằng, hey, khi anh chàng này dereferences con trỏ oxbfff, mà thực sự có nghĩa là rằng ông muốn RAM byte 1000, trong khi nếu chương trình này dereferences oxbfff, ông thực sự muốn RAM byte 10.000. Chúng có thể được tùy tiện cách xa nhau. Điều này thậm chí của sự vật trong một không gian địa chỉ duy nhất quá trình. Vì vậy, giống như nó nhìn thấy tất cả 4 GB với chính nó, nhưng hãy nói - [Sinh viên] tất cả các quá trình duy nhất - Hãy nói rằng bạn có một máy tính với chỉ 4 GB RAM. Tất cả các quá trình duy nhất nhìn thấy cả 4 gigabyte? >>. Nhưng 4 gigabyte nó thấy là nói dối. Nó chỉ là nó nghĩ rằng nó có tất cả bộ nhớ này bởi vì nó không biết bất kỳ quá trình khác đang tồn tại. Nó sẽ chỉ sử dụng bộ nhớ càng nhiều như nó thực sự cần. Các hệ điều hành không phải là đi để cung cấp cho bộ nhớ RAM để quá trình này nếu nó không được sử dụng bất kỳ bộ nhớ trong toàn bộ khu vực này. Nó sẽ không để cho nó bộ nhớ cho khu vực đó. Tuy nhiên, ý tưởng này là - Tôi đang cố gắng để suy nghĩ của tôi không thể nghĩ về một tương tự. Sự so sánh khó khăn. Một trong các vấn đề của bộ nhớ ảo hoặc một trong những điều nó giải quyết là quá trình phải được hoàn toàn không biết gì về nhau. Và như vậy bạn có thể viết bất kỳ chương trình mà chỉ cần dereferences bất kỳ con trỏ, như chỉ cần viết một chương trình nói rằng * (ox1234), và đó là địa chỉ bộ nhớ dereferencing 1234. Nhưng đó là hệ điều hành để sau đó dịch 1234 phương tiện. Vì vậy, nếu 1234 sẽ xảy ra là một địa chỉ bộ nhớ hợp lệ cho quá trình này, như đó là trên stack hoặc một cái gì đó, thì điều này sẽ trả về giá trị của địa chỉ bộ nhớ như xa như là quá trình biết. Nhưng nếu 1234 là không phải là một địa chỉ hợp lệ, giống như nó sẽ xảy ra với đất trong một số đôi chút về bộ nhớ ở đây là ngoài ngăn xếp và vượt ra ngoài đống và bạn đã không thực sự được sử dụng, sau đó đó là khi bạn có được những thứ như segfaults bởi vì bạn đang chạm vào bộ nhớ mà bạn không nên chạm vào. Điều này cũng đúng - Một hệ thống 32-bit, 32 bit có nghĩa là bạn có 32 bit để xác định một địa chỉ bộ nhớ. Đó là lý do tại sao con trỏ là 8 byte bởi vì 32 bit là 8 byte hoặc 4 byte. Con trỏ là 4 byte. Vì vậy, khi bạn nhìn thấy một con trỏ như oxbfffff, đó là - Trong bất kỳ chương trình nào, bạn chỉ có thể xây dựng bất cứ con trỏ tùy ý, bất cứ nơi nào từ ox0 bò 8 f's - ffffffff. [Sinh viên] Không phải bạn nói rằng họ đang 4 byte? >> Yeah. [Sinh viên] Sau đó, mỗi byte sẽ có - >> [Bowden Hexadecimal. Hexadecimal - 5, 6, 7, 8. Vì vậy, con trỏ, bạn sẽ luôn luôn nhìn thấy trong hệ thập lục phân. Đó chỉ là cách chúng tôi phân loại con trỏ. Mỗi 2 chữ số thập lục phân là 1 byte. Vì vậy, sẽ là 8 chữ số thập lục phân cho 4 byte. Vì vậy, mỗi con trỏ duy nhất trên một hệ thống 32-bit sẽ là 4 byte, có nghĩa là trong quá trình của bạn, bạn có thể xây dựng bất kỳ 4 byte tùy ý và làm cho một con trỏ ra khỏi nó, có nghĩa là như xa như nó biết, nó có thể giải quyết toàn bộ khoảng 2 đến 32 byte của bộ nhớ. Mặc dù nó không thực sự có quyền truy cập vào đó, ngay cả khi máy tính của bạn chỉ có 512 MB, nó nghĩ rằng nó đã có nhiều bộ nhớ. Và hệ điều hành là đủ thông minh rằng nó sẽ chỉ phân bổ những gì bạn thực sự cần. Nó không chỉ cần đi, oh, một quá trình mới: 4 đồng biểu diễn. Yeah. >> [Sinh viên] ox có nghĩa là gì? Tại sao bạn viết nó? Nó chỉ là biểu tượng cho hệ thập lục phân. Khi bạn nhìn thấy một sự khởi đầu số với con bò, những điều kế tiếp là thập lục phân. [Sinh viên] Bạn đã được giải thích về những gì sẽ xảy ra khi một chương trình kết thúc. >>. Điều gì sẽ xảy ra khi một chương trình kết thúc là hệ điều hành chỉ cần xóa các ánh xạ mà nó có những địa chỉ này, và đó là nó. Các hệ điều hành có thể chỉ cần cung cấp cho rằng bộ nhớ một chương trình khác để sử dụng. [Sinh viên] Okay. Vì vậy, khi bạn phân bổ một cái gì đó trên heap hoặc stack biến toàn cầu hoặc bất cứ điều gì, tất cả họ đều chỉ biến mất ngay sau khi chương trình kết thúc vì hệ điều hành miễn phí cung cấp cho rằng bộ nhớ cho bất kỳ quá trình khác. [Sinh viên] Mặc dù có lẽ vẫn còn giá trị được viết bằng? >> Yeah. Các giá trị có khả năng vẫn còn đó. Nó chỉ là nó sẽ được khó khăn để có được ở họ. Đó là khó khăn hơn nhiều để có được ở họ hơn là để có được một tập tin đã xóa bởi vì các loại tập tin đã xóa ngồi ở đó trong một thời gian dài và ổ đĩa cứng lớn hơn rất nhiều. Vì vậy, nó sẽ ghi đè lên các bộ phận khác nhau của bộ nhớ trước khi nó xảy ra để ghi đè lên các đoạn bộ nhớ rằng tập tin đó được sử dụng để được ở. Tuy nhiên, bộ nhớ chính, bộ nhớ RAM, bạn chu kỳ thông qua nhanh hơn rất nhiều, do đó, nó sẽ rất nhanh chóng được ghi đè. Câu hỏi này hay bất cứ điều gì khác? [Sinh viên] Tôi có câu hỏi về một chủ đề khác nhau. >> Okay. Có ai có câu hỏi về điều này? Okay. Khác nhau chủ đề. >> [Sinh viên] Okay. Tôi đã đi qua một số bài kiểm tra thực hành, và một trong số họ đã nói về các sizeof và giá trị mà nó trả về hoặc các loại biến khác nhau. >>. Và họ nói rằng cả int và dài trở lại 4, vì vậy họ đang dài cả 4 byte. Có sự khác biệt giữa một int và một thời gian dài, hoặc là nó cùng một điều? Có, có một sự khác biệt. Các tiêu chuẩn C - Tôi có thể sẽ gặp rắc rối. Các tiêu chuẩn C giống như những gì C, tài liệu chính thức của C. Đây là những gì nó nói. Vì vậy, các tiêu chuẩn C chỉ nói rằng một char mãi mãi và sẽ luôn luôn là 1 byte. Tất cả mọi thứ sau đó - một đoạn ngắn là luôn luôn chỉ được xác định là lớn hơn hoặc bằng một char. Điều này có thể được nghiêm chỉnh lớn hơn, nhưng không tích cực. Int chỉ được xác định là lớn hơn hoặc bằng một đoạn ngắn. Và một thời gian dài chỉ được xác định là lớn hơn hoặc bằng một int. Và một lâu dài lớn hơn hoặc bằng một thời gian dài. Vì vậy, điều duy nhất các tiêu chuẩn C định nghĩa là sự sắp xếp tương đối của tất cả mọi thứ. Số tiền thực tế của bộ nhớ rằng mọi thứ mất nói chung là đến thực hiện, nhưng nó khá tốt quy định tại điểm này. >> [Sinh viên] Okay. Vì vậy, quần short được hầu như luôn luôn sẽ là 2 byte. Ints gần như luôn luôn sẽ là 4 byte. Chờ đợi lâu gần như luôn luôn sẽ là 8 byte. Và chờ đợi, nó phụ thuộc vào việc bạn đang sử dụng một hệ thống 32-bit hoặc 64-bit. Vì vậy, một thời gian dài sẽ tương ứng với các loại hệ thống. Nếu bạn đang sử dụng một hệ thống 32-bit như gia dụng, nó sẽ là 4 byte. Nếu bạn đang sử dụng 64-bit như rất nhiều các máy tính gần đây, nó sẽ là 8 byte. Ints hầu như luôn luôn 4 byte vào thời điểm này. Chờ đợi Long là hầu như luôn luôn 8 bytes. Trong quá khứ, ints được sử dụng để chỉ là 2 byte. Nhưng hãy chú ý rằng điều này hoàn toàn đáp ứng tất cả các mối quan hệ lớn hơn hoặc bằng. Vì vậy, miễn là hoàn toàn được phép có cùng kích thước như một số nguyên, và nó cũng được phép có cùng kích thước như một lâu dài. Và nó chỉ như vậy sẽ xảy ra là 99,999% của các hệ thống, nó là có được bằng hoặc một int hoặc một lâu dài. Nó chỉ phụ thuộc vào-32 bit hoặc 64-bit. >> [Sinh viên] Okay. Trong phao, làm thế nào là điểm thập phân được chỉ định trong các bit? Giống như như nhị phân? >> Yeah. Bạn không cần phải biết rằng đối với CS50. Bạn thậm chí không biết rằng ở 61. Bạn không thể học mà thực sự trong bất kỳ khóa học nào. Nó chỉ là một đại diện. Tôi quên allotments bit chính xác. Ý tưởng về điểm nổi là bạn phân bổ một số cụ thể của bit để đại diện - Về cơ bản, mọi thứ trong ký hiệu khoa học. Vì vậy, bạn phân bổ một số cụ thể của bit để đại diện cho số, như 1,2345. Tôi không bao giờ có thể đại diện cho một số với chữ số hơn 5. Sau đó, bạn cũng có thể phân bổ một số cụ thể của bit để nó có xu hướng trở nên giống như bạn chỉ có thể đi đến một số lượng nhất định, như đó là số mũ lớn nhất bạn có thể có, và bạn chỉ có thể đi xuống đến một số mũ nào đó, như đó là số mũ nhỏ nhất bạn có thể có. Tôi không nhớ các bit cách chính xác được giao cho tất cả các giá trị, nhưng một số lượng nhất định của các bit được dành riêng 1,2345, một số lượng nhất định của các bit được dành riêng cho số mũ, và nó chỉ có thể đại diện cho một số mũ của một kích thước nhất định. [Sinh viên] Và một đôi? Là giống như một phao dài thêm? >> Yeah. Đó là điều tương tự như một phao ngoại trừ bây giờ bạn đang sử dụng 8 byte thay vì 4 byte. Bây giờ bạn sẽ có thể sử dụng 9 chữ số hoặc 10 chữ số, và điều này sẽ có thể tăng lên đến 300 thay vì 100. >> [Sinh viên] Okay. Và nổi cũng là 4 byte. >>. Vâng, một lần nữa, nó có thể phụ thuộc tổng thể thực hiện chung, nhưng nổi là 4 byte, tăng gấp đôi là 8. Đôi được gọi là tăng gấp đôi bởi vì họ là tăng gấp đôi kích thước của phao nổi. [Sinh viên] Okay. Và có đôi tăng gấp đôi? >> Không có. Tôi nghĩ rằng - >> [sinh viên] Giống như chờ đợi lâu? >> Yeah. Tôi không nghi vậy. Vâng. [Sinh viên] Trong thử nghiệm năm ngoái đã có một câu hỏi về các chức năng chính phải là một phần của chương trình của bạn. Câu trả lời là rằng nó không phải là một phần của chương trình của bạn. Trong những gì tình hình? Đó là những gì tôi thấy. [Bowden] Có vẻ như - >> [sinh viên] tình hình gì? Bạn có vấn đề? >> [Sinh viên] Yeah, tôi chắc chắn có thể kéo nó lên. Nó không phải là, về mặt kỹ thuật, nhưng về cơ bản nó sẽ là. [Sinh viên] Tôi thấy một trên một năm khác nhau. Nó giống như là Đúng hay Sai: hợp lệ - >> Oh, một tập tin c? [Sinh viên] Bất kỳ c tập tin phải có - [cả hai nói cùng một lúc - không thể hiểu] Okay. Vì vậy, đó là riêng biệt. A c file. Chỉ cần để chứa các chức năng. Bạn có thể biên dịch một tập tin vào mã máy, nhị phân, bất cứ điều gì, mà không có nó là thực thi. File thực thi hợp lệ phải có một hàm main. Bạn có thể viết 100 chức năng trong 1 tập tin nhưng không chính và sau đó biên dịch sang nhị phân, sau đó bạn viết một tập tin mà chỉ có chính nhưng nó gọi một loạt các chức năng này trong tập tin nhị phân này ở đây. Và như vậy khi bạn đang làm thực thi, đó là mối liên kết là nó kết hợp các tập tin nhị phân 2 vào một tập tin thực thi. Vì vậy, một c tập tin không cần phải có một chức năng chính ở tất cả. Và trên cơ sở mã lớn, bạn sẽ thấy hàng ngàn các tập tin c. Và 1 tập tin chính. Câu hỏi nhiều hơn? [Sinh viên] Có một câu hỏi khác. Nó nói làm là một trình biên dịch. Đúng hay Sai? Và câu trả lời là sai, và tôi hiểu lý do tại sao nó không giống như Clang. Nhưng những gì chúng ta gọi làm nếu nó không? Hãy là cơ bản chỉ - tôi có thể thấy chính xác những gì nó gọi nó. Nhưng nó chỉ chạy lệnh. Thực hiện. Tôi có thể kéo lên này. Yeah. Oh, yeah. Thực hiện cũng có nào đó. Điều này nói rằng mục đích của tiện ích make là để xác định tự động phần của một chương trình lớn cần phải được biên dịch lại và ban hành các lệnh để biên dịch lại chúng. Bạn có thể làm cho các tập tin là hoàn toàn rất lớn. Hãy nhìn vào tem thời gian của các tập tin, và như chúng tôi đã nói trước đây, bạn có thể biên dịch các tập tin cá nhân, và nó không phải cho đến khi bạn nhận được các mối liên kết rằng họ đang đặt lại với nhau thành một thực thi. Vì vậy, nếu bạn có 10 tập tin khác nhau và bạn thực hiện một sự thay đổi đến 1 trong số họ, sau đó những gì làm cho là sẽ làm là chỉ cần biên dịch lại 1 tập tin và sau đó liên kết lại tất cả mọi thứ với nhau. Nhưng đó là ngớ ngẩn hơn thế rất nhiều. Đó là vào bạn để hoàn toàn xác định rằng đó là những gì nó cần phải làm. Nó theo mặc định có khả năng nhận ra các công cụ này tem thời gian, nhưng bạn có thể viết một tập tin làm cho làm bất cứ điều gì. Bạn có thể viết một tập tin để khi bạn gõ làm cho nó chỉ cd vào thư mục khác. Tôi đã cảm thấy thất vọng bởi vì tôi tack tất cả mọi thứ bên trong của gia dụng của tôi và sau đó tôi xem các file PDF từ máy Mac. Vì vậy, tôi hãy vào Finder và tôi có thể đi, kết nối với máy chủ, và các máy chủ kết nối với gia dụng của tôi, và sau đó tôi mở ra PDF được biên dịch LaTeX. Nhưng tôi đã thất vọng bởi vì mỗi lần duy nhất tôi cần thiết để làm mới PDF, Tôi đã để sao chép nó vào một thư mục cụ thể mà nó có thể truy cập và nó đã nhận được gây phiền nhiễu. Vì vậy, thay vào đó tôi đã viết một tập tin thực hiện, bạn phải xác định làm thế nào nó làm cho mọi thứ. Làm thế nào bạn thực hiện trong này là PDF LaTeX. Cũng giống như bất kỳ tập tin làm cho khác - tôi đoán bạn không nhìn thấy các tập tin làm cho, nhưng chúng tôi có trong Ứng dụng một tập tin làm cho toàn cầu mà chỉ nói, nếu bạn đang biên soạn một tập tin C, sử dụng Clang. Và vì vậy ở đây trong tập tin làm cho tôi rằng tôi làm cho tôi nói, tập tin này, bạn sẽ muốn để biên dịch với PDF LaTeX. Và thật LaTeX PDF đó là làm việc biên dịch. Phải là không biên dịch. Nó chỉ chạy các lệnh trong trình tự tôi đã chỉ định. Vì vậy, nó chạy PDF LaTeX, nó sao chép nó vào thư mục tôi muốn nó được sao chép vào, nó cd vào thư mục và không những thứ khác, nhưng tất cả nó được nhận ra khi một thay đổi tập tin, và nếu nó thay đổi, sau đó nó sẽ chạy những lệnh mà nó phải chạy khi thay đổi tập tin. >> [Sinh viên] Okay. Tôi không biết nơi mà các tập tin làm cho toàn cầu cho tôi để kiểm tra xem nó ra. Các câu hỏi khác? Bất cứ điều gì từ quá khứ câu đố? Bất kỳ điều con trỏ? Có những điều tinh tế với con trỏ như - Tôi sẽ không để có thể tìm thấy một câu hỏi bài kiểm tra trên đó - nhưng cũng giống như các loại điều này. Hãy chắc chắn rằng bạn hiểu rằng khi tôi nói int * x * y - Điều này là không chính xác bất cứ điều gì ở đây, tôi đoán. Nhưng giống như * x * y, những người đang có 2 biến trên stack. Khi tôi nói x = malloc (sizeof (int)), x là một biến trên stack, malloc là một số khối trên trong đống, và chúng tôi đang có x điểm heap. Vì vậy, một cái gì đó trên các ngăn xếp để heap. Bất cứ khi nào bạn malloc bất cứ điều gì, bạn chắc chắn lưu trữ nó bên trong của một con trỏ. Vì vậy, rằng con trỏ là trên stack, khối malloced trên heap. Rất nhiều người bị lẫn lộn và nói int * x = malloc, x là trên heap. Số gì x điểm đến là trên heap. x chính nó là trên stack, trừ khi vì lý do gì đã x là một biến toàn cầu, trong trường hợp này nó sẽ xảy ra trong một khu vực bộ nhớ. Vì vậy, việc theo dõi, các sơ đồ hộp và mũi tên khá phổ biến cho các bài kiểm tra. Hoặc nếu nó không phải trên đố 0, nó sẽ được bài kiểm tra 1. Bạn nên biết tất cả những điều này, các bước biên soạn kể từ khi bạn đã trả lời câu hỏi về những người. Vâng. [Sinh viên] chúng ta có thể đi qua những bước - >> chắc. Trước khi bước và biên dịch chúng tôi có bộ tiền xử lý, biên soạn, lắp ráp, và liên kết. Tiền xử lý. Điều đó làm gì? Đó là bước đơn giản nhất - tốt, không giống như - điều đó không có nghĩa là nó nên được rõ ràng, nhưng đó là bước đơn giản nhất. Các bạn có thể thực hiện nó chính mình. Yeah. [Sinh viên] những gì bạn có trong của bạn bao gồm như thế này và nó sao chép và sau đó cũng xác định. Nó tìm kiếm những thứ như # include và # xác định, và nó chỉ là bản sao và dạng bột nhão những gì những người thực sự có ý nghĩa. Vì vậy, khi bạn nói # bao gồm cs50.h, tiền xử lý là sao chép và dán cs50.h vào dòng đó. Khi bạn nói # xác định x là 4, tiền xử lý đi qua toàn bộ chương trình và thay thế tất cả các trường hợp của x với 4. Vì vậy, tiền xử lý có một tập tin C hợp lệ và kết quả đầu ra một tập tin C hợp lệ nơi mà mọi thứ đã được sao chép và dán. Vì vậy, bây giờ biên dịch. Điều đó làm gì? [Sinh viên] đi từ C đến nhị phân. [Bowden] Nó không đi tất cả các cách để nhị phân. [Sinh viên] mã máy sau đó? >> Đó không phải là mã máy. [Sinh viên] hội? >> Hội. Nó đi vào hội trước khi nó đi tất cả các cách để mã C, và hầu hết các ngôn ngữ làm một cái gì đó như thế này. Chọn bất kỳ ngôn ngữ cấp cao, và nếu bạn đang đi để biên dịch nó, nó có khả năng biên dịch trong các bước. Đầu tiên nó sẽ biên dịch Python C, sau đó nó sẽ biên dịch C để hội, và sau đó hội sẽ nhận được dịch sang nhị phân. Vì vậy, biên dịch được sẽ mang lại cho nó từ C đến hội. Từ biên dịch thường có nghĩa là đưa nó từ một mức độ cao hơn một ngôn ngữ lập trình cấp thấp hơn. Vì vậy, đây là bước duy nhất trong việc lập, nơi mà bạn bắt đầu với một ngôn ngữ cấp cao và kết thúc bằng một ngôn ngữ cấp thấp, và đó là lý do tại sao bước được gọi là biên dịch. [Sinh viên] Trong quá trình biên soạn, chúng ta hãy nói rằng bạn đã thực hiện # bao gồm cs50.h. Trình biên dịch biên dịch lại các cs50.h, giống như các chức năng mà trong đó, và dịch thành mã hội là tốt, hoặc nó sẽ sao chép và dán một cái gì đó là được trước hội? cs50.h sẽ khá nhiều không bao giờ kết thúc hội. Các công cụ như nguyên mẫu chức năng và những thứ chỉ dành cho bạn phải cẩn thận. Nó đảm bảo rằng các trình biên dịch có thể kiểm tra những thứ như bạn đang gọi chức năng với các loại trả lại quyền và các đối số và các công cụ phù hợp. Vì vậy, cs50.h sẽ được preprocessed vào tập tin, và sau đó khi nó được biên dịch Về cơ bản nó vứt bỏ sau khi nó làm cho chắc chắn rằng tất cả mọi thứ được gọi chính xác. Nhưng các chức năng quy định trong thư viện CS50, riêng biệt từ cs50.h, những người sẽ không được biên soạn riêng. Điều đó thực sự sẽ đi xuống trong bước kết nối, vì vậy chúng tôi sẽ nhận được trong một giây. Nhưng trước tiên, những gì đang lắp ráp? [Sinh viên] hội nhị phân? >> Yeah. Lắp ráp. Chúng tôi không gọi đó là biên dịch bởi vì hội là khá nhiều một bản dịch thuần túy nhị phân. Có rất ít logic trong đi từ hội để nhị phân. Nó giống như nhìn lên trong một bảng, oh, chúng tôi có hướng dẫn này; tương ứng để nhị phân 01.110. Và như vậy các tập tin mà lắp ráp thường kết quả đầu ra các tập tin o. Và o các tập tin là những gì chúng tôi đã nói trước, làm thế nào một tập tin không cần phải có một chức năng chính. Tập tin bất kỳ có thể được biên dịch vào một tập tin o. Miễn là nó là một tập tin C hợp lệ. Nó có thể được biên dịch xuống. O. Bây giờ, liên kết là những gì thực sự mang lại một loạt các o các tập tin và mang lại cho họ một tập tin thực thi. Và vì vậy những gì liên kết nào bạn có thể nghĩ của thư viện CS50 như một tập tin o. Nó là một tập tin nhị phân đã được biên dịch. Và như vậy khi bạn biên dịch tập tin của bạn, hello.c của bạn, mà các cuộc gọi GetString, hello.c được biên dịch xuống hello.o, hello.o bây giờ là trong nhị phân. Nó sử dụng GetString, vì vậy nó cần phải đi qua để cs50.o, và mối liên kết smooshes chúng lại với nhau và bản sao GetString vào tập tin này và đi ra với một thực thi có tất cả các chức năng cần thiết. Vì vậy, cs50.o là không thực sự là một tập tin O, nhưng nó đủ gần là không có sự khác biệt cơ bản. Vì vậy, liên kết chỉ mang đến một loạt các tập tin với nhau riêng chứa tất cả các chức năng cần phải sử dụng và tạo ra các thực thi mà sẽ thực sự chạy. Và đó cũng là những gì chúng tôi đã nói trước nơi bạn có thể có 1000 c các tập tin, bạn biên dịch tất cả chúng để o các tập tin,. mà có lẽ sẽ mất một thời gian, sau đó bạn thay đổi 1 file c. Bạn chỉ cần biên dịch lại 1. C tập tin và sau đó tất cả mọi thứ relink khác, liên kết tất cả mọi thứ trở lại với nhau. [Sinh viên] Khi chúng tôi đang liên kết chúng ta viết lcs50? Yeah, cái lcs50. Mà lá cờ tín hiệu để mối liên kết mà bạn cần phải liên kết trong thư viện đó. Câu hỏi? Chúng ta đã đi qua nhị phân khác hơn so với 5 giây trong bài giảng đầu tiên? Tôi không nghi vậy. Bạn nên biết tất cả của hệ điều hành lớn mà chúng tôi đã đi qua, và bạn sẽ có khả năng, nếu chúng ta đã cho bạn một chức năng, bạn sẽ có thể nói đó là O lớn, khoảng. Hoặc tốt, O lớn là thô. Vì vậy, nếu bạn thấy lồng nhau cho các vòng Looping trên cùng một số vật, như int i, i > [sinh viên] n bình phương. >> Nó có xu hướng được n bình phương. Nếu bạn đã ba lồng nhau, nó có xu hướng được n Cubed. Vì vậy, loại điều đó, bạn sẽ có thể chỉ ra ngay lập tức. Bạn cần phải biết sắp xếp chèn và loại bong bóng và hợp nhất phân loại và tất cả những người. Nó dễ dàng hơn để hiểu lý do tại sao họ là những n bình phương và n log n và tất cả những điều đó bởi vì tôi nghĩ rằng đã có một bài kiểm tra một năm chúng tôi về cơ bản cho bạn thực hiện các loại bong bóng và nói, "thời gian hoạt động của chức năng này là gì?" Vì vậy, nếu bạn nhận ra nó như là loại bong bóng, sau đó bạn có thể ngay lập tức nói n bình phương. Nhưng nếu bạn chỉ cần nhìn vào nó, bạn thậm chí không cần phải nhận ra loại bong bóng; bạn chỉ có thể nói điều này là làm điều này và điều này. Này là n bình phương. [Sinh viên] Có bất kỳ ví dụ khó khăn bạn có thể đến với như một ý tưởng tương tự để tìm ra? Tôi không nghĩ rằng chúng tôi sẽ cung cấp cho bạn bất kỳ ví dụ khó khăn. Việc sắp xếp bong bóng là về là khó khăn như chúng tôi sẽ đi, và thậm chí đó, miễn là bạn hiểu rằng bạn đang iterating trên mảng cho mỗi phần tử trong mảng, là có được cái gì đó là n bình phương. Có những câu hỏi chung, ngay tại đây, chúng tôi có - Oh. Một ngày khác, Doug tuyên bố, "Tôi đã phát minh ra một thuật toán mà có thể sắp xếp một mảng "Số n trong O (log n) thời gian!" Vì vậy, làm thế nào để chúng ta biết đó là không thể? [Không nghe được sinh viên phản ứng] >> Yeah. Ít nhất, bạn phải chạm vào mỗi phần tử trong mảng, vì vậy nó không thể sắp xếp một mảng - Nếu tất cả mọi thứ để phân loại, sau đó bạn sẽ được chạm vào tất cả mọi thứ trong mảng, do đó, nó không thể làm điều đó trong ít hơn O của n. [Sinh viên] cho chúng ta thấy rằng ví dụ có khả năng để làm điều đó trong O n nếu bạn sử dụng rất nhiều bộ nhớ. >> Yeah. Và Đó - tôi quên những gì that's nó đếm loại? Hmm. Đó là một thuật toán phân loại số nguyên. Tôi đang tìm kiếm các tên đặc biệt cho điều này mà tôi không thể nhớ tuần trước. Yeah. Đây là các loại của các loại mà có thể thực hiện những điều trong O lớn của n. Nhưng có những hạn chế, như bạn chỉ có thể sử dụng số nguyên đến một số lượng nhất định. Cộng thêm nếu bạn đang cố gắng để sắp xếp that's một cái gì đó - Nếu mảng của bạn là 012, -12, 151, 4 triệu đồng, sau đó là yếu tố duy nhất được sẽ hoàn toàn hủy hoại toàn bộ việc phân loại. Câu hỏi? [Sinh viên] Nếu bạn có một hàm đệ quy và nó chỉ làm cho các cuộc gọi đệ quy trong một tuyên bố trở lại, đó là đệ quy đuôi, và do đó sẽ không sử dụng bộ nhớ nhiều hơn nữa trong thời gian chạy hoặc ít nhất sẽ sử dụng bộ nhớ so sánh như một giải pháp lặp đi lặp lại? [Bowden]. Nó có khả năng sẽ là hơi chậm, nhưng không thực sự. Đệ quy đuôi là khá tốt. Nhìn lại các khung stack, chúng ta hãy nói rằng chúng ta có chính và chúng tôi có thanh int (int x) hoặc một cái gì đó. Đây không phải là một hàm đệ quy hoàn hảo, nhưng trở lại bar (x - 1). Vì vậy, rõ ràng, điều này là sai lầm. Bạn cần phải trường hợp cơ sở và các công cụ. Tuy nhiên, ý tưởng ở đây là đây là đệ quy đuôi, có nghĩa là khi thanh cuộc gọi chính nó sẽ nhận được stack frame. Stack frame này sẽ là một khối ít bộ nhớ tương ứng với đối số x của nó. Và do đó, chúng ta hãy nói rằng chính xảy ra để gọi bar (100); Vì vậy, x sẽ bắt đầu ra như là 100. Nếu trình biên dịch nhận ra rằng đây là một chức năng đệ quy đuôi, sau đó khi thanh làm cho cuộc gọi đệ quy của nó để thanh, thay vì làm một khung stack mới, đó là nơi mà ngăn xếp bắt đầu phát triển phần lớn, cuối cùng nó sẽ chạy vào heap và sau đó bạn sẽ có được segfaults bởi vì bộ nhớ bắt đầu va chạm. Vì vậy, thay vì làm cho khung stack riêng của mình, nó có thể nhận ra, hey, tôi không bao giờ thực sự cần phải quay trở lại stack frame này, do đó, thay vào đó tôi sẽ chỉ thay thế đối số này với 99 và sau đó bắt đầu thanh trên tất cả. Và sau đó nó sẽ làm điều đó một lần nữa và nó sẽ đạt đến thanh trở lại (x - 1), và thay vì làm một stack frame mới, nó chỉ sẽ thay thế tham số hiện tại của nó với 98 và sau đó nhảy đến sự khởi đầu của thanh. Những hoạt động, thay thế giá trị đó 1 trên stack và nhảy trở lại để bắt đầu, là khá hiệu quả. Vì vậy, không chỉ là sử dụng bộ nhớ giống như là một chức năng riêng biệt được lặp đi lặp lại bởi vì bạn chỉ sử dụng 1 stack frame, nhưng bạn không bị các nhược điểm có để gọi chức năng. Chức năng gọi điện thoại có thể hơi tốn kém bởi vì nó đã làm tất cả những thiết lập này và teardown và tất cả các công cụ này. Vì vậy, điều này đệ quy đuôi là tốt. [Sinh viên] Tại sao nó không tạo ra bước tiến mới? Bởi vì nó nhận ra rằng nó không cần phải. Các cuộc gọi đến một quán bar chỉ là trở về cuộc gọi đệ quy. Vì vậy, nó không cần phải làm bất cứ điều gì với các giá trị trả về. Nó sẽ ngay lập tức trả lại. Vì vậy, nó sẽ thay thế lập luận riêng của mình và bắt đầu lại từ đầu. Và cũng có thể, nếu bạn không có phiên bản đệ quy đuôi, sau đó bạn sẽ có được tất cả các quán bar, nơi khi thanh này trả về nó phải trả về giá trị của nó với trang này, sau đó là thanh ngay lập tức trở và nó trả về giá trị là một trong những điều này, sau đó nó chỉ sẽ ngay lập tức quay trở lại và trả lại giá trị của nó với trường này. Vì vậy, bạn đang tiết kiệm này popping tất cả những việc của chồng kể từ khi giá trị trả lại sẽ được thông qua tất cả các cách trở lại lên anyway. Vậy tại sao không chỉ cần thay thế lập luận của chúng tôi với các đối số được cập nhật và bắt đầu lại từ đầu? Nếu chức năng không phải là đệ quy đuôi, nếu bạn làm một cái gì đó như [Sinh viên] bar (x + 1). >> Yeah. Vì vậy, nếu bạn đặt nó trong điều kiện, sau đó bạn đang làm một cái gì đó với giá trị trả về. Hoặc ngay cả khi bạn chỉ cần làm trở lại 2 bar (x - 1). Vì vậy, bây giờ bar (x - 1) cần phải quay trở lại để cho nó để tính toán 2 lần giá trị đó, vì vậy bây giờ nó không cần stack frame riêng biệt của riêng mình, và bây giờ, không có vấn đề khó khăn như thế nào bạn cố gắng, bạn sẽ cần phải - Đây không phải là đệ quy đuôi. [Sinh viên] Tôi sẽ cố gắng để mang lại một đệ quy để nhằm mục đích cho một đệ quy đuôi - [Bowden] Trong một thế giới lý tưởng, nhưng trong CS50 bạn không có. Để có được đệ quy đuôi, nói chung, bạn thiết lập một đối số bổ sung nơi thanh sẽ mất int x vào y và y tương ứng với điều cuối cùng bạn muốn trả lại. Vì vậy, sau đó bạn sẽ được trả lại bar (x - 1), 2 * y. Vì vậy, đó chỉ là một cấp cao như thế nào bạn biến đổi những điều cần phải được đệ quy đuôi. Nhưng cuộc tranh luận thêm Và rồi cuối cùng khi bạn đạt đến trường hợp cơ sở của bạn, bạn chỉ cần trả lại y bởi vì bạn đã được tích lũy toàn bộ thời gian giá trị trả về mà bạn muốn. Bạn chắc hẳn đã được làm việc đó lặp đi lặp lại nhưng bằng cách sử dụng các cuộc gọi đệ quy. Câu hỏi? [Sinh viên] Có lẽ về con trỏ số học, giống như khi sử dụng dây. >> Chắc chắn rồi. Con trỏ số học. Khi sử dụng dây rất dễ dàng bởi vì dây là sao char, ký tự là mãi mãi và luôn luôn một byte duy nhất, và do đó, con trỏ số học tương đương với số học thường xuyên khi bạn đang làm việc với chuỗi. Hãy chỉ nói char * s = "hello". Vì vậy, chúng tôi có một khối trong bộ nhớ. Nó cần 6 byte bởi vì bạn luôn cần terminator vô giá trị. Và char * s để trỏ đến đầu của mảng này. Vì vậy, s chỉ ở đó. Bây giờ, điều này về cơ bản là mảng bất kỳ hoạt động như thế nào, bất kể cho dù đó là sự trở lại của malloc hoặc cho dù đó là trên stack. Bất kỳ mảng về cơ bản là một con trỏ đến sự bắt đầu của mảng, và sau đó bất kỳ hoạt động mảng, bất kỳ lập chỉ mục, chỉ cần đi vào mảng đó nhất định bù đắp. Vì vậy, khi tôi nói một cái gì đó như s [3]; điều này là có s và đếm 3 ký tự. Vì vậy, s [3], chúng ta có 0, 1, 2, 3, nên s [3] sẽ tham khảo l này. [Sinh viên] Và chúng ta có thể đạt được cùng một giá trị bằng cách làm s + 3 và sau đó sao ngoặc? Vâng. Điều này tương đương với * (+ 3); và đó là mãi mãi và luôn luôn tương đương không có vấn đề gì bạn làm. Bạn không bao giờ cần sử dụng cú pháp khung. Bạn luôn có thể sử dụng * cú pháp (s + 3). Mọi người có xu hướng thích cú pháp khung, mặc dù. [Sinh viên] Vì vậy, tất cả các mảng thực sự chỉ là con trỏ. Có sự phân biệt nhẹ khi tôi nói int x [4]; [sinh viên] Điều đó tạo ra bộ nhớ? Bowden] Đó là để tạo ra 4 ints trên stack, do đó, 16 byte tổng thể. Nó sẽ tạo ra 16 bytes trên stack. x không được lưu trữ bất cứ nơi nào. Nó chỉ là một biểu tượng đề cập đến sự khởi đầu của điều này. Bởi vì bạn tuyên bố các mảng bên trong chức năng này, trình biên dịch sẽ làm là chỉ cần thay thế tất cả các trường hợp của biến x nơi nó xảy ra để lựa chọn để đưa những 16 byte. Nó không thể làm điều đó với char * s vì s là một con trỏ thực tế. Nó là miễn phí để sau đó trỏ đến những thứ khác. x là một hằng số. Bạn không thể có nó trỏ đến một mảng khác nhau. >> [Sinh viên] Okay. Tuy nhiên, ý tưởng này, lập chỉ mục, là như nhau bất kể cho dù đó là một mảng truyền thống hoặc nếu nó là một con trỏ đến một cái gì đó hoặc nếu đó là một con trỏ đến một mảng malloced. Và trên thực tế, nó là như vậy tương đương rằng đó cũng là điều tương tự. Nó thực sự chỉ dịch những gì bên trong các dấu ngoặc và những gì còn lại của khung, thêm chúng với nhau, và dereferences. Vì vậy, đây chỉ là giá trị như * (s + 3) hoặc s [3]. [Sinh viên] bạn có thể có con trỏ trỏ đến mảng 2-chiều? Đó là khó khăn hơn. Theo truyền thống, không. Một mảng 2-chiều chỉ là một mảng 1 chiều với một số cú pháp thuận tiện bởi vì khi tôi nói int x [3] [3], điều này thực sự chỉ là 1 mảng với 9 giá trị. Và như vậy khi tôi chỉ số, trình biên dịch biết những gì tôi có ý nghĩa. Nếu tôi nói x [1] [2], nó biết tôi muốn đi đến hàng thứ hai, do đó, nó sẽ bỏ qua người đầu tiên 3, và sau đó nó muốn điều thứ hai trong đó, do đó, nó sẽ nhận được một trong những điều này. Nhưng nó vẫn chỉ là một mảng một chiều. Và vì vậy nếu tôi muốn gán một con trỏ đến mảng đó, Tôi sẽ nói int * p = x; Các loại x chỉ là - Đó là kiểu nói thô của x vì nó chỉ là một biểu tượng và nó không phải là một biến thực tế, nhưng nó chỉ là một int *. x chỉ là một con trỏ đến sự bắt đầu của điều này. >> [Sinh viên] Okay. Và vì vậy tôi sẽ không thể truy cập [1] [2]. Tôi nghĩ rằng đó là cú pháp đặc biệt để khai báo một con trỏ, một cái gì đó vô lý như int (* p [- một cái gì đó hoàn toàn vô lý, tôi thậm chí không biết. Nhưng có một cú pháp để khai báo con trỏ như thế nào với các dấu ngoặc đơn và những thứ. Nó có thể không cho phép bạn làm điều đó. Tôi có thể nhìn lại một cái gì đó mà có thể cho tôi biết sự thật. Tôi sẽ xem xét cho nó sau này, nếu có một cú pháp cho điểm. Nhưng bạn sẽ không bao giờ nhìn thấy nó. Và ngay cả cú pháp như vậy là cổ xưa rằng nếu bạn sử dụng nó, mọi người sẽ bị bối rối. Mảng nhiều chiều là khá hiếm như nó được. Bạn có khá nhiều - Vâng, nếu bạn đang làm những việc ma trận nó sẽ không là hiếm, nhưng trong C, bạn hiếm khi sẽ được sử dụng các mảng đa chiều. Yeah. >> [Sinh viên] Hãy nói rằng bạn có một mảng thực sự dài. Vì vậy, trong bộ nhớ ảo, nó sẽ xuất hiện là tất cả các liên tiếp, giống như các yếu tố ngay bên cạnh nhau, nhưng trong bộ nhớ vật lý, nó sẽ có thể cho rằng để được chia? >>. Làm thế nào các công trình bộ nhớ ảo là nó chỉ tách - Các đơn vị phân bổ là một trang, mà có xu hướng là 4 kilobyte, và do đó, khi một quá trình nói, hey, tôi muốn sử dụng bộ nhớ này, hệ điều hành được phân bổ cho 4 kilobyte cho rằng khối ít bộ nhớ. Thậm chí nếu bạn chỉ sử dụng một ít byte duy nhất trong toàn bộ khối của bộ nhớ, hệ điều hành sẽ để cho nó đủ trong 4 kilobyte. Vì vậy, điều này có nghĩa là tôi có thể có - chúng ta hãy nói rằng đây là chồng của tôi. Ngăn xếp này có thể được tách ra. Ngăn xếp của tôi có thể là MB và MB. Ngăn xếp của tôi có thể là rất lớn. Nhưng ngăn xếp chính nó có thể được chia thành các trang cá nhân, nếu chúng ta nhìn ở đây chúng ta hãy nói rằng đây là bộ nhớ RAM của chúng tôi, nếu tôi có 2 GB RAM, đây là địa chỉ thực tế 0 như các byte 0 của bộ nhớ RAM của tôi, và đây là 2 GB tất cả các con đường xuống đây. Vì vậy, trang này có thể tương ứng với khối này ở đây. Trang này có thể tương ứng với khối này ở đây. Điều này có thể tương ứng này ở đây. Vì vậy, hệ điều hành là miễn phí để gán bộ nhớ vật lý bất kỳ trang cá nhân tùy tiện. Và điều đó có nghĩa là nếu biên giới này xảy ra với sự dang chân ra một mảng, một mảng xảy ra để được bên trái và bên phải của thứ tự của trang này, sau đó mà mảng sẽ được phân chia trong bộ nhớ vật lý. Và sau đó khi bạn thoát khỏi chương trình, khi quá trình này kết thúc, các ánh xạ này được xoá hoàn toàn và sau đó nó miễn phí để sử dụng các khối nhỏ cho những thứ khác. Câu hỏi nhiều hơn? [Sinh viên] con trỏ số học. >> Oh yeah. Dây dễ dàng hơn, nhưng nhìn vào một cái gì đó giống như ints, để trở lại int x [4]; Cho dù đây là một mảng hoặc cho dù đó là một con trỏ đến một mảng malloced của 4 số nguyên, nó sẽ được đối xử theo cùng một cách. [Sinh viên] Vì vậy, mảng là trên heap? Bowden] Mảng không phải là trên heap này. >> [Sinh viên] Oh. [Bowden] của mảng này có xu hướng được trên stack trừ khi bạn tuyên bố nó - bỏ qua các biến toàn cầu. Không sử dụng các biến toàn cầu. Bên trong của một chức năng tôi nói int x [4]; Nó sẽ tạo ra một khối 4 số nguyên trên stack cho mảng này. Nhưng điều này malloc (4 * sizeof (int)); sẽ đi trên heap. Tuy nhiên, sau thời điểm này, tôi có thể sử dụng x và p khá nhiều cùng một cách, khác với các trường hợp ngoại lệ tôi đã nói trước khi về, bạn có thể gán lại p. Về mặt kỹ thuật, kích thước của họ là hơi khác nhau, nhưng đó là hoàn toàn không liên quan. Bạn không bao giờ thực sự sử dụng kích thước của chúng. P tôi có thể nói p [3] = 2; x [3] = 2; Bạn có thể sử dụng chúng trong chính xác cùng một cách. Vì vậy, con trỏ số học bây giờ - Có. [Sinh viên] Bạn không cần phải làm p * nếu bạn có các dấu ngoặc? Ngoặc là một dereference ngầm. >> Okay. Trên thực tế, cũng là những gì bạn đang nói với các bạn có thể nhận được các mảng đa chiều với con trỏ, những gì bạn có thể làm là một cái gì đó như thế, chúng ta hãy nói, int ** pp = malloc (sizeof (int *) * 5); Tôi chỉ sẽ viết tất cả ra đầu tiên. Tôi không muốn điều đó. Okay. Những gì tôi đã làm ở đây là - Đó nên là pp [i]. Vì vậy, Trang là một con trỏ đến một con trỏ. Bạn đang mallocing Trang để trỏ đến một mảng trong 5 sao int. Vì vậy, trong bộ nhớ bạn có trên các trang ngăn xếp Nó sẽ để trỏ đến một mảng của 5 khối đó là tất cả bản thân con trỏ. Và sau đó khi tôi malloc xuống đây, tôi malloc rằng mỗi người trong số các con trỏ riêng biệt phải trỏ đến một khối riêng biệt của 4 byte trên heap. Vì vậy, điều này dẫn đến 4 byte. Và điều này một trong những điểm đến 4 byte khác nhau. Và tất cả chúng trỏ đến 4 byte của riêng của họ. Điều này mang lại cho tôi một cách để làm những việc đa chiều. Tôi có thể nói pp [3] [4], nhưng bây giờ điều này không phải là điều tương tự như các mảng đa chiều bởi vì các mảng đa chiều dịch [3] [4] vào một bù đắp vào mảng x. Này dereferences p, truy cập các chỉ số thứ ba, sau đó dereferences rằng và truy cập - 4 sẽ là không hợp lệ - chỉ số thứ hai. Trong khi đó, khi chúng tôi đã có int x [3] [4] trước khi một mảng đa chiều và khi bạn tăng gấp đôi khung nó thực sự chỉ là một dereference duy nhất, bạn đang theo dõi một con trỏ đơn và sau đó một bù đắp, đây thực sự là tài liệu tham khảo 2D. Bạn làm theo 2 con trỏ riêng biệt. Vì vậy, đây cũng là kỹ thuật cho phép bạn có các mảng đa chiều trong đó mỗi mảng cá nhân là kích cỡ khác nhau. Vì vậy, tôi nghĩ rằng mảng lởm chởm đa chiều là những gì nó được gọi là kể từ khi thực sự là điều đầu tiên có thể trỏ đến cái gì đó có 10 yếu tố, Điều thứ hai có thể trỏ đến cái gì đó có 100 phần tử. [Sinh viên] có bất kỳ giới hạn về số lượng của các con trỏ bạn có thể có chỉ cho con trỏ khác? >> Số Bạn có thể có int ***** p. Con trỏ số học - >> [sinh viên] Oh. >> Yeah. [Sinh viên] Nếu tôi có int *** p và sau đó tôi làm một dereferencing và tôi nói p * bằng giá trị này, là nó chỉ sẽ làm 1 mức độ dereferencing? >>. Vì vậy, nếu tôi muốn truy cập những điều mà con trỏ cuối cùng là chỉ vào - Sau đó, bạn làm p ***. >> Okay. Vì vậy, đây là điểm p để 1 block, điểm cho khối khác, các điểm cho khối khác. Sau đó, nếu bạn nào * p = cái gì khác, sau đó bạn đang thay đổi này bây giờ trỏ đến một khối khác nhau. >> Okay. [Bowden] Và nếu những malloced, sau đó bạn đã bị rò rỉ bộ nhớ trừ khi bạn xảy ra để có tài liệu tham khảo khác nhau của các vì bạn không thể có được trở lại với những những người mà bạn chỉ cần ném đi. Con trỏ số học. int x [4]; là sẽ cấp phát một mảng của 4 số nguyên trong đó x sẽ để trỏ đến đầu của mảng. Vì vậy, khi tôi nói một cái gì đó giống như x [1], tôi muốn nó có nghĩa là đi đến số nguyên thứ hai trong mảng, đó sẽ là một trong những điều này. Nhưng thực sự, đó là 4 byte vào mảng kể từ khi số nguyên này chiếm 4 byte. Vì vậy, một offset của 1 thực sự có nghĩa là một bù đắp của 1 lần kích thước của bất cứ loại của mảng. Đây là một mảng các số nguyên, để nó biết làm 1 lần kích thước của int khi nó muốn để bù đắp. Cú pháp khác. Hãy nhớ rằng đây là tương đương với * (x + 1); Khi tôi nói con trỏ + 1, những gì mà trả về là địa chỉ mà con trỏ được lưu trữ cộng thêm 1 lần kích thước của các loại của con trỏ. Vì vậy, nếu x = ox100, sau đó x + 1 = ox104. Và bạn có thể lợi dụng điều này và nói điều gì đó như char * c = (char *) x; và bây giờ c là có được địa chỉ giống như x. c sẽ để được bằng ox100, nhưng c + 1 là có được bằng ox101 kể từ khi con trỏ số học phụ thuộc vào kiểu của con trỏ mà bạn đang thêm. Vì vậy, c + 1, nó nhìn vào c, đó là một con trỏ char, vì vậy nó sẽ thêm 1 lần kích thước của char, mà luôn luôn có được 1, do đó, bạn sẽ có được 101, trong khi đó nếu tôi làm x, cũng vẫn còn 100, x + 1 sẽ là 104. [Sinh viên] Bạn có thể sử dụng c + + để tạm ứng con trỏ của bạn bằng 1? Có, bạn có thể. Bạn không thể làm điều đó với x vì x chỉ là một biểu tượng, nó là một hằng số, bạn không thể thay đổi x. Nhưng c xảy ra chỉ là một con trỏ, vì vậy c + + là hoàn toàn hợp lệ và nó sẽ tăng 1. Nếu c chỉ là một int *, sau đó c + + sẽ được 104. + + Con trỏ số học cũng giống như c + 1 sẽ phải thực hiện con trỏ số học. Điều này thực sự là rất nhiều những thứ như merge sort - Thay vì tạo ra các bản sao của sự vật, thay vào đó bạn có thể vượt qua - Cũng giống như nếu tôi muốn để vượt qua một nửa của mảng - hãy cho xóa một số điều này. Hãy nói rằng tôi muốn vượt qua bên này của mảng vào một chức năng. Những gì tôi sẽ vượt qua chức năng đó? Nếu tôi vượt qua x, tôi đi qua địa chỉ này. Nhưng tôi muốn vượt qua địa chỉ cụ thể này. Vì vậy, những gì tôi cần phải vượt qua? [Sinh viên] Pointer + 2? Bowden] Vì vậy x + 2. Vâng. Đó sẽ là địa chỉ này. Bạn cũng sẽ rất thường xuyên xem nó như là x [2] và sau đó địa chỉ đó. Vì vậy, bạn cần phải lấy địa chỉ của nó bởi vì khung là một dereference ngầm. x [2] đề cập đến giá trị trong hộp này, và sau đó bạn muốn địa chỉ của hộp đó, vì vậy bạn nói & x [2]. Vì vậy, đó là một cái gì đó như thế nào trong hợp nhất loại mà bạn muốn vượt qua một nửa danh sách để một cái gì đó bạn thực sự chỉ cần vượt qua & x [2], và bây giờ như xa như các cuộc gọi đệ quy là có liên quan, mảng mới của tôi bắt đầu có. Đến phút cuối của câu hỏi. [Sinh viên] Nếu chúng ta không đặt một dấu "và" hoặc một - đó là những gì được gọi là? >> Sao? [Sinh viên] Star. >> Mặt kỹ thuật, điều hành dereference, nhưng - >> [sinh viên] tham chiếu đến. Nếu chúng ta không đưa một ngôi sao hay một ký hiệu, những gì sẽ xảy ra nếu tôi chỉ nói y = x và x là một con trỏ? Loại của y là gì? >> [Sinh viên] tôi sẽ chỉ nói con trỏ nó 2. Vì vậy, nếu bạn chỉ cần nói y = x, bây giờ x và y điểm để điều tương tự. >> [Sinh viên] Point để điều tương tự. Và nếu x là một con trỏ int? >> Nó sẽ phàn nàn vì bạn không thể gán con trỏ. [Sinh viên] Okay. Hãy nhớ rằng con trỏ, mặc dù chúng ta vẽ chúng như là mũi tên, thực sự tất cả các cửa hàng họ - int * x - thực sự tất cả các x được lưu trữ là một cái gì đó giống như ox100, mà chúng xảy ra với đại diện là chỉ khối được lưu trữ ở 100. Vì vậy, khi tôi nói int * y = x; tôi chỉ cần sao chép ox100 vào y, mà chúng ta chỉ cần đi để đại diện cho y, cũng chỉ để ox100. Và nếu tôi nói int i = (int) x, sau đó tôi sẽ để lưu trữ bất cứ điều gì giá trị của ox100 bên trong của nó, nhưng bây giờ nó sẽ được hiểu như là một số nguyên thay vì một con trỏ. Nhưng bạn cần các diễn viên hoặc người nào khác nó sẽ khiếu nại. [Sinh viên] Vì vậy, bạn có nghĩa là để cast - Là nó sẽ được đúc int int x hoặc đúc của y? [Bowden] gì? [Sinh viên] Okay. Sau khi các dấu ngoặc đơn là có sẽ là một x hoặc ay có? [Bowden] Hoặc. x và y là tương đương. >> [Sinh viên] Okay. Bởi vì họ là cả hai con trỏ. >> Yeah. [Sinh viên] Vì vậy, nó sẽ lưu trữ các hệ thập lục phân 100 ở dạng số nguyên? >> [Bowden] Yeah. Nhưng không phải là giá trị của bất cứ điều gì nó trỏ tới. [Bowden] Yeah. >> [Sinh viên] Vì vậy, chỉ cần địa chỉ ở dạng số nguyên. Okay. [Bowden] Nếu bạn muốn đối với một số lý do kỳ lạ, bạn hoàn toàn có thể đối phó với con trỏ và không bao giờ đối phó với số nguyên và chỉ cần như int * x = 0. Sau đó, bạn sẽ nhận được thực sự bối rối khi con trỏ số học bắt đầu xảy ra. Vì vậy, những con số mà họ lưu trữ là vô nghĩa. Đó chỉ là cách bạn kết thúc giải thích chúng. Vì vậy, tôi là miễn phí để sao chép ox100 từ một int * đến một int, và tôi là miễn phí để gán - phổ cập như có thể sẽ bị mắng không đúc - Tôi là miễn phí để chỉ định một cái gì đó giống như (int *) ox1234 vào int * tùy ý. Vì vậy, ox123 chỉ là một địa chỉ hợp lệ bộ nhớ như là & y. & Y xảy ra để trở về một cái gì đó là khá nhiều ox123. [Sinh viên] mà có thể là một cách thực sự mát mẻ để đi từ hệ thập lục phân dạng thập phân, giống như nếu bạn có một con trỏ và bạn cast nó như là một int? [Bowden] Bạn thực sự chỉ có thể in bằng cách sử dụng giống như printf. Hãy nói rằng tôi có int y = 100. Vì vậy, printf (% d \ n - như bạn đã nên biết - in rằng, cũng như một số nguyên, x%. Chúng tôi sẽ chỉ in nó như là hệ thập lục phân. Vì vậy, một con trỏ không được lưu trữ như hệ thập lục phân, và một số nguyên không được lưu trữ là số thập phân. Tất cả mọi thứ được lưu trữ như nhị phân. Nó chỉ là chúng ta có xu hướng hiển thị con trỏ như là hệ thập lục phân bởi vì chúng tôi nghĩ rằng những thứ trong các khối 4-byte, và địa chỉ bộ nhớ có xu hướng là quen thuộc. Chúng tôi đang như thế nào, nếu nó bắt đầu với bf, sau đó nó sẽ xảy ra trên stack. Vì vậy, nó chỉ là giải thích của chúng ta về con trỏ như là hệ thập lục phân. Okay. Bất kỳ câu hỏi cuối cùng? Tôi sẽ ở đây cho một chút sau khi nếu bạn có bất cứ điều gì khác. Và đó là kết thúc đó. [Sinh viên] Yay! [Vỗ tay] [CS50.TV]