Bài 13: Con trỏ [Lý Thuyết]
Mục tiêu:
Kết thúc bài học này, bạn có thể:
Ø Hiểu con trỏ là gì, và con trỏ được sử
dụng ở đâu
Ø Biết cách sử dụng biến con trỏ và các toán
tử con trỏ
Ø Gán giá trị cho con trỏ
Ø Hiểu các phép toán số học con trỏ
Ø Hiểu các phép toán so sánh con trỏ
Ø Biết cách truyền tham số con trỏ cho hàm
Ø Hiểu cách sử dụng con trỏ kết hợp với mảng
một chiều
Ø Hiểu cách sử dụng con trỏ kết hợp với mảng
đa chiều
Ø Hiểu cách cấp phát bộ nhớ được thực hiện
như thế nào
Giới thiệu
Con trỏ cung cấp một cách thức truy xuất biến mà không tham chiếu
trực tiếp đến biến. Nó cung cấp cách thức sử dụng địa chỉ. Bài này sẽ đề cập
đến các khái niệm về con trỏ và cách sử dụng chúng trong C.
13.1 Con trỏ là gì?
Một con trỏ là một biến, nó chứa địa chỉ
vùng nhớ của một biến khác, chứ không lưu trữ giá trị của biến đó. Nếu một biến
chứa địa chỉ của một biến khác, thì biến này được gọi là con trỏ đến
biến thứ hai kia. Một con trỏ cung cấp phương thức gián tiếp để truy xuất giá
trị của các phần tử dữ liệu. Xét hai biến var1 và var2, var1 có giá trị 500 và
được lưu tại địa chỉ 1000 trong bộ nhớ. Nếu var2 được khai báo như là một con
trỏ tới biến var1, sự biểu diễn sẽ như sau:
Vị
trí Giá
trị
Tên
Bộ nhớ lưu
trữ biến
1000
500
var1
1001
1002
.
.
1108
1000
var2
Ở đây, var2 chứa giá trị 1000, đó là địa chỉ của biến var1.
Các con trỏ có thể trỏ đến các biến của các kiểu dữ liệu cơ sở
như int, char, hay double hoặc
dữ liệu có cấu trúc như mảng.
13.1.2 Tại sao con trỏ được dùng?
Con trỏ có thể được sử dụng trong một số trường hợp sau:
Ø Để trả về nhiều hơn một giá trị từ một hàm
Ø Thuận tiện hơn trong việc truyền các mảng
và chuỗi từ một hàm đến một hàm khác
Ø Sử dụng con trỏ để làm việc với các phần
tử của mảng thay vì truy xuất trực tiếp vào các phần tử này
Ø Để cấp phát bộ nhớ động và truy xuất vào
vùng nhớ được cấp phát này (dynamic memory allocation)
13.2 Các biến con trỏ
Nếu một biến được sử dụng như một con trỏ, nó phải được khai báo
trước. Câu lệnh khai báo con trỏ bao gồm một kiểu dữ liệu cơ bản, một dấu *, và một tên
biến. Cú pháp tổng quát để
khai báo một biến con trỏ như sau:
type *name;
Ở đó type là một kiểu dữ liệu hợp lệ bất kỳ,
và name là tên của biến con trỏ. Câu lệnh khai báo trên nói
với trình biên dịch là name được sử dụng để lưu địa chỉ của
một biến có kiểu dữ liệu type. Trong câu lệnh khai báo, * xác định
rằng một biến con trỏ đang được khai báo.
Trong ví dụ của var1 và var2 ỏ trên,
vì var2 là một con trỏ giữ địa chỉ của biến var1 có kiểu int,
nó sẽ được khai báo như sau:
int *var2;
Bây giờ, var2 có thể được sử dụng trong một
chương trình để trực tiếp truy xuất giá trị của var1. Nhớ
rằng, var2 không phải có kiểu dữ liệu int nhưng
nó là một con trỏ trỏ đến một biến có kiểu dữ liệu int.
Kiểu dữ liệu cơ sở của con trỏ xác định kiểu của biến mà con trỏ
trỏ đến. Về mặt kỹ thuật, một con trỏ có kiểu bất kỳ có thể trỏ đến bất kỳ vị
trí nào trong bộ nhớ. Tuy nhiên, tất cả các phép toán số học trên con trỏ đều
có liên quan đến kiểu cơ sở của nó, vì vậy khai báo kiểu dữ liệu của con trỏ
một cách rõ ràng là điều rất quan trọng.
13.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ử *, là phần bổ sung của 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, là giá trị của biến mà var2
trỏ đến. Toán tử * có
thể được hiểu là: “giá trị của”.
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>
#include <conio.h>
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);
getch();
}
Kết quả của ví dụ trên như sau: