Sự khác biệt giữa các biến và con trỏ là gì?


10

Khi đọc một bài viết phác thảo sự khác biệt trong OO và lập trình hàm tôi đã bắt gặp các con trỏ hàm. Đã được một thời gian kể từ khi tôi hoàn thành bằng Khoa học Máy tính (2003) và vì vậy tôi đã tìm kiếm các con trỏ để làm mới bộ nhớ của mình.

Con trỏ là các biến chứa tham chiếu đến địa chỉ bộ nhớ. Chúng có thể được coi là trỏ đến dữ liệu được chứa trong địa chỉ bộ nhớ đó nếu dữ liệu đó tồn tại. Hoặc, như trong trường hợp trong bài viết, họ có thể chỉ ra điểm vào một phần của mã và có thể được sử dụng để gọi mã đó.

Tại sao điều này khác với một biến? Biến là tên tượng trưng cho địa chỉ bộ nhớ và trình biên dịch sẽ thay thế tên bằng địa chỉ thực tế. Điều này có nghĩa là các biến chứa tham chiếu đến các vị trí bộ nhớ và có thể được xem xét để trỏ đến dữ liệu tại địa chỉ đó nếu dữ liệu đó tồn tại.

Nếu sự khác biệt là trong hành vi (có thể một con trỏ không thể được gán lại trong thời gian chạy hoặc chỉ có thể được gán một tên biến tượng trưng, ​​không phải bất kỳ giá trị nào khác) không có nghĩa đó chỉ là một biến của một loại cụ thể, loại con trỏ? Theo cùng một cách, một biến được khai báo là kiểu nguyên bị hạn chế bởi trình biên dịch trong những gì nó có thể được sử dụng cho.

Tôi đang thiếu gì ở đây?


4
Không có gì, một con trỏ thực sự là một loại có giải nghĩa ngữ nghĩa là nội dung của biến là một địa chỉ. Tất nhiên lưu ý rằng có hai địa chỉ, có địa chỉ của con trỏ (tức là nơi lưu trữ biến) và địa chỉ tham chiếu con trỏ (dữ liệu thực tế tại địa chỉ của biến). Một con trỏ là một loại tham chiếu .
Luke Mathieson

Câu trả lời:


8

Câu hỏi của bạn thú vị theo nhiều cách, vì nó đòi hỏi sự phân biệt cẩn thận cho một số vấn đề. Nhưng tầm nhìn của bạn đối với tôi về cơ bản là chính xác. Tôi đã không đọc tài liệu tham khảo của bạn trước khi viết hầu hết câu trả lời này để tránh thiên vị câu trả lời của tôi.

Đầu tiên, tuyên bố của bạn Variables are symbolic names for memory addresses, nó gần như đúng, nhưng nhầm lẫn giữa khái niệm và cách thực hiện thông thường của nó. Một biến thực sự chỉ là một thùng chứa có thể chứa một giá trị có thể thay đổi. Thông thường, bộ chứa này được triển khai trên máy tính như một bock của không gian bộ nhớ, được đặc trưng bởi và địa chỉ và kích thước do các biến có thể chứa đối tượng yêu cầu biểu diễn với nhiều thông tin hơn hoặc ít hơn.

Nhưng tôi sẽ xem xét chủ yếu là một quan điểm trừu tượng hơn về ngữ nghĩa của các ngôn ngữ, độc lập với các kỹ thuật thực hiện.

Vì vậy, các biến chỉ là container từ một quan điểm trừu tượng. Một container như vậy không cần phải có tên. Tuy nhiên, các ngôn ngữ thường có các biến được đặt tên bằng cách liên kết với nó một định danh, do đó việc sử dụng biến có thể được biểu thị bằng mã định danh. Một biến thực sự có thể có một số định danh thông qua cơ chế răng cưa khác nhau. Một biến cũng có thể là một phần con của một biến lớn hơn: một ví dụ là một ô của một biến mảng, có thể được đặt tên bằng cách chỉ định biến mảng và chỉ mục của ô, nhưng cũng có thể được liên kết với các định danh thông qua bí danh.

Tôi đang cố tình sử dụng bộ chứa từ phần trung tính, để tránh gọi các từ khác có thể được tải về mặt kỹ thuật. Nó thực sự gần với khái niệm tham chiếu được mô tả trong wilipedia , thường bị nhầm lẫn với một địa chỉ bộ nhớ. Bản thân con trỏ từ thường được hiểu là một địa chỉ bộ nhớ, nhưng tôi không nghĩ nó có ý nghĩa khi xem xét hầu hết các ngôn ngữ cấp cao và có thể không phù hợp trong tài liệu thảo luận mà bạn đề cập (mặc dù địa chỉ có thể được sử dụng), vì nó không phù hợp đề cập đến một thực hiện cụ thể. Tuy nhiên, nó phù hợp với một ngôn ngữ như C, được cho là gần gũi hơn nhiều với các khái niệm triển khai và kiến ​​trúc máy.

