2015-04-02 21 views
9

Tôi vẫn tự hỏi tại sao trong C bạn không thể chỉ đơn giản là thiết lập một cái gì đó là một điều khác bằng cách sử dụng các biến đơn giản. Một biến chính nó là một con trỏ đến dữ liệu, phải không? Vậy tại sao con trỏ trỏ đến dữ liệu trong biến khi bạn chỉ có thể sử dụng biến ban đầu? Là nó để truy cập vào bit cụ thể (hoặc byte, tôi đoán) của dữ liệu trong biến nói?Tại sao sử dụng con trỏ trong C?

Tôi chắc chắn nó hợp lý, tuy nhiên tôi chưa bao giờ hoàn toàn nắm bắt được khái niệm và khi đọc mã nhìn thấy *pointers luôn ném tôi đi.

+0

Khi học tôi tìm thấy nó dễ dàng hơn để nhớ rằng con trỏ chỉ là các biến mà giữ một địa chỉ bộ nhớ không có vấn đề như thế nào phức tạp nó trông. Vì vậy, 'char *** p' là khủng khiếp để suy nghĩ về nhưng cuối cùng nó chỉ giữ một địa chỉ bộ nhớ duy nhất. – teppic

+4

Điều gì xảy ra nếu 'biến ban đầu' là bộ đệm mạng 10MB? Làm thế nào bạn sẽ vượt qua đó vào một chức năng, sao chép nó? Làm thế nào bạn sẽ xếp hàng nó vào một chủ đề khác, sao chép nó hai lần? –

+0

@MartinJames gợi ý hay. – Gewure

Trả lời

13

Một biến chính nó là một con trỏ đến dữ liệu

Không, nó không phải là. Biến đại diện cho một đối tượng, một lvalue. Khái niệm về lvalue về cơ bản khác với khái niệm về con trỏ . Bạn dường như đang trộn lẫn cả hai.

Trong C, không thể "rebind" một giá trị để làm cho nó "trỏ" đến một vị trí khác trong bộ nhớ. Sự ràng buộc giữa các giá trị và vị trí bộ nhớ của chúng được xác định và cố định tại thời gian biên dịch. Nó không phải lúc nào cũng là 100% cụ thể (ví dụ: vị trí tuyệt đối của biến cục bộ không được biết tại thời gian biên dịch), nhưng nó đủ cụ thể để làm cho nó không thể điều chỉnh theo thời gian chạy.

Toàn bộ ý tưởng về con trỏ là giá trị của nó thường được xác định tại thời gian chạy và có thể được thực hiện để trỏ đến các vị trí bộ nhớ khác nhau trong thời gian chạy.

7

Không, biến không phải là con trỏ đến dữ liệu. Nếu bạn khai báo hai số nguyên với int x, y;, thì không có cách nào để tạo xy đề cập đến cùng một điều; chúng riêng biệt.

Bất cứ khi nào bạn đọc hoặc ghi từ một biến, máy tính của bạn phải xác định vị trí chính xác của biến đó trong bộ nhớ máy tính của bạn. Máy tính của bạn sẽ xem xét mã bạn đã viết và sử dụng để xác định vị trí của biến. Một con trỏ có thể biểu diễn tình huống mà vị trí không được biết tại thời điểm bạn biên dịch mã của mình; địa chỉ chính xác chỉ được tính sau khi bạn thực sự chạy mã của mình.

Nếu bạn không được phép sử dụng con trỏ hoặc mảng, mọi dòng mã bạn viết sẽ phải truy cập các biến cụ thể được biết tại thời gian biên dịch. Bạn không thể viết một đoạn mã chung đọc và ghi từ các vị trí khác nhau trong bộ nhớ được chỉ định bởi người gọi. Lưu ý: Bạn cũng có thể sử dụng các mảng có chỉ mục biến để truy cập các biến có vị trí không được biết tại thời gian biên dịch, nhưng các mảng chủ yếu chỉ là đường cú pháp cho con trỏ. Bạn có thể suy nghĩ về tất cả các hoạt động mảng về hoạt động của con trỏ để thay thế. Mảng không linh hoạt như con trỏ.

