6.4.1 Gán giá trị cho biến con trỏ
Giá trị của biến con trỏ chính là địa chỉ của biến được nó trỏ tới, do đó phép gán giá trị cho biến con trỏ có thể thực hiện theo nhiều cách, bao gồm gán địa chỉ của biến giá trị, gán giá trị của biến con trỏ khác và gán giá trị NULL (Ví dụ 6.6).
Ví dụ 6.6. Gán giá trị cho biến con trỏ
Xét các câu lệnh:
int var = 5, *ptr1, *ptr2, *ptr3; //Khai báo biến giá trị và các biến con trỏ
ptr1 = &var; //Địa chỉ của var được lưu trong biến ptr1 ptr2 = ptr1; //Giá trị của biến ptr1 được gán cho biến ptr2 ptr3 = 0; //Giá trị của biến ptr3 là NULL
Nhìn chung, các biểu thức có chứa con trỏ tuân theo quy tắc như các biểu thức khác trong ngôn ngữ C/C++. Điều quan trọng cần chú ý là phải gán giá trị cho biến con trỏ trước khi sử dụng chúng; nếu không chúng có thể trỏ đến một giá trị không xác định nào đó.
110
6.4.2 Phép toán số học con trỏ
Biến con trỏ là một loại biến đặc biệt với giá trị là địa chỉ trong bộ nhớ (thuộc kiểu số nguyên không dấu – không âm). Do đó, các phép toán số học có thể áp dụng với biến con trỏ là phép cộng và trừ các số không âm mà thôi.
Bảng 6.1. Các phép toán số học con trỏ (ptr là biến con trỏ trỏ tới biến số nguyên var)
++ptr_var or ptr_var++ Trỏ đến số nguyên đứng ngay sau var --ptr_var or ptr_var-- Trỏ đến số nguyên đứng ngay trước var ptr_var + i Trỏ đến số nguyên thứ i sau var
ptr_var - i Trỏ đến số nguyên thứ i trước var ++*ptr_var or (*ptr_var)++ Tương đương với var++
Ví dụ 6.7. Phép toán số học con trỏ
Xét ba câu lệnh:
int var, *ptr; //Giả sử var, ptr lưu tại địa chỉ 1000 và 1100
ptr = &var; //Giá trị của ptr là 1000
ptr++; //Giá trị mới của ptr là 1002
Giá trị sau cùng của biến con trỏ ptr là 1002 chứ không phải 1001 là do kích thước của một số nguyên là 2 byte. Điều này có nghĩa là ptr trỏ đến một số nguyên được lưu tại địa chỉ 1002. Mỗi khi ptr được tăng lên, nó sẽ trỏ đến số nguyên kế tiếp và bởi vì các số nguyên là 2 bytes, ptr sẽ được tăng trị là 2. Điều này cũng tương tự với phép toán giảm trị.
6.4.3 So sánh con trỏ
Hai con trỏ có thể được so sánh trong một biểu thức quan hệ. Tuy nhiên, điều này chỉ có thể nếu cả hai biến này đều trỏ đến các biến có cùng kiểu dữ liệu. Trong trường
ptr_a và ptr_b là hai biến con trỏ trỏ đến các phần tử dữ liệu a và b cùng kiểu, Bảng 6.2 liệt kê một số phép so sánh có thể thực hiện:
111 ptr_a < ptr_b Trả về giá trị true nếu a được lưu trữ ở vị trí trước b
ptr_a > ptr_b Trả về giá trị true nếu a được lưu trữ ở vị trí sau b
ptr_a <= ptr_b Trả về giá trị true nếu a được lưu trữ ở vị trí trước b hoặc ptr_a và ptr_b trỏ đến cùng một vị trí
ptr_a >= ptr_b Trả về giá trị true nếu a được lưu trữ ở vị trí sau b hoặc ptr_a và ptr_b trỏ đến cùng một vị trí
ptr_a == ptr_b Trả về giá trị true nếu cả hai con trỏ ptr_a và ptr_b trỏ đến cùng một phần tử dữ liệu
ptr_a != ptr_b Trả về giá trị true nếu cả hai con trỏ ptr_a và ptr_b trỏ đến các phần tử dữ liệu khác nhau nhưng có cùng kiểu dữ liệu
ptr_a == NULL Trả về giá trị true nếu ptr_a được gán giá trị NULL (0)
6.4.4 Con trỏ và mảng một chiều
Tên của một mảng thật ra là một con trỏ trỏ đến phần tử đầu tiên của mảng đó. Vì vậy, nếu a là một mảng một chiều, thì địa chỉ của phần tử đầu tiên trong mảng (a[0]) có thể được biểu diễn là &a[0] hoặc đơn giản chỉ là a. Tương tự, địa chỉ của phần tử mảng thứ hai có thể được viết như &a[1] hoặc a + 1, … Tổng quát, địa chỉ của phần tử mảng thứ (i + 1) có thể được biểu diễn là &a[i] hay (a + i). Ví dụ 6.8 xuất giá trị và địa chỉ của các phần tử trong mảng một chiều. Ví dụ 6.8. Con trỏ và mảng một chiều #include<stdio.h> int main(){ int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int i; for (i = 0; i < 10; i ++){
printf("i = %d , a[i] = %d , *(a+i) = %d, ", i, a[i], *(a+i)); printf("&a[i] = %X , a+i = %X\n", &a[i], a+i);
}
getchar(); return 0; }
Chương trình trên định nghĩa mảng một chiều a, có 10 phần tử kiểu số nguyên, các phần tử mảng được gán giá trị tương ứng là 1, 2, ..10. Vòng lặp for được dùng để hiển thị giá trị và địa chỉ tương ứng của mỗi phần tử mảng. Chú ý rằng, giá trị của mỗi phần tử
112
được xác định theo hai cách khác nhau, a[i] và *(a+i), nhằm minh họa sự tương đương của chúng. Tương tự, địa chỉ của mỗi phần tử mảng cũng được hiển thị theo hai cách. Kết quả của chương trình trên như sau:
Khi gán một giá trị cho một phần tử mảng như a[i], vế trái của lệnh gán có thể được viết là a[i] hoặc *(a+i). Vì vậy, một giá trị có thể được gán trực tiếp đến một phần tử mảng hoặc nó có thể được gán đến vùng nhớ mà địa chỉ của nó là phần tử mảng. Đôi khi cần thiết phải gán một địa chỉ đến một định danh. Trong những trường hợp như vậy, một con trỏ phải xuất hiện trong vế trái của câu lệnh gán. Không thể gán một địa chỉ tùy ý cho một tên mảng hoặc một phần tử của mảng. Vì vậy, các biểu thức như a, (a+i) và
&a[i] không thể xuất hiện trong vế trái của một câu lệnh gán. Hơn thế nữa, địa chỉ của một mảng không thể thay đổi một cách tùy ý, vì thế các biểu thức như a++ là không được phép. Lý do là vì: a là địa chỉ của mảng a và khi mảng được khai báo, địa chỉ mảng đã được xác định và không thay đổi; việc cố gắng tăng/giảm địa chỉ mảng là điều vô nghĩa.