Trên thực tế, nếu bạn xem xét các biến hoặc giá trị ở cấp độ triển khai, có thể có một số hệ thống gián tiếp phức tạp, "con trỏ cấp độ máy", nhưng đó là (và nên) vô hình đối với người dùng, do đó, quan điểm trừu tượng Tôi phát triển có thể hợp lệ. Đối với hầu hết các ngôn ngữ lập trình, người dùng không cần phải lo lắng, hoặc thậm chí biết về việc triển khai, vì việc triển khai có thể thay đổi rất nhiều đối với một ngôn ngữ nhất định. Điều này có thể không đúng với một số ngôn ngữ, chẳng hạn như C, có chủ ý gần với kiến ​​trúc máy, như một sự thay thế nâng cao cho các ngôn ngữ lắp ráp có liên quan gần như trực tiếp đến mã hóa nhị phân rõ ràng, nhưng mức độ quá thấp để sử dụng thuận tiện trong hầu hết tình huống.

Những gì người sử dụng ngôn ngữ nên biết, và đôi khi nó thậm chí còn ít hơn thế, đó là giá trị và các hoạt động liên quan, nơi chúng có thể được chứa, cách chúng có thể được liên kết với tên, cách hệ thống đặt tên hoạt động, làm thế nào mới các loại giá trị được xác định, vv

Vì vậy, một khái niệm quan trọng khác là định danh và đặt tên. Việc đặt tên một thực thể (một giá trị) có thể được thực hiện bằng cách liên kết một định danh với một giá trị (thường là trong một khai báo). Nhưng một giá trị cũng có thể đạt được bằng cách áp dụng các hoạt động cho các giá trị được đặt tên khác. Tên có thể được sử dụng lại và có các quy tắc (quy tắc phạm vi) để xác định những gì được liên kết với một định danh nhất định, theo bối cảnh sử dụng. Ngoài ra còn có các tên đặc biệt, được gọi là lứa, để đặt tên cho các giá trị của một số tên miền, chẳng hạn như số nguyên (ví dụ ) hoặc boolean (ví dụ: true ).612

Sự kết hợp của một giá trị không thay đổi với một định danh thường được gọi là hằng số. Litterals là hằng số theo nghĩa đó.

"Các thùng chứa giá trị" cũng có thể được coi là các giá trị và liên kết của chúng với một mã định danh là một biến theo nghĩa "ngây thơ" thông thường mà bạn đang sử dụng. Vì vậy, bạn có thể nói rằng một biến là "hằng số chứa".

Bây giờ bạn có thể tự hỏi sự khác biệt giữa việc liên kết một định danh với một giá trị (khai báo không đổi) hoặc gán một giá trị cho một biến, tức là lưu trữ giá trị trong vùng chứa được xác định là hằng số chứa. Về cơ bản, khai báo có thể được xem như là một hoạt động xác định ký hiệu, liên kết một định danh là một thực thể cú pháp với một giá trị nào đó là một thực thể ngữ nghĩa. Chuyển nhượng là một hoạt động ngữ nghĩa thuần túy sửa đổi một trạng thái, tức là sửa đổi giá trị của một container. Theo một nghĩa nào đó, khai báo là một khái niệm meta không có hiệu ứng ngữ nghĩa, ngoài việc cung cấp một cơ chế đặt tên (tức là cú pháp) cho các thực thể ngữ nghĩa.

Trên thực tế, các bài tập là các hoạt động ngữ nghĩa diễn ra linh hoạt khi chương trình được thực thi, trong khi các khai báo có tính chất cú pháp hơn và thường được diễn giải trong văn bản của chương trình, độc lập với thực thi. Đây là lý do tại sao phạm vi tĩnh (tức là phạm vi văn bản) thường là cách tự nhiên để hiểu ý nghĩa của định danh.

Sau tất cả những điều này, tôi có thể nói rằng một giá trị con trỏ chỉ là một tên khác cho một container và một biến con trỏ là một biến chứa, tức là một container (hằng số) có thể chứa một container khác (với những hạn chế có thể có trong trò chơi chứa bởi một số loại hệ thống).

Về mã, bạn nêu [pointers] might indicate the entry point to a section of code and can be used to call that code. Thật ra điều này không hoàn toàn đúng. Một phần của mã thường là vô nghĩa một mình (từ quan điểm cấp cao hoặc triển khai). Từ quan điểm cấp cao, mã thường chứa các định danh và bạn phải diễn giải các định danh này trong ngữ cảnh tĩnh nơi chúng được khai báo. Nhưng thực sự có một sự trùng lặp có thể có của cùng một bối cảnh tĩnh, về cơ bản là do đệ quy là hiện tượng động (thời gian chạy) và mã chỉ có thể được thực thi trong một thể hiện động thích hợp của bối cảnh tĩnh. Điều này hơi phức tạp, nhưng hậu quả là khái niệm đúng là một bao đóng liên kết một đoạn mã và môi trường nơi các định danh sẽ được giải thích. Việc đóng cửa là khái niệm ngữ nghĩa thích hợp, nghĩa là một giá trị ngữ nghĩa có thể xác định đúng. Sau đó, bạn có thể có hằng số đóng, biến đóng,