Thông báo khác: Như AnT chỉ ra, vị trí của biến cục bộ thường nằm trên ngăn xếp, do đó, chúng là một loại biến nơi vị trí không được biết tại thời gian biên dịch. Nhưng lý do duy nhất mà ngăn xếp hoạt động để lưu trữ biến cục bộ trong hàm reentrant là trình biên dịch của bạn triển khai con trỏ ẩn được gọi là con trỏ ngăn xếp và/hoặc con trỏ khung và chức năng sử dụng các con trỏ này để tìm ra phần nào của bộ nhớ chứa đối số của chúng và biến cục bộ. Con trỏ hữu ích đến nỗi trình biên dịch thực sự sử dụng chúng phía sau lưng mà không nói cho bạn biết.

5

Một nơi phổ biến nơi con trỏ hữu ích là khi bạn đang viết hàm. Các hàm nhận đối số của chúng 'theo giá trị', có nghĩa là chúng nhận được một bản sao của những gì được truyền vào và nếu một hàm gán một giá trị mới cho một trong các đối số của nó sẽ không ảnh hưởng đến người gọi. Điều này có nghĩa rằng bạn không thể viết một "nhân đôi" chức năng như thế này:

void doubling(int x) 
{ 
    x = x * 2; 
} 

này có ý nghĩa bởi vì nếu không điều gì sẽ chương trình làm gì nếu bạn gọi tăng gấp đôi như thế này:

doubling(5); 

Pointers cung cấp một công cụ để giải quyết vấn đề này bởi vì chúng cho phép bạn viết các hàm lấy địa chỉ của một biến, ví dụ:

void doubling2(int *x) 
{ 
    (*x) = (*x) * 2; 
} 

Hàm ở trên lấy địa chỉ của một số nguyên a s đối số của nó. Một dòng trong các hàm dereferences body function có địa chỉ hai lần: ở phía bên tay trái của dấu bằng chúng ta đang lưu trữ vào địa chỉ đó và bên phải chúng ta đang nhận giá trị nguyên từ địa chỉ đó và nhân nó với 2 Kết quả cuối cùng là giá trị tìm thấy tại địa chỉ đó hiện đã tăng gấp đôi. Ngoài ra, khi chúng tôi muốn gọi hàm mới này, chúng tôi không thể chuyển giá trị bằng chữ (ví dụ: doubling2(5)) vì nó sẽ không biên dịch vì chúng tôi không cung cấp địa chỉ cho hàm đúng cách. Một cách để cung cấp cho nó một địa chỉ sẽ trông như thế này:

int a = 5; 
doubling2(&a); 

Kết quả cuối cùng này sẽ là biến a của chúng tôi sẽ chứa 10

+0

tôi thích câu trả lời này tốt nhất. của nó thẳng về phía trước và ví dụ là tối giản nhưng rất tốt. – Gewure

3

Một lý do khác: C được thiết kế để xây dựng hệ điều hành và rất nhiều mã cấp thấp liên quan đến phần cứng. Mỗi phần cứng trưng ra giao diện của nó bằng các thanh ghi, và trên gần như tất cả các kiến ​​trúc, các thanh ghi được ánh xạ vào không gian bộ nhớ CPU, và chúng không phải luôn ở cùng một địa chỉ (nhờ cài đặt jumper, PnP, autoconfig, và như vậy trên)

vì vậy, các nhà văn OS, khi viết một trình điều khiển ví dụ, cần một cách để đối phó với những gì có vẻ vị trí bộ nhớ, chỉ là họ không tham khảo các tế bào bộ nhớ RAM.

Pointers phục vụ cho mục đích này bằng cách cho phép các nhà văn OS để xác định những gì vị trí bộ nhớ người đó muốn truy cập vào.

Các vấn đề liên quan