2009-06-30 40 views
5

Tôi tương đối mới đối với C++ (khoảng một năm kinh nghiệm, bật và tắt). Tôi tò mò về những gì đã dẫn đến quyết định của type * name làm cú pháp để xác định con trỏ. Dường như với tôi rằng cú pháp phải là type & name vì biểu tượng & được sử dụng ở mọi nơi khác trong mã để tham chiếu đến địa chỉ bộ nhớ của biến. Vì vậy, để sử dụng ví dụ truyền thống của int con trỏ:Tại sao chúng ta sử dụng "type * var" thay vì "type & var" khi xác định con trỏ?

int a = 1; 
int * b = &a; 

sẽ trở thành

int a = 1; 
int & b = &a 

tôi chắc chắn rằng có một số lý do cho điều này mà tôi chỉ không nhìn thấy, và tôi rất muốn nghe một số đầu vào từ các cựu chiến binh C++.

Cảm ơn, -S

+2

Không được nói "C/C++". C và C++ là các ngôn ngữ khác nhau. –

+0

Thay đổi nó, nhưng không con trỏ làm việc theo cùng một cách trong cả hai? – spencewah

+0

C không có tham chiếu. –

Trả lời

7

C++ thông qua cú pháp C. Như được tiết lộ trong "The Development of the C Language" (bởi Dennis Ritchie) C sử dụng * cho con trỏ trong khai báo kiểu vì nó đã được quyết định rằng loại cú pháp nên làm theo sử dụng.

Đối với mỗi đối tượng của [một loại hợp chất], đã có cách đề cập đến đối tượng bên dưới: chỉ mục mảng, gọi hàm, sử dụng toán tử gián tiếp [*] trên con trỏ. Lý do tương tự dẫn đến một cú pháp khai báo cho các tên phản ánh cú pháp biểu thức trong đó các tên thường xuất hiện. Do đó,

int i, *pi, **ppi; 

khai báo số nguyên, con trỏ đến số nguyên, con trỏ trỏ đến số nguyên. Cú pháp của các khai báo này phản ánh quan sát rằng i, * pi, và ** ppi đều mang lại một kiểu int khi được sử dụng trong một biểu thức.

Dưới đây là một ví dụ phức tạp hơn:

int *(*foo)[4][]; 

khai Điều này có nghĩa một biểu *(*foo)[4][0] có kiểu int, và từ đó (và rằng [] có độ ưu tiên cao hơn unary *), bạn có thể giải mã các loại: foo là một con trỏ tới một mảng có kích thước 4 của mảng con trỏ tới ints.

Cú pháp này được áp dụng trong C++ để tương thích với C. Ngoài ra, đừng quên rằng C++ có sử dụng cho & trong các khai báo.

int & b = a; 

Dòng trên có nghĩa là biến tham chiếu tham chiếu đến một biến loại int. Sự khác biệt giữa tham chiếu và con trỏ gần như là các tham chiếu được khởi tạo chỉ, và bạn không thể thay đổi vị trí của chúng, và cuối cùng chúng luôn tự động bị bỏ qua.

int x = 5, y = 10; 
int& r = x; 

int sum = r + y; // you do not need to say '*r' automatically dereferenced. 

r = y; // WRONG, 'r' can only have one thing pointing at during its life, only at its infancy ;) 
+0

Cảm ơn bạn, tôi đã đưa ra một số giả định không chính xác về tài liệu tham khảo và con trỏ (chủ yếu là chúng là một trong những giống nhau, khi chúng không). – spencewah

1

ví dụ thứ hai của bạn không phải là mã C có giá trị, chỉ có C++. Sự khác biệt là một là một con trỏ, trong khi khác là một tham chiếu.

Ở phía bên tay phải, '&' luôn có nghĩa là địa chỉ. Trong một định nghĩa nó chỉ ra rằng biến là một tham chiếu.

Ở bên phải '*' luôn có nghĩa là giá trị tại địa chỉ. Trong một định nghĩa nó chỉ ra rằng biến là một con trỏ.

Tham chiếu và con trỏ giống nhau, nhưng không giống nhau. Điều này article giải quyết sự khác biệt.

+0

Tôi không nghĩ rằng khối thứ hai là * được cho là mã pháp lý - đó là một ví dụ giả định. –

+0

Cảm ơn bạn, bài viết (và sự định hướng của tài liệu tham khảo và con trỏ) là hữu ích. – spencewah

+0

Ví dụ thứ hai là hợp lệ trong C và C++. Bạn không sử dụng toán tử địa chỉ để ràng buộc một tham chiếu đến một giá trị. –

1

Thay vì đọc int* b là "b là một con trỏ đến int", đọc nó như int *b: "* b là một int". Sau đó, bạn có & làm chống *: * b là một int. Địa chỉ của *b&*b hoặc chỉ b.

-2

Tôi nghĩ câu trả lời có thể là "bởi vì đó là cách K & R đã làm."

Bỏ qua kẻ ngu. Giải thích tại sao tôi sai.

