Khi bắt đầu với ngôn ngữ lập trình C++, một trong những khái niệm được nhắc đến nhiều nhất là con trỏ (pointer). Vậy con trỏ có vai trò gì trong ngôn ngữ C++ nói riêng và trong lập trình nói chung?
Trong khoa học máy tính, con trỏ là một đối tượng ngôn ngữ lập trình, mà giá trị nó chỉ tới giá trị khác được chứa nơi nào đó trong bộ nhớ máy tính sử dụng địa chỉ bộ nhớ. Một con trỏ tham chiếu đến một vị trí trong bộ nhớ, và lấy giá trị được lưu ở vị trí đó được gọi là tham chiếu ngược con trỏ.
– Wikipedia –
Định nghĩa
Hiểu một cách đơn giản hơn, giả sử bạn A có một ngôi nhà, và ngôi nhà đó được đại diện bởi một biến là B. Khi ta nói con trỏ C trỏ đến vị trí ô nhớ B thì có nghĩa là biến C có giá trị bằng địa chỉ nhà của A. Cú pháp trên được cài đặt trong ngôn ngữ C++ như sau:
int b = 5; // Khai báo biến b có giá trị bằng 5 cout << b; // 5 cout << &b; // 0x7ffde13ffa44 -> địa chỉ của b int* c = &b; // Con trỏ c trỏ tới địa chỉ của b cout << c; // 0x7ffde13ffa44 -> địa chỉ của b cout << *c; // 5 cout << &c; // 0x7ffde13ffc21 -> địa chỉ của c int** d = &c; // Con trỏ d trỏ tới con trỏ c cout << *d; // 0x7ffde13ffc21 -> địa chỉ của c cout << **d; // 5;
Như ví dụ bên trên, biến b khi được cấp phát trong chương trình sẽ có một địa chỉ ô nhớ. Và 1 biến kiểu con trỏ sẽ chứa địa chỉ của 1 ô nhớ nào đó. Con trỏ có thể có nhiều cấp như: con trỏ trỏ đến biến (con trỏ c), con trỏ trỏ đến con trỏ (con trỏ d), con trỏ trỏ đến con trỏ trỏ đến con trỏ, con trỏ trỏ đến con trỏ trỏ đến con trỏ trỏ đến con trỏ, con trỏ trỏ đến con trỏ trỏ đến con trỏ trỏ đến con trỏ trỏ đến con trỏ, …..
Tác dụng của con trỏ
Trong C++, con trỏ được sử dụng nhiều nhất cho mục đích cấp phát động. Kiểu dữ liệu mảng nguyên thủy của C++ là cấp phát tĩnh, do đó để cấp phát động chúng ta cần sử dụng con trỏ. Việc cài đặt cấp phát động bằng con trỏ trong C++ được thực hiện như sau:
int a[5] = {3,4,1,5,6}; // Kiểu cấp phát tĩnh cout << a[3]; // KQ: 5 int *b = new int[5]; // Kiểu cấp phát động b[3] = 5; // Tương đương: *(b+3) = 5; cout << *(b+3); // Tương đương: b[3] = 5; // KQ: 5
Ưu điểm của cấp phát động là ta có thể cấp phát số lượng thay đổi trong khi thực thi chương trình, khác với cấp phát tĩnh phải khai báo trước đó, khi chương trình được biên dịch.
Các trò vui khác
Ngoài ra, con trỏ còn có một số tác dụng khác. Tiêu biểu nhất có thể kể đến chính là thay đổi giá trị của thuộc tính private trong class:
Con trỏ t được gán giá trị bằng địa chỉ của thuộc tính x, do đó khi ta thay đổi giá trị của biến tại địa chỉ mà t đang nắm giữ, nghĩa là ta đang trực tiếp thao tác lên ô nhớ của thuộc tính x. Điều này là không khuyến khích khi thiết kế lớp trong lập trình hướng đối tượng vì nó sẽ phá vỡ tính đóng gói. Đọc thêm về lập trình hướng đối tượng tại đây.
Fact: biến mảng trong C++ thực tế cũng là một con trỏ, địa chỉ của một mảng chính là địa chỉ của ô nhớ đầu tiên trong mảng.
Lưu ý khi dùng con trỏ trong C++
Một trong những điểm cần lưu ý khi sử dụng con trỏ trong C++ là Memory leak. Nguyên nhân là do C++ không hỗ trợ cơ chế tự thu gom bộ nhớ như một vài ngôn ngữ khác (Đọc thêm về Garbage Collection trong Java). Do đó, mỗi một con trỏ sau khi được cấp phát cần phải thu hồi nếu không muốn xảy ra tình trạng memory leak.
int *a = new int; delete a; // Thu hồi
Hi vọng với bài viết ngắn này sẽ giúp bạn nắm được một số điểm cơ bản nhất về con trỏ trong lập trình C++. Nếu có bất kì điều gì cần đóng góp thì để lại ý kiến bên dưới nhé.