Tóm tắt bài học
8.3 Các toán tử con trỏ
Có hai toán tử đặc biệt được dùng với con trỏ: * và &. Toán tử & là một toán tử một ngôi và nó trả về địa chỉ của toán hạng. Ví dụ,
var2 = &var1;
lấy địa chỉ vùng nhớ của biến var1 gán cho var2. Địa chỉ này là vị trí ô nhớ bên trong máy tính của biến var1 và nó không làm gì với giá trị của var1. Toán tử & có thể hiểu là trả về “địa chỉ của”. Vì vậy, phép gán trên có nghĩa là “var2 nhận địa chỉ của var1”. Trở lại, giá trị của var1 là 500 và nó dùng vùng nhớ 1000 để lưu giá trị này. Sau phép gán trên, var2 sẽ có giá trị 1000.
Toán tử thứ hai, toán tử *, được dùng với con trỏ là phần bù, bổ xung của toán tử &, toán tử *.
Nó là một toán tử một ngôi và trả về giá trị chứa trong vùng nhớ được trỏ bởi giá trị của biến con trỏ.
Xem ví dụ trước, ở đó var1 có giá trị 500 và được lưu trong vùng nhớ 1000, sau câu lệnh var2 = &var1;
var2 chứa giá trị 1000, và sau lệnh gán temp = *var2;
temp sẽ chứa 500 không phải là 1000. Toán tử * có thể được hiểu là “tại địa chỉ”.
Cả hai toán tử * và & có độ ưu tiên cao hơn tất cả các toán tử toán học ngoại trừ toán tử lấy giá trị âm. Chúng có cùng độ ưu tiên với toán tử lấy giá trị âm (-).
Chương trình dưới đây in ra giá trị của một biến kiểu số nguyên, địa chỉ của nó được lưu trong một biến con trỏ, và chương trình cũng in ra địa chỉ của biến con trỏ.
#include <stdio.h>
void main() {
int var = 500, *ptr_var;
/* var is declared as an integer and ptr_var as a pointer pointing to an integer */
ptr_var = &var; /*stores address of var in ptr_var*/
/* Prints value of variable (var) and address where var is stored */
printf(“The value %d is stored at address %u:”, var, &var);
/* Prints value stored in ptr variable (ptr_var) and address where ptr_var is stored */
printf(“\nThe value %u is stored at address: %u”,
ptr_var, &ptr_var);
/* Prints value of variable (var) and address where var is stored, using pointer to variable */
printf(“\nThe value %d is stored at address:%u”, *ptr_var, ptr_var);
}
Một ví dụ về kết quả thực thi chương trình như sau:
The value 500 is stored at address: 65500 The value 65500 is stored at address: 65502
The value 500 is stored at address: 65500
Trong ví dụ trên, ptr_var chứa địa chỉ 65500, là địa chỉ vùng nhớ lưu trữ giá trị của var. Nội dung ô nhớ 65500 này có thể lấy được bằng cách sử dụng toán tử *, như *ptr_var. Lúc này
*ptr_var trình bày tương ứng với giá trị 500, là giá trị của var. Bởi vì ptr_var cũng là một biến, nên địa chỉ của nó có thể được in ra bằng toán tử &. Trong ví dụ trên, ptr_var được lưu tại địa chỉ 65502. Mã quy cách %u chỉ định cách in giá trị các tham số theo kiểu số nguyên không dấu (unsigned int).
Nhớ lại là, một biến kiểu số nguyên chiếm 2 bytes bộ nhớ. Vì vậy, giá trị của var được lưu trữ tại địa chỉ 65500 và trình biên dịch cấp phát ô nhớ kế tiếp 65502 cho ptr_var. Tương tự, một số thập phân kiểu float yêu cầu 4 bytes và kiểu double yêu cầu 8 bytes. Các biến con trỏ lưu trữ một giá trị nguyên. Với hầu hết các chương trình sử dụng con trỏ, kiểu con trỏ có thể xem như một giá trị 16-bit – chiếm 2 bytes bộ nhớ.
Chú ý rằng hai câu lệnh sau cho ra cùng một kết quả.
printf(“The value is %d”, var);
printf(“The value is %d”, *(&var));
Gán giá trị cho con trỏ
Các giá trị có thể được gán cho biến con trỏ thông qua toán tử &. Câu lệnh gán sẽ là:
ptr_var = &var;
Lúc này địa chỉ của var được lưu trong biến ptr_var. Cũng có thể gán giá trị cho con trỏ thông qua một biến con trỏ khác trỏ đến một phần tử dữ liệu có cùng kiểu.
ptr_var = &var;
ptr_var2 = ptr_var;
Giá trị NULL cũng có thể được gán đến một con trỏ bằng số 0 như sau:
ptr_var = 0;
Các biến cũng có thể được gán giá trị thông qua con trỏ của chúng.
*ptr_var = 10;
sẽ gán 10 cho biến var nếu ptr_var trỏ đến var.
Nói chung, các biểu thức có chứa con trỏ cũng theo cùng qui luật như các biểu thức khác trong C.
Điều quan trọng cần chú ý 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 đó.
Phép toán số học con trỏ
Chỉ phép cộng và trừ là các toán tử có thể thực hiện trên các con trỏ. Ví dụ sau minh họa điều này:
int var, *ptr_var;
ptr_var = &var;
var = 500;
Trong ví dụ trên, chúng ta giả sử rằng var được lưu tại địa chỉ 1000. Sau đó, giá trị 1000 sẽ được lưu vào ptr_var. Vì kiểu số nguyên chiếm 2 bytes, nên sau biểu thức:
ptr_var++ ;
ptr_var sẽ chứa 1002 mà KHÔNG phải là 1001. Điều này có nghĩa là ptr_var bây giờ trỏ đến một số nguyên được lưu tại địa chỉ 1002. Mỗi khi ptr_var đượ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_var 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ị.
Đây là một vài ví dụ.
++ptr_var or ptr_var++ Trỏ đến số nguyên kế tiếp đứng sau var --ptr_var or ptr_var-- Trỏ đến số nguyên đứng 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)++ Sẽ tăng trị var bởi 1
*ptr_var++ Sẽ tác động đến giá trị của số nguyên kế tiếp sau var
Mỗi khi một con trỏ được tăng giá trị, nó sẽ trỏ đến ô nhớ của phần tử kế tiếp. Mỗi khi nó được giảm giá trị, nó sẽ trỏ đến vị trí của phần tử đứng trước nó. Với những con trỏ trỏ tới các ký tự, nó xuất hiện bình thường, bởi vì mỗi ký tự chiếm 1 byte. Tuy nhiên, tất cả những con trỏ khác sẽ tăng hoặc giảm trị tuỳ thuộc vào độ dài kiểu dữ liệu mà chúng trỏ tới.
Như đã thấy trong các ví dụ trên, ngoài các toán tử tăng trị và giảm trị, các số nguyên cũng có thể được cộng vào và trừ ra với con trỏ. Ngoài phép cộng và trừ một con trỏ với một số nguyên, không có một phép toán nào khác có thể thực hiện được trên các con trỏ. Nói rõ hơn, các con trỏ không thể được nhân hoặc chia. Cũng như kiểu float và double không thể được cộng hoặc trừ với con trỏ.
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. ptr_a và ptr_b là hai biến con trỏ trỏ đến các phần tử dữ liệu a và b. Trong trường hợp này, các phép so sánh sau đây là có thể thực hiện:
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)
Tương tự, nếu ptr_begin và ptr_end trỏ đến các phần tử của cùng một mảng thì, ptr_end - ptr_begin
sẽ trả về số bytes cách biệt giữ hai vị trí mà chúng trỏ đến.