K & R là những người đã quyết định cú pháp C để khai báo con trỏ là gì.

Nó không phải là int & x; thay vì int * x; bởi vì đó là cách ngôn ngữ được xác định bởi những kẻ đã làm cho nó lên - K & R.

3

Nếu bạn là một nhà tư tưởng thị giác, nó có thể giúp để tưởng tượng dấu hoa thị như một lỗ đen dẫn đến giá trị dữ liệu . Do đó, nó là một con trỏ.

Dấu và là đầu đối diện của lỗ, coi đó là một dấu hoa thị làm sáng tỏ hoặc tàu vũ trụ lắc lư trong một khóa học thất thường khi phi công vượt qua quá trình chuyển đổi ra khỏi hố đen.

Tôi nhớ rất bối rối bởi C++ quá tải ý nghĩa của dấu và, để cung cấp cho chúng tôi tài liệu tham khảo. Trong nỗ lực tuyệt vọng của họ để tránh sử dụng thêm bất kỳ nhân vật nào, được biện minh bởi khán giả quốc tế sử dụng C và các vấn đề đã biết với giới hạn bàn phím, họ thêm vào một nguồn gây nhầm lẫn lớn.

Một điều có thể hữu ích trong C++ là suy nghĩ về các tham chiếu dưới dạng các con trỏ được chuẩn bị trước. Thay vì sử dụng & someVariable khi bạn vượt qua trong một đối số, bạn đã sử dụng dấu và sau khi bạn đã xác định someVariable. Sau đó, một lần nữa, điều đó có thể gây nhầm lẫn cho bạn thêm nữa!

Một trong những vật cưng của tôi ghét, mà tôi không hài lòng để xem ban hành trong các mẫu Objective-C của Apple, là bố trí theo phong cách int *someIntPointer thay vì int* someIntPointer

IMHO, giữ dấu hoa thị với biến là một C old-fashioned phương pháp tiếp cận nhấn mạnh các cơ chế về cách bạn xác định biến, trên kiểu dữ liệu của nó.

Kiểu dữ liệu của someIntPointer theo nghĩa đen là một con trỏ đến số nguyên và khai báo phải phản ánh điều đó. Điều này dẫn đến các yêu cầu mà bạn khai báo một biến trên mỗi dòng, để tránh lỗi vi tế như:

int* a, b; // b is a straight int, was that our intention? 

int *a, *b; // old-style C declaring two pointers 

int* a; 
int* b; // b is another pointer to an int 

Trong khi người cho rằng khả năng để khai báo con trỏ và các giá trị hỗn hợp trên cùng một dòng, cố ý, là một mạnh mẽ tính năng, tôi đã nhìn thấy nó dẫn đến lỗi và sự nhầm lẫn tinh tế.

+0

+1, nhận xét tuyệt vời về cú pháp khai báo. –

4

Tôi nghĩ rằng Dennis Ritchie đã trả lời này trong The Development of the C Language:

Đối với mỗi đối tượng của một sáng tác loại như vậy, đã có một cách để đề cập đến các đối tượng cơ bản: chỉ số mảng, gọi chức năng, sử dụng toán tử vô hướng trên con trỏ. Lý do tương tự dẫn đến cú pháp khai báo để sao chép tên cú pháp biểu thức trong đó các tên thường xuất hiện. Như vậy,

int i, *pi, **ppi; 

tuyên bố một số nguyên, một con trỏ đến một số nguyên , một con trỏ đến một con trỏ đến một số nguyên . Cú pháp của các khai báo này phản ánh quan sát rằng i, * pi và ** ppi đều mang lại một loại int int khi được sử dụng trong một biểu thức. Tương tự,

int f(), *f(), (*f)(); 

tuyên bố một hàm trả về một số nguyên , một hàm trả về một con trỏ đến một số nguyên, một con trỏ tới một hàm trả lại một số nguyên;

int *api[10], (*pai)[10]; 

khai báo một mảng của các con trỏ tới số nguyên, và một con trỏ đến một mảng của số nguyên. Trong tất cả các trường hợp, khai báo của một biến tương tự như cách sử dụng của nó trong một biểu thức có loại là tên được đặt ở đầu tuyên bố .

Vì vậy, chúng tôi sử dụng type * var để khai báo con trỏ vì điều này cho phép khai báo phản ánh việc sử dụng (dereferencing) của con trỏ.

Trong bài viết này, Ritchie cũng kể lại rằng trong "NB", một phiên bản mở rộng của "B" ngôn ngữ lập trình, ông đã sử dụng int pointer[] để khai báo một con trỏ đến một int, như trái ngược với int array[10] để khai báo một mảng của int s .

+0

Thật vậy. Và làm int i, & i; nó sẽ là bất hợp pháp, bởi vì & tôi sẽ không mang lại lợi nhuận. Đó là lý do tại sao nó không sử dụng & i in C và như vậy trong C++. Đáng buồn thay, C + + đã phá vỡ với đối xứng này, và thực hiện int &a = i; mã hợp lệ, thậm chí tho "& i" không mang lại int :( –

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