>> [MUSIC CHƠI]
>> DOUG LLOYD: Con trỏ, ở đây chúng tôi đang có.
Điều này có lẽ sẽ là chủ đề khó khăn nhất
rằng chúng ta nói về trong CS50.
Và nếu bạn đã đọc bất cứ điều gì về con trỏ
trước khi bạn có thể là một chút đáng sợ đi vào video này.
Đó là sự thật các con trỏ làm cho phép bạn khả năng
để có thể vít lên khá nặng khi bạn
làm việc với các biến, và dữ liệu, và gây ra chương trình của bạn để sụp đổ.
Nhưng chúng thực sự hữu ích và họ cho phép chúng ta một cách thực sự tuyệt vời
để truyền dữ liệu trở lại và ra giữa các chức năng,
rằng chúng tôi khác không thể làm được.
>> Và vì vậy những gì chúng ta thực sự muốn làm ở đây là tàu
bạn phải có kỷ luật con trỏ tốt, vì vậy mà bạn có thể sử dụng con trỏ một cách hiệu quả
để làm cho chương trình của bạn là tốt hơn nhiều.
Như tôi đã nói con trỏ cho chúng ta một khác nhau cách để truyền dữ liệu giữa các chức năng.
Bây giờ nếu bạn nhớ lại từ một video trước đó, khi
chúng tôi đã nói về phạm vi biến, tôi đã đề cập
rằng tất cả các dữ liệu mà chúng tôi vượt qua giữa chức năng trong C là tham trị.
Và tôi có thể không đã sử dụng mà hạn, những gì tôi có nghĩa là có
là chúng tôi đang đi qua các bản sao của dữ liệu.
Khi chúng tôi vượt qua một biến cho một chức năng, chúng ta không thực sự đi qua các biến
đến chức năng, phải không?
Chúng tôi đang đi qua một bản sao của rằng dữ liệu cho hàm.
Chức năng làm những gì nó sẽ và nó tính toán một số giá trị,
và có lẽ chúng ta sử dụng giá trị đó khi nó mang lại cho nó trở lại.
>> Có một ngoại lệ quy tắc này đi ngang qua giá trị,
và chúng tôi sẽ trở lại với những gì mà là một chút sau này trong video này.
Nếu chúng ta sử dụng con trỏ thay của việc sử dụng các biến,
hoặc thay vì sử dụng các biến bản thân hoặc bản sao của các biến,
bây giờ chúng tôi có thể vượt qua các biến xung quanh giữa các chức năng một cách khác nhau.
Điều này có nghĩa rằng nếu chúng ta một sự thay đổi trong một chức năng,
sự thay đổi đó sẽ thực sự mất ảnh hưởng trong một chức năng khác nhau.
Một lần nữa, đây là một cái gì đó chúng tôi không thể làm được trước đó,
và nếu bạn đã bao giờ cố gắng để trao đổi giá trị của hai biến trong một hàm,
bạn đã nhận thấy vấn đề này loại leo lên, phải không?
>> Nếu chúng tôi muốn trao đổi X và Y, và chúng tôi vượt qua họ để một chức năng gọi là trao đổi,
bên trong của hàm trao đổi biến làm giá trị trao đổi.
Một trở thành hai, hai thành một, nhưng chúng tôi không thực sự
thay đổi bất cứ điều gì trong bản gốc chức năng, trong người gọi.
Bởi vì chúng ta không có thể, chúng tôi chỉ làm việc với các bản sao của chúng.
Với con trỏ mặc dù, chúng ta có thể thực sự vượt qua X và Y để một chức năng.
Đó là chức năng có thể làm một cái gì đó với họ.
Và những biến các giá trị thực sự có thể thay đổi.
Vì vậy, đó là khá một sự thay đổi trong khả năng của chúng tôi để làm việc với dữ liệu.
>> Trước khi chúng ta đi sâu vào con trỏ, tôi nghĩ rằng nó có giá trị
tham gia một vài phút để quay trở lại vấn đề cơ bản ở đây.
Và có một cái nhìn như thế nào công trình bộ nhớ máy tính
vì hai đối tượng này đang đi để thực sự là khá tương quan với nhau.
Như bạn đã biết, trên hệ thống máy tính của bạn
bạn có một ổ đĩa cứng hoặc có lẽ một ổ trạng thái rắn,
một số loại vị trí lưu trữ tập tin.
Nó thường là một nơi nào đó trong khu phố của 250 gigabytes
để có thể một vài terabyte bây giờ.
Và đó là nơi mà tất cả các bạn các tập tin cuối cùng sống,
ngay cả khi máy tính của bạn được đóng tắt, bạn có thể biến nó trở lại
và bạn sẽ tìm thấy các tập tin của bạn đang có một lần nữa khi bạn khởi động lại hệ thống của bạn.
Nhưng các ổ đĩa, giống như một ổ đĩa cứng, một ổ cứng, hoặc một ổ đĩa trạng thái rắn, ổ SSD,
là không gian lưu trữ chỉ.
>> Chúng tôi có thể không thực sự làm bất cứ điều gì với các dữ liệu đó là trong đĩa cứng,
hoặc trong một ổ đĩa trạng thái rắn.
Để thực sự thay đổi dữ liệu hoặc di chuyển nó xung quanh,
chúng ta phải di chuyển nó đến RAM, bộ nhớ truy cập ngẫu nhiên.
Bây giờ RAM, bạn có rất nhiều ít trong máy tính của bạn.
Bạn có thể có một nơi nào đó trong khu phố của 512 MB
nếu bạn có một máy tính cũ, để có thể hai, bốn, tám, 16,
thậm chí có một chút hơn, GB bộ nhớ RAM.
Vì vậy, đó là nhỏ hơn nhiều, nhưng đó là nơi mà tất cả các dữ liệu dễ bay hơi tồn tại.
Đó là nơi mà chúng ta có thể thay đổi mọi thứ.
Nhưng khi chúng ta bật máy tính của chúng tôi ra, tất cả các dữ liệu trong bộ nhớ RAM bị phá hủy.
>> Vì vậy, đó là lý do tại sao chúng ta cần phải có đĩa cứng cho các vị trí lâu dài hơn của nó,
để nó exists- nó sẽ được thực sự xấu nếu mỗi lần chúng tôi
bật máy tính của chúng tôi ra, mỗi tập tin trong hệ thống của chúng tôi đã được xóa sạch.
Vì vậy, chúng tôi làm việc bên trong bộ nhớ RAM.
Và mỗi khi chúng ta đang nói về bộ nhớ, khá nhiều, trong CS50,
chúng ta đang nói về bộ nhớ RAM, ổ cứng không.
>> Vì vậy, khi chúng tôi di chuyển mọi thứ vào bộ nhớ, nó chiếm một số tiền nhất định của không gian.
Tất cả các loại dữ liệu mà chúng tôi đã làm việc với
mất nhau lượng của không gian trong bộ nhớ RAM.
Vì vậy, mỗi khi bạn tạo ra một số nguyên biến, bốn byte của bộ nhớ
được đặt sang một bên trong bộ nhớ RAM, do đó bạn có thể làm việc với số nguyên đó.
Bạn có thể khai báo các số nguyên, thay đổi nó, gán cho nó
đến một giá trị tăng lên 10 một, vv và vv.
Tất cả những gì cần phải xảy ra RAM, và bạn nhận được bốn byte
để làm việc với cho mỗi nguyên mà bạn tạo ra.
>> Mỗi nhân vật bạn tạo được một byte.
Đó chỉ là bao nhiêu không gian cần thiết để lưu trữ một nhân vật.
Mỗi float, một thực tế số, được bốn byte
trừ khi đó là một đôi độ chính xác điểm nổi
số lượng, mà cho phép bạn có chữ số chính xác hơn hoặc nhiều hơn
sau dấu thập phân mà không làm mất độ chính xác,
mà mất đến tám byte của bộ nhớ.
Chờ đợi lâu, số nguyên thực sự lớn, cũng mất tám byte của bộ nhớ.
Có bao nhiêu byte bộ nhớ làm dây mất?
Vâng chúng ta hãy đặt một chân trong câu hỏi đó cho bây giờ, nhưng chúng tôi sẽ trở lại với nó.
>> Vì vậy, trở lại ý tưởng này của bộ nhớ như một mảng lớn của các tế bào byte kích thước.
Đó thực sự là tất cả nó là, nó chỉ là một mảng rất lớn của các tế bào,
giống như các mảng khác mà bạn đã quen thuộc với và xem,
trừ mọi phần tử là rộng một byte.
Và cũng giống như một mảng, mỗi phần tử có một địa chỉ.
Mỗi phần tử của một mảng có một chỉ số, và chúng tôi
có thể sử dụng chỉ số đó để làm cái gọi là truy cập ngẫu nhiên trên mảng.
Chúng tôi không cần phải bắt đầu từ đầu của mảng,
lặp qua mỗi yếu tố duy nhất của chúng,
để tìm thấy những gì chúng tôi đang tìm kiếm.
Chúng tôi chỉ có thể nói, tôi muốn để có được các Yếu tố thứ 15 hoặc các yếu tố thứ 100.
Và bạn chỉ có thể vượt qua trong số đó và nhận được những giá trị mà bạn đang tìm kiếm.
>> Tương tự như vậy mỗi địa điểm trong bộ nhớ có địa chỉ.
Vì vậy, bộ nhớ của bạn có thể giống như thế này.
Dưới đây là một đoạn rất nhỏ bộ nhớ, điều này là 20 byte của bộ nhớ.
20 byte đầu tiên bởi vì tôi địa chỉ có ở phía dưới
là 0, 1, 2, 3, và như vậy trên tất cả các con đường lên đến 19.
Và khi tôi khai báo các biến và khi tôi bắt đầu làm việc với họ,
hệ thống sẽ thiết lập dành một không gian đối với tôi
trong bộ nhớ này để làm việc với các biến số của tôi.
Vì vậy, tôi có thể nói, char c bằng vốn H. Và điều gì sẽ xảy ra?
Vâng hệ thống sẽ dành cho tôi một byte.
Trong trường hợp này nó đã chọn số byte bốn, các byte tại địa chỉ bốn,
và nó sẽ lưu trữ H vốn thư trong đó cho tôi.
Nếu tôi sau đó nói int tốc độ hạn bằng 65, đó là
sẽ dành bốn byte của bộ nhớ đối với tôi.
Và nó sẽ đối xử với những người bốn byte như là một đơn vị duy nhất
bởi vì những gì chúng tôi đang làm việc với là một số nguyên ở đây.
Và nó sẽ lưu trữ 65 trong đó.
>> Bây giờ đã được tôi là loại nói cho bạn một chút của một lời nói dối,
đúng, bởi vì chúng ta biết rằng máy tính làm việc trong hệ nhị phân.
Họ không hiểu nhất thiết những gì một H vốn là
hoặc những gì một là 65, họ chỉ hiểu nhị phân, số không và những người thân.
Và do đó, thực tế những gì chúng tôi đang lưu trữ trong đó
không phải là H thư và số 65, mà là cơ quan đại diện nhị phân
của chúng, mà nhìn một chút gì đó như thế này.
Và đặc biệt trong bối cảnh của biến số nguyên,
nó không phải đi để chỉ nhổ nó vào, nó sẽ không đối xử với nó như là một trong bốn
byte đoạn nhất thiết, nó thực sự đi
để xử lý nó như một khối bốn byte, mà có thể giống như thế này.
Và ngay cả điều này không phải là hoàn toàn đúng, hoặc,
vì cái gì đó gọi một endianness, mà chúng tôi không
sẽ nhận được vào lúc này, nhưng nếu bạn đang tò mò về,
bạn có thể đọc lên trên ít và endianness lớn.
Nhưng vì lợi ích của lập luận này, vì lợi ích của video này,
chúng ta hãy giả định rằng, trong Thực tế, làm thế nào số 65 sẽ
được đại diện trong bộ nhớ trên mỗi hệ thống,
mặc dù nó không hoàn toàn đúng.
>> Nhưng chúng ta hãy thực sự chỉ có được thoát khỏi tất cả nhị phân hoàn toàn,
và chỉ nghĩ về như H và 65, nó dễ dàng hơn rất nhiều
để suy nghĩ về nó như thế mà như một con người.
Tất cả các quyền, do đó, nó cũng có vẻ như có thể một ít ngẫu nhiên mà I've- hệ thống của tôi
đã không cho tôi byte 5, 6, 7, và 8 để lưu trữ các số nguyên.
Có một lý do cho rằng, quá, mà chúng tôi sẽ không nhận được vào ngay bây giờ, nhưng đủ
nó nói rằng những gì máy tính đang làm gì ở đây
có lẽ là một động thái tốt về phần mình.
Để không cho tôi nhớ đó là nhất thiết phải trở lại để trở lại.
Mặc dù nó sẽ làm ngay bây giờ nếu tôi muốn có được một chuỗi khác,
gọi tên họ, và tôi muốn để đưa Lloyd trong đó.
Tôi sẽ cần phải phù hợp với một nhân vật, mỗi lá thư của đó
sẽ đòi hỏi một nhân vật, một byte của bộ nhớ.
Vì vậy, nếu tôi có thể đưa Lloyd vào mảng của tôi như thế này tôi khá tốt để đi, phải không?
Cái gì còn thiếu?
>> Hãy nhớ rằng mỗi chuỗi chúng ta làm việc với trong C kết thúc với dấu gạch chéo ngược bằng không,
và chúng ta không thể bỏ qua ở đây, hoặc.
Chúng tôi cần dành một byte của bộ nhớ để giữ đó vì vậy chúng tôi
biết khi chuỗi của chúng tôi đã kết thúc.
Vì vậy, một lần nữa sắp xếp này các cách thức điều
xuất hiện trong bộ nhớ sức là một chút ngẫu nhiên,
nhưng nó thực sự là như thế nào hầu hết các hệ thống được thiết kế.
Để xếp chúng vào bội bốn, vì những lý do nữa
rằng chúng ta không cần phải nhận được vào ngay bây giờ.
Nhưng điều này, vì vậy nó đủ để nói rằng sau ba dòng mã,
đây là những gì bộ nhớ có thể trông như thế nào.
Nếu tôi cần vị trí bộ nhớ 4, 8, và 12 để giữ dữ liệu của tôi,
đây là những gì bộ nhớ của tôi có thể trông như thế nào.
>> Và chỉ cần được đặc biệt mô phạm ở đây, khi
chúng ta đang nói về bộ nhớ địa chỉ chúng ta thường
làm như vậy bằng cách sử dụng ký hiệu thập lục phân.
Vậy tại sao chúng ta không chuyển đổi tất cả các từ thập phân sang thập lục phân
chỉ vì đó là thường làm thế nào chúng tôi đề cập đến bộ nhớ.
Vì vậy, thay vì thông qua 0 19, những gì chúng tôi có là số không
x zero qua zero x1 ba.
Đó là những 20 byte của bộ nhớ mà chúng tôi đã hoặc chúng tôi đang tìm kiếm ở trong hình ảnh này
ngay tại đây.
>> Vì vậy, tất cả điều đó đang được nói, chúng ta hãy bước ra khỏi bộ nhớ cho một thứ hai
và trở lại con trỏ.
Dưới đây là quan trọng nhất Điều cần nhớ
khi chúng tôi bắt đầu làm việc với con trỏ.
Một con trỏ là gì nhiều hơn một địa chỉ.
Tôi sẽ nói lại một lần nữa bởi vì nó là quan trọng,
một con trỏ là gì nhiều hơn một địa chỉ.
Con trỏ là địa chỉ đến các địa điểm trong bộ nhớ nơi mà các biến sống.
Biết rằng nó sẽ trở thành một hy vọng chút chút dễ dàng hơn để làm việc với họ.
Một điều tôi thích để làm là phải có loại
các biểu đồ trực quan đại diện cho những gì xảy ra với các dòng khác nhau của mã.
Và chúng tôi sẽ làm điều này một vài lần trong con trỏ,
và khi chúng ta nói về năng động cấp phát bộ nhớ là tốt.
Bởi vì tôi nghĩ rằng các sơ đồ có thể đặc biệt hữu ích.
>> Vì vậy, nếu tôi nói ví dụ, int k trong mã của tôi, những gì đang xảy ra?
Vâng những gì đang xảy ra là về cơ bản Tôi nhận được bộ nhớ dành cho tôi,
nhưng tôi thậm chí không thích nghĩ về nó như thế, tôi
thích nghĩ về nó như một chiếc hộp.
Tôi có một hộp và nó màu xanh lá cây vì tôi
có thể đưa các số nguyên trong hộp màu xanh lá cây.
Nếu nó là một nhân vật tôi có thể có một hộp màu xanh.
Nhưng tôi luôn nói, nếu tôi là tạo một hộp có thể giữ nguyên
hộp đó là màu xanh lá cây.
Và tôi có một điểm đánh dấu vĩnh viễn và tôi viết k trên mặt của nó.
Vì vậy, tôi có một hộp gọi là k, vào đó tôi có thể đưa số nguyên.
Vì vậy, khi tôi nói int k, đó là những gì xảy ra trong đầu tôi.
Nếu tôi nói k bằng năm, những gì tôi làm?
Vâng, tôi đang lăm trong hộp, phải.
Điều này là khá dễ dàng, nếu Tôi nói int k, tạo ra một hộp gọi là k.
Nếu tôi nói k bằng 5, đặt năm vào hộp.
Hy vọng rằng đó không phải là quá nhiều của một bước nhảy vọt.
Đây là nơi mà mọi thứ đi một ít thú vị mặc dù.
Nếu tôi nói int * pk, tốt ngay cả nếu tôi không làm biết điều này nhất thiết có nghĩa là,
nó có một cái gì đó rõ ràng để làm với một số nguyên.
Vì vậy, tôi sẽ tô màu hộp này màu xanh lá cây-ish,
Tôi biết nó có một cái gì đó để làm với một số nguyên,
nhưng nó không phải là một số nguyên chính nó, bởi vì nó là một ngôi sao int.
Có cái gì đó hơi khác nhau về nó.
Vì vậy, tham gia của một số nguyên, nhưng nếu không nó
không quá khác nhau từ những gì chúng ta đang nói về.
Đó là một hộp, nhận nó một nhãn, nó mặc một pk nhãn,
và nó có khả năng nắm giữ int sao, bất cứ điều gì người đang có.
Họ có cái gì để làm với số nguyên, rõ ràng.
Dưới đây là những dòng cuối cùng dù.
Nếu tôi nói pk = & k, whoa, những gì vừa xảy ra, phải không?
Vì vậy, số ngẫu nhiên này, dường như ngẫu nhiên số, được ném vào hộp đó.
Tất cả đó là, là pk được địa chỉ của k.
Vì vậy, tôi gắn bó với k sống trong bộ nhớ, địa chỉ, địa chỉ của byte của nó.
Tất cả tôi đang làm là tôi đang nói giá trị đó là những gì tôi sẽ
để đặt bên trong của hộp được gọi là pk của tôi.
Và bởi vì những điều này là con trỏ, và bởi vì tìm kiếm
tại một chuỗi như zero x tám zero c bảy bốn tám
hai số không có lẽ không rất có ý nghĩa.
Khi chúng ta thường hình dung con trỏ, chúng tôi thực sự làm như vậy là con trỏ.
Pk cung cấp cho chúng tôi thông tin chúng ta cần phải tìm k trong bộ nhớ.
Vì vậy, về cơ bản pk có một mũi tên trong đó.
Và nếu chúng ta đi theo chiều dài các mũi tên đó, hãy tưởng tượng
nó là một cái gì đó bạn có thể đi bộ trên, nếu chúng ta đi bộ dọc theo chiều dài của mũi tên,
ở đầu rất của mũi tên đó, chúng tôi sẽ tìm được vị trí trong bộ nhớ
nơi k sống.
Và đó thực sự quan trọng bởi vì một khi chúng ta biết nơi k sống,
chúng ta có thể bắt đầu làm việc với các dữ liệu bên của vị trí bộ nhớ.
Mặc dù chúng tôi đang nhận được một teeny cắn trước mình cho bây giờ.
>> Vì vậy, một con trỏ là gì?
Một con trỏ là một mục dữ liệu mà giá trị là một địa chỉ bộ nhớ.
Đó là zero x tám zero thứ xảy ra, đó là một địa chỉ bộ nhớ.
Đó là một vị trí trong bộ nhớ.
Và các loại của một con trỏ mô tả các loại
dữ liệu mà bạn sẽ tìm thấy tại địa chỉ bộ nhớ.
Vì vậy, có những int sao phần bên phải.
Nếu tôi làm theo mũi tên đó, nó sẽ dẫn tôi đến một địa điểm.
Và vị trí đó, những gì tôi sẽ tìm thấy ở đó trong ví dụ của tôi,
là một hộp màu xanh lá cây.
Đó là một số nguyên, đó là những gì tôi sẽ tìm thấy nếu tôi đi đến địa chỉ đó.
Các kiểu dữ liệu của một con trỏ mô tả những gì
bạn sẽ tìm thấy tại địa chỉ bộ nhớ.
Vì vậy, đây là điều thực sự mát mẻ mặc dù.
Con trỏ cho phép chúng ta vượt qua biến giữa các chức năng.
Và thực sự vượt qua các biến và không vượt qua bản của chúng.
Bởi vì nếu chúng ta biết chính xác nơi trong bộ nhớ để tìm một biến,
chúng ta không cần phải thực hiện một bản sao của nó, chúng ta chỉ có thể đi đến vị trí đó
và làm việc với các biến đó.
Vì vậy, trong bản chất con trỏ loại của làm cho một môi trường máy tính
nhiều hơn như thế giới thực, phải.
>> Vì vậy, đây là một tương tự.
Hãy nói rằng tôi có một máy tính xách tay, phải, và nó đầy đủ các ghi chú.
Và tôi sẽ như bạn để cập nhật nó.
Bạn là một chức năng mà cập nhật ghi chú, phải.
Trên đường đi, chúng tôi đã làm việc cho đến nay, những gì
xảy ra là bạn sẽ mất máy tính xách tay của tôi, bạn sẽ đi đến các cửa hàng sao chép,
bạn sẽ tạo một bản sao của Xerox mỗi trang của các máy tính xách tay.
Bạn sẽ rời khỏi máy tính xách tay của tôi trở lại trên bàn của tôi khi bạn đang thực hiện,
bạn sẽ đi và gạch bỏ những thứ trong tôi máy tính xách tay mà là lỗi thời hoặc sai,
và sau đó bạn sẽ vượt qua trở lại tôi ngăn xếp của các trang Xerox
đó là một bản sao của máy tính xách tay của tôi với những thay đổi mà bạn đã thực hiện cho nó.
Và tại thời điểm đó, nó thuộc vào tôi như chức năng gọi điện thoại, như người gọi,
quyết định để ghi chép lại và tích hợp chúng lại thành máy tính xách tay của tôi.
Vì vậy, có rất nhiều bước liên quan ở đây, phải.
Như thế sẽ không thể tốt hơn nếu tôi chỉ nói, hey, có thể bạn
cập nhật máy tính xách tay của tôi cho tôi, đưa cho bạn máy tính xách tay của tôi,
và bạn có những điều và nghĩa là vượt qua chúng ra
và cập nhật ghi chú của tôi trong máy tính xách tay của tôi.
Và sau đó đưa cho tôi máy tính xách tay của tôi trở lại.
Đó là loại gì con trỏ cho phép chúng ta làm,
họ làm cho môi trường này rất nhiều giống như cách chúng tôi hoạt động trong thực tế.
>> Tất cả các quyền đó là những gì một con trỏ, chúng ta hãy nói
khoảng cách con trỏ làm việc trong C, và làm thế nào chúng ta có thể bắt đầu làm việc với họ.
Vì vậy, có một con trỏ rất đơn giản trong C gọi là con trỏ null.
Các điểm con trỏ null không có gì.
Điều này có thể có vẻ như nó là thực sự không phải là một điều rất hữu ích,
nhưng như chúng ta sẽ thấy một chút sau này, thực tế
rằng con trỏ null này tồn tại thực sự có thể thực sự có ích.
Và bất cứ khi nào bạn tạo ra một con trỏ, và bạn không đặt immediately- giá trị của nó
một ví dụ về thiết lập giá trị của nó ngay lập tức
sẽ có một vài slide lại mà tôi đã nói pk bằng & k,
pk được địa chỉ k, như chúng ta sẽ thấy điều đó có nghĩa,
chúng tôi sẽ xem làm thế nào để mã đó shortly- nếu chúng ta không đặt giá trị là một cái gì đó
có ý nghĩa ngay lập tức, bạn nên luôn luôn
đặt con trỏ của bạn để trỏ đến null.
Bạn nên thiết lập nó để trỏ đến không có gì.
>> Đó là rất khác so với chỉ để lại những giá trị như nó là
và sau đó tuyên bố một con trỏ và chỉ là giả định
nó là null bởi vì đó là hiếm khi thực sự.
Vì vậy, bạn nên luôn luôn đặt giá trị của một con trỏ
để null nếu bạn không đặt giá trị của nó một cái gì đó có ý nghĩa ngay lập tức.
Bạn có thể kiểm tra xem giá trị của một con trỏ là null bằng cách sử dụng toán tử bằng
(==), Giống như bạn so sánh bất kỳ số nguyên giá trị hoặc giá trị ký tự sử dụng (==)
cũng.
Đó là một loại đặc biệt của hằng số giá trị mà bạn có thể sử dụng để kiểm tra.
Vì vậy, đó là một rất đơn giản con trỏ, con trỏ null.
Một cách khác để tạo một con trỏ là để trích xuất
địa chỉ của một biến bạn đã tạo ra,
và bạn làm điều này bằng cách sử dụng & khai thác địa chỉ nhà điều hành.
Mà chúng ta đã thấy trước đây trong sơ đồ ví dụ đầu tiên tôi thấy.
Vì vậy, nếu x là một biến mà chúng tôi đã đã tạo ra các kiểu số nguyên,
sau đó & x là một con trỏ đến một số nguyên.
& x là- nhớ, và sẽ tách địa chỉ của những điều trên bên phải.
Và từ một con trỏ chỉ là một địa chỉ, hơn & x là một con trỏ đến một số nguyên
giá trị mà là nơi mà trong bộ nhớ x cuộc sống.
Đó là địa chỉ của x.
Vì vậy, & x là địa chỉ của x.
Hãy lấy một bước xa hơn và kết nối với một cái gì đó
Tôi ám chỉ trong một video trước.
Nếu arr là một mảng tăng gấp đôi, sau đó & khung vuông arr tôi là một con trỏ
để một đôi.
ĐƯỢC.
arr khung vuông i, nếu arr là một mảng tăng gấp đôi,
sau đó arr khung vuông i là các phần tử thứ i của mảng đó,
và & arr khung vuông i là nơi ở bộ nhớ các phần tử thứ i của arr tồn tại.
>> Vì vậy, hàm ý ở đây là gì?
Một tên mảng, hàm ý của toàn bộ điều này,
là tên của một mảng là thực sự chính nó là một con trỏ.
Bạn đã làm việc với con trỏ tất cả cùng
mỗi thời gian mà bạn đã sử dụng một mảng.
Ghi từ các ví dụ trên phạm vi biến,
gần cuối của đoạn video, tôi trình bày một ví dụ mà chúng ta có một chức năng
gọi là bộ int và một chức năng gọi là mảng tập.
Và thách thức của bạn để xác định hay không, hoặc những gì
giá trị mà chúng tôi in ra kết thúc chức năng,
ở phần cuối của chương trình chính.
>> Nếu bạn nhớ lại từ ví dụ mà hoặc nếu bạn đã xem video,
Bạn có biết rằng khi anh- cuộc gọi đến set int hiệu quả không có gì.
Nhưng các cuộc gọi để thiết lập mảng nào.
Và tôi loại che đậy lý do tại sao đó là trường hợp vào thời điểm đó.
Tôi chỉ nói, tốt nó một mảng, nó đặc biệt, bạn đã biết, có một lý do.
Lý do là một mảng của tên thực sự chỉ là một con trỏ,
và có này đặc biệt cú pháp khung vuông đó
làm cho mọi việc rất nhiều đẹp hơn để làm việc với.
Và họ làm cho các ý tưởng của một con trỏ ít hơn rất nhiều đáng sợ,
và đó là lý do tại sao họ loại của giới theo cách đó.
Nhưng thực sự là mảng chỉ con trỏ.
Và đó là lý do tại sao khi chúng ta thực hiện một sự thay đổi để mảng,
khi chúng tôi đi qua một mảng như là một tham số đến một chức năng hay như một đối số
đến một chức năng, nội dung của mảng thực sự thay đổi trong cả callee
và trong những người gọi.
Mà cho tất cả các loại khác biến chúng tôi thấy là không phải vậy.
Vì vậy, đó chỉ là một cái gì đó để giữ trong tâm trí khi bạn đang làm việc với con trỏ,
là tên của một mảng thực sự là một con trỏ
tới phần tử đầu tiên của mảng đó.
>> OK như vậy bây giờ chúng tôi có tất cả những sự thật, chúng ta hãy tiếp tục đi, phải.
Tại sao chúng ta quan tâm nơi một cái gì đó sống.
Cũng như tôi đã nói, nó khá hữu ích để biết rằng điều gì sống
vì vậy bạn có thể đến đó và thay đổi nó.
Làm việc với nó và thực sự có những điều mà bạn
muốn làm gì để điều đó ảnh hưởng take biến, và không có hiệu lực trên một số bản sao của nó.
Điều này được gọi là dereferencing.
Chúng tôi đi đến các tài liệu tham khảo và chúng ta thay đổi giá trị ở đó.
Vì vậy, nếu chúng ta có một con trỏ và nó được gọi là pc, và nó chỉ vào một nhân vật,
sau đó chúng ta có thể nói * pc và * pc là tên của những gì chúng ta sẽ tìm thấy nếu chúng ta đi
cho pc địa chỉ.
Một nhân vật mà chúng ta sẽ tìm thấy ở đó là và * pc là cách chúng tôi tham khảo các dữ liệu ở đó
vị trí.
Vì vậy, chúng ta có thể nói điều gì đó như * pc = D hoặc một cái gì đó như thế,
và điều đó có nghĩa rằng bất cứ điều gì là tại địa chỉ bộ nhớ máy tính,
bất kỳ nhân vật trước đây đó, bây giờ là D, nếu chúng ta nói * pc = D.
>> Vì vậy, ở đây chúng tôi đi một lần nữa với một số công cụ C lạ, phải.
Vì vậy, chúng tôi đã nhìn thấy * trước đây như là bằng cách nào đó một phần của các kiểu dữ liệu,
và bây giờ nó đang được sử dụng trong một bối cảnh hơi khác nhau
để truy cập vào dữ liệu tại một địa điểm.
Tôi biết đó là một chút bối rối và đó là thực sự là một phần của toàn bộ này
như thế, tại sao con trỏ có thần thoại này xung quanh họ như là quá phức tạp,
là loại một vấn đề cú pháp, trung thực.
Nhưng * được sử dụng trong cả hai trường hợp, cả hai như là một phần của tên loại,
và chúng ta sẽ thấy một chút sau đó một cái gì đó khác nữa.
Và ngay bây giờ là toán tử tham chiếu.
Vì vậy, nó đi vào các tài liệu tham khảo, nó truy cập vào dữ liệu
tại vị trí của con trỏ, và cho phép bạn thao tác nó theo ý thích.
>> Bây giờ điều này là rất tương tự như thăm người hàng xóm của bạn, phải.
Nếu bạn biết những gì bạn người hàng xóm sống, bạn
không đi chơi với người hàng xóm của bạn.
Bạn biết bạn tình cờ biết nơi họ sinh sống,
nhưng điều đó không có nghĩa là bởi nhờ có kiến ​​thức mà
bạn đang tương tác với họ.
Nếu bạn muốn tương tác với họ, bạn phải đi đến nhà của họ,
bạn phải đi đến nơi họ sinh sống.
Và một khi bạn làm điều đó, sau đó bạn có thể tương tác
với họ chỉ thích bạn muốn.
Và tương tự như vậy với các biến, bạn cần phải đi đến địa chỉ của họ
nếu bạn muốn tương tác cho họ, bạn không thể chỉ biết địa chỉ.
Và cách bạn đi đến địa chỉ là sử dụng *, các toán tử tham chiếu.
Bạn nghĩ gì sẽ xảy ra nếu chúng ta cố gắng và tới đích
một con trỏ có giá trị là null?
Nhớ lại rằng null con trỏ trỏ tới không có gì.
Vì vậy, nếu bạn cố gắng và tới đích không có gì hoặc đi đến một địa chỉ không có gì,
Bạn nghĩ gì sẽ xảy ra?
Phân khúc Vâng, nếu bạn đoán lỗi, bạn muốn được quyền.
Nếu bạn cố gắng và tới đích một con trỏ null,
bạn phải chịu một phân khúc lỗi. Nhưng chờ đợi,
tôi không nói với bạn rằng nếu bạn không đi
để thiết lập giá trị của bạn của bạn con trỏ đến một cái gì đó có ý nghĩa,
bạn nên đặt là null?
Tôi đã làm và thực sự các phân khúc lỗi là một loại hành vi tốt.
>> Bạn có bao giờ tuyên bố một biến và không được chỉ định giá trị của nó ngay lập tức?
Vì vậy, bạn chỉ cần nói int x; bạn làm không thực sự gán nó vào bất cứ điều gì
và sau đó vào trong mã của bạn, bạn in ra các giá trị của x,
có vẫn không giao nó cho bất cứ điều gì.
Thường xuyên, bạn sẽ nhận được bằng không, nhưng đôi khi bạn
có thể nhận được một số số ngẫu nhiên, và bạn không có ý tưởng đó đến từ đâu.
Tương tự như vậy có thể điều xảy ra với con trỏ.
Khi bạn khai báo một con trỏ int * pk ví dụ,
và bạn không gán cho nó một giá trị, bạn nhận được bốn byte cho bộ nhớ.
Dù bốn byte của bộ nhớ hệ thống có thể
thấy rằng có một số giá trị có ý nghĩa.
Và có thể có được cái gì đã có mà
là không còn cần thiết khác chức năng, vì vậy bạn chỉ có
bất cứ dữ liệu đã có.
>> Điều gì nếu bạn cố gắng làm tới đích một số địa chỉ mà bạn không- có
đã byte và thông tin trong có, đó là bây giờ trong con trỏ của bạn.
Nếu bạn cố gắng và tới đích của con trỏ, bạn có thể bị rối tung với một số bộ nhớ
rằng bạn không có ý định gây rối với nó tất cả.
Và trong thực tế, bạn có thể làm một cái gì đó thực sự tàn phá,
như phá vỡ một chương trình khác, hoặc phá vỡ chức năng khác,
hoặc làm điều gì đó độc hại bạn không có ý định làm ở tất cả.
Và đó là lý do tại sao nó thực sự là một ý tưởng tốt
để đặt con trỏ của bạn để null nếu bạn không đặt chúng vào một cái gì đó có ý nghĩa.
Đây có thể là tốt hơn ở cuối ngày cho chương trình của bạn
sụp đổ sau đó cho nó để làm một cái gì đó mà vít lên
một chương trình hoặc chức năng khác.
Hành vi đó có lẽ thậm chí ít lý tưởng hơn là chỉ đâm.
Và đó là lý do tại sao nó thực sự là một thói quen tốt
để có được vào để thiết lập con trỏ của bạn để null nếu bạn không đặt chúng
đến một giá trị có ý nghĩa ngay lập tức, một giá trị mà bạn biết
và rằng bạn có thể một cách an toàn tới đích.
>> Vì vậy, chúng ta hãy quay lại bây giờ và có một cái nhìn vào cú pháp tổng thể về tình hình.
Nếu tôi nói int * p ;, những gì đã tôi chỉ thực hiện?
Những gì tôi đã làm điều này là.
Tôi biết giá trị của p là một địa chỉ bởi vì tất cả các con trỏ chỉ
địa chỉ.
Tôi có thể tới đích của p sử dụng các nhà điều hành *.
Trong bối cảnh này ở đây, ở rất đầu nhớ lại * là một phần của các loại.
Int * là các kiểu dữ liệu.
Nhưng tôi có thể tới đích p sử dụng nhà điều hành *,
và nếu tôi làm như vậy, nếu tôi đi đến địa chỉ đó, những gì tôi sẽ tìm thấy tại địa chỉ đó?
Tôi sẽ tìm một số nguyên.
Vì vậy, int * p về cơ bản là nói, p là một địa chỉ.
Tôi có thể tới đích của p và nếu Tôi làm, tôi sẽ tìm thấy một số nguyên
ở vị trí bộ nhớ.
>> OK vì vậy tôi biết có một người khác Điều khó chịu với các ngôi sao
và đây là nơi mà Điều khó chịu với các ngôi sao là.
Bạn đã bao giờ cố gắng để khai báo nhiều biến cùng loại
trên cùng một dòng mã?
Vì vậy, cho một thứ hai, giả vờ rằng các dòng, mã tôi thực sự có có màu xanh lá cây
là không có và nó chỉ nói int x, y, z ;.
Gì mà có thể làm thực sự tạo ra là ba biến số nguyên cho bạn,
một gọi là x, một gọi là y, và một gọi là z.
Đó là một cách để làm điều đó mà không cần phải chia vào ba dòng.
>> Đây là nơi những ngôi sao có được gây phiền nhiễu một lần nữa mặc dù,
vì * thực sự là một phần của cả hai loại tên và một phần
của tên biến.
Và như vậy, nếu tôi nói int * px, py, pz, những gì tôi thực sự có được là một con trỏ đến một số nguyên
gọi là px và hai số nguyên, py và pz.
Và có lẽ đó không phải những gì chúng ta muốn, đó là không tốt.
>> Vì vậy, nếu tôi muốn tạo ra nhiều con trỏ trên cùng một dòng, cùng loại,
và các ngôi sao, những gì tôi thực sự cần làm là nói int * pa, pb *, * pc.
Bây giờ đã chỉ cho biết rằng và bây giờ nói với bạn điều này,
có thể bạn sẽ không bao giờ làm điều này.
Và nó có thể là một điều tốt một cách trung thực, vì có lẽ bạn vô tình
bỏ qua một ngôi sao, một cái gì đó như thế.
Đây có thể là tốt nhất để có thể khai báo con trỏ trên từng đường nét,
nhưng nó chỉ là một số khác của những cú pháp gây phiền nhiễu
điều với các ngôi sao mà làm con trỏ rất khó khăn để làm việc với.
Bởi vì nó chỉ là cú pháp này mess bạn phải làm việc thông qua.
Với thực tế nó không thực sự trở thành bản chất thứ hai.
Tôi vẫn có những sai sót với nó vẫn sau khi lập trình trong 10 năm,
vì vậy đừng buồn nếu một cái gì đó xảy ra cho bạn, nó khá phổ biến một cách trung thực.
Nó thực sự loại một lỗ hổng của cú pháp.
>> OK vì vậy tôi loại hứa rằng chúng tôi sẽ xem xét lại
các khái niệm lớn như thế nào là một chuỗi.
Vâng, nếu tôi nói với bạn rằng một chuỗi, chúng tôi đã thực sự loại
được nói dối bạn toàn bộ thời gian.
Không có loại dữ liệu được gọi là chuỗi, và trong thực tế, tôi
đề cập đến điều này trong một của chúng tôi video sớm nhất về các kiểu dữ liệu,
chuỗi đó là một kiểu dữ liệu mà đã được tạo ra cho bạn trong CS50.h.
Bạn phải #include CS50.h để sử dụng nó.
>> Vâng chuỗi thực sự là chỉ một bí danh cho một cái gì đó
gọi là char *, a con trỏ đến một nhân vật.
Vâng con trỏ, thu hồi, chỉ là địa chỉ.
Vì vậy, kích thước là gì trong byte của một chuỗi?
Vâng đó là bốn hoặc tám.
Và lý do tôi nói bốn hoặc tám là bởi vì nó thực sự
phụ thuộc vào hệ thống, nếu bạn đang sử dụng CS50 ide, char * là kích thước của một char
* Là tám, nó là một hệ thống 64-bit.
Mỗi địa chỉ trong bộ nhớ dài 64 bit.
Nếu bạn đang sử dụng thiết bị CS50 hoặc sử dụng bất kỳ máy tính 32-bit,
và bạn đã nghe nói rằng thời hạn 32-bit máy, một máy 32-bit là gì?
Vâng nó chỉ có nghĩa rằng mỗi địa chỉ trong bộ nhớ dài 32 bit.
Và nên 32 bit là bốn byte.
Vì vậy, một char * là bốn hoặc tám byte tùy thuộc vào hệ thống của bạn.
Và thực sự bất kỳ loại dữ liệu, và một con trỏ đến bất kỳ dữ liệu
gõ, vì tất cả các con trỏ chỉ địa chỉ, bốn hoặc tám byte.
Vì vậy, chúng ta hãy xem lại này sơ đồ và chúng ta hãy kết luận
video này với một bài tập nhỏ ở đây.
Vì vậy, đây là sơ đồ chúng ta rời đi với lúc bắt đầu của video.
Vì vậy, những gì sẽ xảy ra nếu tôi nói * pk = 35?
Vì vậy, nó có nghĩa gì khi tôi nói, * pk = 35?
Mất một giây.
* pk.
Trong bối cảnh ở đây, là * toán tử tham chiếu.
Vì vậy, khi tới đích điều hành được sử dụng,
chúng tôi đi đến địa chỉ được trỏ tới bởi pk, và chúng ta thay đổi những gì chúng tôi tìm thấy.
So * pk = 35 có hiệu quả thực hiện điều này với hình ảnh.
Vì vậy, về cơ bản nó cú pháp giống của có nói k = 35.
>> Một lần nữa.
Nếu tôi nói int m, tôi tạo ra một biến mới gọi là m.
Một hộp mới, đó là một hộp màu xanh lá cây vì nó sẽ giữ một số nguyên,
và nó có nhãn m.
Nếu tôi nói m = 4, tôi đặt một số nguyên vào hộp đó.
Nếu nói pk = & m, làm thế nào sơ đồ này thay đổi?
Pk = & m, làm bạn nhớ lại những gì & Điều hành nào hay được gọi là?
Hãy nhớ rằng một số tên biến & là địa chỉ của một tên biến.
Vì vậy, những gì chúng ta đang nói là pk có được địa chỉ của m.
Và như vậy có hiệu quả những gì xảy ra sơ đồ là pk điểm không còn
k, nhưng điểm đến m.
>> Một lần nữa con trỏ là rất khó khăn để làm việc với
và họ mất rất nhiều thực hành, nhưng vì
khả năng của mình để cho phép bạn để truyền dữ liệu giữa các chức năng
và thực sự có những thay đổi có hiệu lực,
việc đầu của bạn xung quanh là thực sự quan trọng.
Nó có lẽ là phức tạp nhất chủ đề được thảo luận ở CS50,
nhưng giá trị mà bạn có được từ việc sử dụng con trỏ
xa giá trị hơn các biến chứng mà đến từ việc học chúng.
Vì vậy, tôi muốn bạn tốt nhất của may mắn học hỏi về con trỏ.
Tôi Doug Lloyd, đây là CS50.