Để bắt đầu ta sẽ có 1 ví dụ:
float a = 3.6;
Theo khai báo trên, máy sẽ cấp phát cho biến a một khoảng nhớ gồm 4 bytes liên tiếp, địa chỉ của biến a là địa chỉ của ô nhớ đầu tiên trong khoảng nhớ.
Địa chỉ của biến là một số nguyên nhưng không được đánh đồng nó với các số nguyên thông thường dùng trong các phép tính.
Rõ ràng, địa chỉ của 2 biến kiểu int liên tiếp cách nhau 2 bytes, địa chỉ của 2 biến kiểu float liên tiếp cách nhau 4 bytes,... Nên máy sẽ phân biệt các kiểu địa chỉ: địa chỉ kiểu int, kiểu float, kiểu double,...
Phép toán &a cho ta địa chỉ của biến a.
1. Con trỏ:
- Là một biến dùng để chứa địa chỉ, vì có nhiều loại địa chỉ nên cũng có nhiều loại con trỏ tương ứng.
- Cũng giống như bất kỳ biến nào khác, con trỏ phải được khai báo trước khi sử dụng.
- Cú pháp khai báo: kiểu *<tên con trỏ>;
Ví dụ câu lệnh:
int x, y, *px, *py;
khai báo hai biến kiểu int x, y và hai con trỏ kiểu int là px, py.
Khi đã có khai báo trên thì các câu lệnh:
px = &x;
py = &y;
hoàn toàn xác định, câu lệnh thứ 1 sẽ gán địa chỉ của biến x cho con trỏ px và câu lệnh thứ 2 sẽ gán địa chỉ của biến y cho con trỏ py.
- Khi con trỏ px chứa địa chỉ của biến x thì ta nói px trỏ tới x.
2. Quy tắc sử dụng con trỏ: Ta có thể sử dụng tên con trỏ hoặc dạng khai báo của nó trong các biểu thức, ví dụ: đối với con trỏ px, ta có thể sử dụng cách viết px (tên con trỏ) và *px (dạng khai báo của con trỏ).
a. Sử dụng tên con trỏ: Con trỏ cũng là 1 biến nên khi tên của nó xuất hiện trong biểu thức thì giá trị của nó sẽ được sử dụng trong biểu thức này. Chỉ có điều cần có lưu ý ở đây: giá trị của một con trỏ là địa chỉ của một biến nào đó. Khi con trỏ đứng bên trái của một toán tử gán thì giá trị của biểu thức bên phải phải là một địa chỉ.
Ví dụ câu lệnh:
float a, *p, *q;
p = &a;
q = p;
Câu lệnh thứ 1 khai báo một biến a kiểu float và hai con trỏ p, q kiểu float.
Câu lệnh thứ 2 sẽ gán địa chỉ của biến a cho con trỏ p.
Câu lệnh thứ 3 sẽ gán giá trị của p cho q.
Kết quả là con trỏ q chứa địa chỉ của biến a.
Lưu ý: Câu lệnh p = a là sai :)
b. Sử dụng dạng khai báo của con trỏ: Như đã biết, sau khi thực hiện các câu lệnh:
float x, y, z, *px, *py;
px = &x;
py = &y;
thì px trỏ tới x, py trỏ tới y. Bây giờ ta bàn đến ý nghĩa của cách viết:
*px và *py
Điều này được phát biểu trong một nguyên lý rất ngắn gọn như sau:
Nếu con trỏ px trỏ tới x, thì các cách viết:
x và *px
là tương đương trong mọi ngữ cảnh.
Theo nguyên lý này thì 3 câu lệnh sau đều có hiệu lực như nhau:
y = x + z;
*py = x + z;
*py = *px + z;
Từ đây có thể rút ra 1 kết luận: Khi biết được địa chỉ của 1 biến thì chẳng những ta có thể sử dụng giá trị của nó mà có thể gán cho nó 1 giá trị mới.
3. Hàm có đối số con trỏ:
void HoanVi(float *px, float *py)
{
float z;
z = *px;
*px = *py;
*py = z;
}
void main()
{
float a = 6.9, b = 9.6;
HoanVi(&a, &b);
printf("a = %0.2f, b = %0.2f", a, b);
getch();
}
Chúc bạn học tốt!