Xin lỗi vì đã sử dụng assembly để giải thích điều này nhưng tôi nghĩ đây là cách tốt nhất để hiểu cách tham chiếu được thực hiện bởi trình biên dịch.
#include <iostream>
using namespace std;
int main()
{
int i = 10;
int *ptrToI = &i;
int &refToI = i;
cout << "i = " << i << "\n";
cout << "&i = " << &i << "\n";
cout << "ptrToI = " << ptrToI << "\n";
cout << "*ptrToI = " << *ptrToI << "\n";
cout << "&ptrToI = " << &ptrToI << "\n";
cout << "refToNum = " << refToI << "\n";
//cout << "*refToNum = " << *refToI << "\n";
cout << "&refToNum = " << &refToI << "\n";
return 0;
}
Output của mã này là như thế này
i = 10
&i = 0xbf9e52f8
ptrToI = 0xbf9e52f8
*ptrToI = 10
&ptrToI = 0xbf9e52f4
refToNum = 10
&refToNum = 0xbf9e52f8
Cho phép xem xét tháo gỡ (tôi đã sử dụng GDB cho việc này. 8,9 và 10 ở đây là những con số dòng mã)
8 int i = 10;
0x08048698 <main()+18>: movl $0xa,-0x10(%ebp)
Ở đây $0xa
là 10 (số thập phân) mà chúng tôi đang gán cho i
. -0x10(%ebp)
ở đây có nghĩa là nội dung của ebp register
–16 (thập phân). -0x10(%ebp)
trỏ đến địa chỉ i
trên ngăn xếp.
9 int *ptrToI = &i;
0x0804869f <main()+25>: lea -0x10(%ebp),%eax
0x080486a2 <main()+28>: mov %eax,-0x14(%ebp)
Chỉ định địa chỉ i
to ptrToI
. ptrToI
một lần nữa trên ngăn xếp nằm tại địa chỉ -0x14(%ebp)
, đó là ebp
- 20 (thập phân).
10 int &refToI = i;
0x080486a5 <main()+31>: lea -0x10(%ebp),%eax
0x080486a8 <main()+34>: mov %eax,-0xc(%ebp)
Bây giờ ở đây là bắt! So sánh việc tháo gỡ dòng 9 và 10 và bạn sẽ quan sát rằng, -0x14(%ebp)
được thay thế bằng -0xc(%ebp)
trong dòng số 10. -0xc(%ebp)
là địa chỉ của refToNum. Nó được cấp phát trên stack. Nhưng bạn sẽ không bao giờ có thể lấy địa chỉ này từ mã của bạn vì bạn không bắt buộc phải biết địa chỉ.
Vì vậy; một tham chiếu không chiếm bộ nhớ. Trong trường hợp này, nó là bộ nhớ ngăn xếp vì chúng ta đã phân bổ nó như là một biến cục bộ. Bộ nhớ chiếm bao nhiêu bộ nhớ? Con trỏ chiếm nhiều.
Bây giờ, hãy xem cách chúng tôi truy cập tham chiếu và con trỏ. Để đơn giản, tôi đã chỉ hiển thị một phần của đoạn mã lắp ráp
16 cout << "*ptrToI = " << *ptrToI << "\n";
0x08048746 <main()+192>: mov -0x14(%ebp),%eax
0x08048749 <main()+195>: mov (%eax),%ebx
19 cout << "refToNum = " << refToI << "\n";
0x080487b0 <main()+298>: mov -0xc(%ebp),%eax
0x080487b3 <main()+301>: mov (%eax),%ebx
Bây giờ, so sánh hai dòng trên, bạn sẽ thấy sự giống nhau nổi bật. -0xc(%ebp)
là địa chỉ thực tế của refToI
mà bạn không bao giờ có thể truy cập được. Nói một cách đơn giản, nếu bạn nghĩ tham chiếu như một con trỏ bình thường, thì việc truy cập một tham chiếu giống như tìm nạp giá trị tại địa chỉ được trỏ tới bởi tham chiếu. Có nghĩa là hai dòng dưới đây của mã sẽ cung cấp cho bạn kết quả tương tự
cout << "Value if i = " << *ptrToI << "\n";
cout << " Value if i = " << refToI << "\n";
Bây giờ so sánh này
15 cout << "ptrToI = " << ptrToI << "\n";
0x08048713 <main()+141>: mov -0x14(%ebp),%ebx
21 cout << "&refToNum = " << &refToI << "\n";
0x080487fb <main()+373>: mov -0xc(%ebp),%eax
Tôi đoán bạn có thể nhận ra những gì đang xảy ra ở đây. Nếu bạn yêu cầu &refToI
, nội dung của -0xc(%ebp)
vị trí địa chỉ được trả lại và -0xc(%ebp)
là nơi refToi
cư trú và nội dung của nó không là gì ngoài địa chỉ của i
.
Điều cuối cùng, Tại sao dòng này lại nhận xét?
//cout << "*refToNum = " << *refToI << "\n";
Vì không được phép *refToI
và nó sẽ cho bạn lỗi thời gian biên dịch.
"Tôi cảm thấy rằng sự hiểu biết sâu bên dưới mức đó sẽ làm cho tôi hiểu con trỏ khái niệm so với tham chiếu tốt hơn" Tôi không nghĩ điều đó sẽ giúp ích gì. –
Bạn có thể thử điều này: http://eetimes.com/discussion/programming-pointers/4023307/References-vs-Pointers – Vlad
@ MR.Anubis ..... bạn có ý gì? – howtechstuffworks