Hàm là một bao đóng, thường có một số tham số để xác định hoặc khởi tạo một số thực thể của nó (hằng và biến).

Tôi đang bỏ qua nhiều biến thể về việc sử dụng các cơ chế này.

Đóng có thể được sử dụng để xác định cấu trúc OO trong các ngôn ngữ bắt buộc hoặc chức năng. Trên thực tế, công việc ban đầu về phong cách OO (có thể là trước tên) đã được thực hiện theo cách đó.

Bài viết mà bạn tham khảo, mà tôi đọc lướt qua nhanh chóng, dường như là một bài thú vị, được viết bởi một người có thẩm quyền, nhưng có thể không dễ đọc nếu bạn không có kinh nghiệm đáng kể với nhiều ngôn ngữ và mô hình tính toán cơ bản của họ.

Nhưng hãy nhớ rằng: nhiều điều nằm trong mắt của kẻ si tình, miễn là anh ta giữ được quan điểm nhất quán. Quan điểm có thể khác nhau.

Điều này có trả lời câu hỏi của bạn không?

PS: Đây là một câu trả lời dài. Nếu bạn xem xét một số phần của nó không đầy đủ, xin vui lòng rõ ràng về nó là gì. Cảm ơn bạn.


Tôi đã phải đọc qua nó một vài lần nhưng tôi nghĩ nó trả lời câu hỏi của tôi, vâng. Mặc dù các định nghĩa của tôi quá tập trung vào thực thi, ý tưởng rằng một con trỏ là một loại biến cụ thể dường như là chính xác, nghĩa là "một giá trị con trỏ chỉ là một tên khác cho một container và một biến con trỏ là một biến chứa" Trong đó tôi không nhận được sự phân biệt mà bạn thực hiện là giữa một con trỏ chứa địa chỉ của một khối mã và là một thùng chứa chứa một thùng chứa có chứa một bao đóng. Là sự phân biệt giữa bối cảnh tĩnh và của một chương trình trong thực thi?
NectarSoft

1
@NectarSoft Sự khác biệt chỉ là giữa khối mã và bao đóng. Điều tôi đang nói là, tự nó, tách biệt với bất kỳ bối cảnh nào, một khối mã thường có nghĩa là không có gì. Nếu tôi nói với bạn rằng " nếu mome raths lớn hơn borogove, thì toves outgrabe ", câu có nghĩa là không có gì vì bạn bỏ lỡ bối cảnh nơi tất cả các khái niệm này được định nghĩa. Bối cảnh tĩnh này thường là văn bản kèm theo đoạn mã. Vấn đề là bối cảnh tĩnh này có thể có một vài trường hợp thời gian chạy và đoạn mã phải chỉ tham chiếu đến một trong số chúng.
babou

1
@NectarSoft (tiếp theo) Vì vậy, giá trị đóng là liên kết của đoạn mã và một thể hiện động của bối cảnh tĩnh mang lại ý nghĩa cho đoạn này. Một liên kết của cùng một đoạn mã với một thể hiện động khác nhau của cùng một bối cảnh tĩnh cung cấp một bao đóng khác nhau. Thông thường, mã của bạn có thể sử dụng một biến thuộc về bối cảnh thứ, nhưng nó sẽ là một biến khác nhau cho mỗi phiên bản động của bối cảnh, mặc dù tên của nó (được xác định tĩnh) vẫn giữ nguyên. Điều này có làm rõ vấn đề không, hay tôi nên xây dựng một ví dụ trong câu trả lời (định dạng bình luận bị hạn chế)?
babou

Nó làm rõ vấn đề, cảm ơn, trong khi đưa ra các câu hỏi khác mà tôi sẽ dành thời gian để suy nghĩ. Ví dụ: nếu một con trỏ riêng biệt được yêu cầu cho mỗi lần đóng, có vẻ như con trỏ trở thành một phần của bối cảnh động và do đó thuộc về bao đóng! Ngoài ra tôi tự hỏi về ranh giới đóng và phân cấp đóng, vì mỗi bối cảnh được liên kết với một khối mã tĩnh và được tham chiếu bởi một con trỏ sẽ nhất thiết phải là một biến trong một bao đóng của chính nó. Tuy nhiên, tất cả điều này không đúng chủ đề, vì vậy tôi sẽ đọc một số câu hỏi và có thể đặt ra một câu hỏi khác khi tôi gặp khó khăn một lần nữa
NectarSoft

@NectarSoft Thật ra. Khi bạn xây dựng một bao đóng, bạn cố gắng hạn chế ngữ cảnh liên quan đến mã đến những gì cần thiết để đưa ra ý nghĩa đúng cho mã đó (tối đa một số ràng buộc thực tế để tránh thông tin quản lý vi mô). Điều này có thể được quyết định tĩnh.
babou

2

Sự khác biệt là theo định nghĩa và ứng dụng, một con trỏ là một biến chuyên biệt để giữ địa chỉ bộ nhớ của một biến khác; trong thuật ngữ OO, một con trỏ có thể được coi là kế thừa hành vi của nó từ một lớp chung gọi là biến.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.