Khái niệm con trỏ (pointer) và cách khai báo biến con trỏ trong C++

Đây là bài 37/43 bài của series môn học Nhập môn lập trình

Trong C++, có một khái niệm rất hay nhưng khá khó hiểu khi mới lập trình là con trỏ (pointer). Để tìm hiểu về con trỏ, các bạn cần nắm vững về bộ nhớ của biến trong lập trình. Các bạn có thể đọc lại bài Hiểu rõ về bộ nhớ của biến trong C++.

1. Khái niệm con  trỏ

Nhớ lại bộ nhớ lưu trữ biến

Bộ nhớ RAM chứa rất nhiều ô nhớ, mỗi ô nhớkích thước 1 byte.

Mỗi ô nhớ có địa chỉ duy nhất và địa chỉ này được đánh số từ 0 trở đi. Nếu CPU 32 bit thì có 2^32 địa chỉ có thể đánh cho các ô nhớ trong RAM.

Địa chỉ của các ô nhớ

Khi khai báo biến, trình biên dịch dành riêng một vùng nhớ với địa chỉ duy nhất để lưu biến. Trình biên dịch có nhiệm vụ liên kết địa chỉ ô nhớ đó với tên biến. Khi gọi tên biến, nó sẽ truy xuất tự động đến ô nhớ đã liên kết với tên biến để lấy dữ liệu. Các bạn phải luôn phân biệt giữa địa chỉ bộ nhớdữ liệu được lưu trong đó.

Địa chỉ của biến là địa chỉ ô nhớ đầu tiên của vùng nhớ mà biến nắm giữ

Có thể lấy địa chỉ của một biến bằng cách sử dụng toán tử &.

int a;
cout << "Address of a: " << &a << endl;
Address of a: 00F6FDFC

Địa chỉ của biến bản chất cũng là một con số thường được biểu diễn ở hệ cơ số 16. Ta có thể sử dụng con trỏ (pointer) để lưu địa chỉ của các biến.

Con trỏ là gì?

Trong ngôn ngữ C++, con trỏ (pointer) là những biến lưu trữ địa chỉ bộ nhớ của những biến khác.

Con trỏ lưu địa chỉ của biến

Trong hình trên, biến var lưu giá trị 5 có địa chỉ là 0x61ff08. Biến pointVarbiến con trỏ, lưu địa chỉ của biến var (trỏ đến vùng nhớ của biến var), tức là nó lưu giá trị 0x61ff08.

2. Khai báo và khởi tạo biến con trỏ

Cú pháp khai báo biến con trỏ

<kiểu dữ liệu> *<tên biến con trỏ>;

Ví dụ:

char *ch1, *ch2;
int *p1, p2;

ch1ch2 là biến con trỏ, trỏ tới vùng nhớ kiểu char (1 byte). p1 là biến con trỏ, trỏ tới vùng nhớ kiểu int (4 bytes), còn p2 là biến kiểu int bình thường.

Khởi tạo biến con trỏ

Một biến bất kỳ phải xác định 2 thứ: địa chỉ của biếngiá trị của biến. Biến con trỏ cũng thế.

Khi mới khai báo, biến con trỏ được đặt ở địa chỉ nào đó (không biết trước), chứa giá trị là một địa chỉ không xác định hoặc địa chỉ 0xCCCCCCCC – là địa chỉ đặc biệt, cho biết con trỏ chưa được khởi tạo. Sử dụng toán tử & để đặt địa chỉ của một biến vào con trỏ.

Cú pháp: <tên biến con trỏ> = &<tên biến>;

Ví dụ:

int a, b;
int *pa, *pb;
pa = &a;
pb = &b;

Con trỏ NULL

Con trỏ NULL là con trỏ lưu địa chỉ 0x00000000. Tức địa chỉ bộ nhớ 0, có ý nghĩa đặc biệt, cho biết con trỏ không trỏ vào đâu cả.

int *p2;//con trỏ chưa khởi tạo, vẫn trỏ đến một vùng nhớ nào đó không xác định
int *p3 = NULL;//con trỏ null không trỏ đến vùng nhớ nào

3. Sử dụng biến con trỏ

Ví dụ có các khai báo:

int a = 5;
int *pa = &a;

Nắm rõ quy tắc sau: *pa và a đều chỉ giá trị của biến a, pa và &a đều chỉ địa chỉ của biến a, &pa là lấy địa chỉ của biến con trỏ pa.

#include <iostream>
using namespace std;

int main() {
	int a = 5;
	int *pa;
	pa = &a;
	//Dia chi
	cout<<"Gia tri cua bien con tro pa la dia chi cua bien a:"<<pa<<endl;
	cout<<"Dia chi cua bien a ma bien con tro pa tro den:"<<&a<<endl;
	//Gia tri
	cout<<"Gia tri vung nho ma bien con tro pa tro den:"<<*pa<<endl;
	cout<<"Gia tri cua bien a ma bien con tro pa tro den:"<<a<<endl;
	//Dia chi bien con tro
	cout<<"Dia chi cua bien con tro pa:"<<&pa<<endl;
	system("pause");
}
Kết quả
Gia tri cua bien con tro pa la dia chi cua bien a:00AFF948
Dia chi cua bien a ma bien con tro pa tro den:00AFF948
Gia tri vung nho ma bien con tro pa tro den:5
Gia tri cua bien a ma bien con tro pa tro den:5
Dia chi cua bien con tro pa:00AFF93C

4. Kích thước của con trỏ

Ví dụ các khai báo con trỏ sau:

char *p1;
int *p2;
float *p3;
double *p4;

Kích thước của các biến con trỏ có khác nhau không? Con trỏ chỉ lưu địa chỉ nên kích thước của mọi con trỏ là như nhau. Kích thước này phụ thuộc vào môi trường hệ thống máy tính:

    • Môi trường MS-DOS 16 bit: 2 bytes
    • Môi trường Windows 32 bit: 4 bytes
    • Môi trường Windows 64 bit: 8 bytes

Chương trình xem kích thước của con trỏ

#include <iostream>
using namespace std;

int main() {
	char *p1;
	int *p2;
	float *p3;
	double *p4;
	cout<<"Size of char type pointer:"<<sizeof(char *)<<" bytes" <<endl;
	cout<<"Size of int type pointer:"<<sizeof(int *)<<" bytes" <<endl;
	cout<<"Size of float type pointer:"<<sizeof(float *)<<" bytes" <<endl;
	cout<<"Size of double type pointer:"<<sizeof(double *)<<" bytes" <<endl;
	system("pause");
}
Kết quả trên Windows 64 bit
Size of char type pointer:8 bytes
Size of int type pointer:8 bytes
Size of float type pointer:8 bytes
Size of double type pointer:8 bytes

5. Một số lưu ý khi sử dụng con trỏ

Khi khởi tạo con trỏ NULL

Chữ NULL phải viết hoa, viết thường null sẽ bị lỗi.

int *p1 = NULL;//đúng
int *p2 = null;//lỗi

Không nên sử dụng con trỏ khi chưa được khởi tạo

Kết quả tính toán có thể sẽ phát sinh những lỗi không lường trước được nếu chưa khởi tạo con trỏ.

int a = 5, *pa;
pa = &a;//được khởi tạo
int *pb;//phải khởi tạo

Sử dụng biến con trỏ sai cách

int var, *varPoint;

// lỗi 
// varPoint là địa chỉ nhưng var không phải địa chỉ
varPoint = var;

// lỗi
// &var là địa chỉ
// *varPoint là giá trị được lưu trữ trong var
*varPoint = &var;

// đúng 
// varPoint là địa chỉ và &var cũng là địa chỉ
varPoint = &var;

 // đúng
// *varPoint và var đều là giá trị
*varPoint = var;

Khi thay đổi giá trị trong vùng nhớ

Xem ví dụ sau:

#include <iostream>
using namespace std;

int main() {
	int *pa;
	int a=5;
	pa=&a;//pa lưu địa chỉ của biến a
	*pa=100;//thay đổi giá trị trong vùng nhớ mà pa lưu trữ
	cout<<"Gia tri cua a:"<<a;//giá trị lưu trong a cũng thay đổi
	system("pause");
}
Kết quả
Gia tri cua a:100

Tên biến chỉ đại diện cho vùng nhớ, có thể truy xuất hoặc thay đổi giá trị trong vùng nhớ đó. Do đó, khi giá trị trong vùng nhớ thay đổi thì giá trị truy xuất được thông qua tên biến cũng thay đổi.

Con trỏ là khái niệm quan trọng khó nhất trong C/C++. Mức độ thành thạo C/C++ được đánh giá qua mức độ sử dụng con trỏ. Do đó, các bạn cố gắng hiểu rõ và luyện tập cách sử dụng con trỏ nhé.

Nếu có bất cứ thắc mắc nào, các bạn cứ comment bên dưới, mình sẽ giải đáp cho các bạn.

5/5 - (2 bình chọn)
Bài trước và bài sau trong môn học<< Kỹ thuật lập trình với mảng cấu trúc và truyền cấu trúc cho hàmSự tương quan giữa con trỏ và mảng trong C++ >>
Chia sẻ trên mạng xã hội:

Để lại một bình luận

Lưu ý:

1) Vui lòng bình luận bằng tiếng Việt có dấu.

2) Khuyến khích sử dụng tên thật và địa chỉ email chính xác.

3) Mọi bình luận trái quy định sẽ bị xóa bỏ.