Tổng quan về Pointers trong C/C++

CongDC
Chim cu chăm code
Published in
5 min readFeb 25, 2020

Pointers là một trong những tính năng mạnh nhất trong ngôn ngữ C/C++, chúng cho phép lập trình viên trực tiếp thao tác bộ nhớ để quản lý bộ nhớ hiệu quả — tài nguyên hiếm nhất của computer — để cho cho hiệu suất tốt nhất. Tuy nhiên “Pointer” cũng là tính năng phức tạp rắc rối nhất trong C/C++.

Trong blog này chúng ta sẽ thảo luận một số vấn đề xoay quanh “Pointer”: Pointer là gì, sử dụng nó như thế nào, một số loại pointer,…

Bắt đầu thôi.

Pointer là gì?

Biến pointer chứa địa chỉ của một biến khác (a memory location).

Sơ đồ minh hoạ mối quan hệ giữa địa chỉ và nội dung bộ nhớ của máy tính; và tên, loại và giá trị của biến được sử dụng bởi các lập trình viên.

Sử dụng Pointer như thế nào?

Khai báo pointer

Khởi tạo Pointer bằng toán tử lấy địa chỉ &

Truy cập giá trị (của biến) được trỏ đến bởi con trỏ

Kiểu của con trỏ

Kiểu của con trỏ là kiểu của giá trị mà nó trỏ đến.

Ví dụ dùng con trỏ

Tên mảng là con trỏ hằng trỏ tới phần tử đầu tiên

Biểu thức và số học Pointer

Một con trỏ có thể là:

a. tăng (++)

b. giảm ( — )

c. một số nguyên có thể được thêm vào một con trỏ (+ hoặc +=)

d. một số nguyên có thể được trừ từ một con trỏ (- hoặc -=)

Số học con trỏ là vô nghĩa trừ khi được thực hiện trên một mảng.

Lưu ý: Con trỏ chứa địa chỉ. Cộng hai địa chỉ không có ý nghĩa, bởi vì không biết nó sẽ trỏ đến cái gì. Trừ hai địa chỉ cho phép bạn tính toán khoảng cách (offset) giữa hai địa chỉ này.

Pointer trỏ tới toàn bộ mảng

Các ứng dụng của Pointer

A. Truyền đối số bằng tham chiếu (Pass arguments by reference)

A1. Truyền đối số bằng tham chiếu để thay đổi đối số được truyền. Ví dụ hoán đổi giá trị hai biến.

A2. Truyền đối số bằng tham chiếu để truyền cấu trúc dữ liệu lớn mà tránh phải copy/clone cả cấu trúc dữ liệu. Ví dụ truyền array, vector, linked list, tree…

B. Để truy cập các phần tử mảng. Trình biên dịch nội bộ sử dụng con trỏ để truy cập các phần tử mảng.

C. Để trả về nhiều giá trị. Ví dụ trả về bình phương và căn bậc hai của một số.

D. Cấp phát bộ nhớ động: Chúng ta có thể sử dụng các con trỏ để cấp phát động bộ nhớ.

E. Để cài đặt các cấu trúc dữ liệu.

Ví dụ như danh sách liên kết, cây, v.v. Chúng ta không thể sử dụng các tham chiếu (references) để triển khai các cấu trúc dữ liệu này vì các tham chiếu được cố định vào một vị trí (địa chỉ). Ví dụ chúng ta không thể duyệt qua danh sách liên kết bằng các tham chiếu.

F. Để thực hiện lập trình cấp hệ thống, nơi các địa chỉ bộ nhớ là hữu ích.

Ví dụ bộ nhớ chia sẻ được sử dụng chung bởi nhiều luồng. Để biết thêm ví dụ, hãy xem IPC thông qua bộ nhớ dùng chung, Lập trình socket trong C / C ++, v.v.

Pointer trỏ tới Pointer (double pointer)

Pointer to Function

Trong C/C++, giống như các biến con trỏ tới dữ liệu thông thường (int *, char *, etc) chúng ta có thể có biến con trỏ tới hàm.

Con trỏ hàm là một biến mà nó lưu giữ địa chỉ của một hàm để sau đó gọi hàm này thông qua con trỏ hàm. Điều này là hữu dụng vì functions đóng gói hành vi chức năng (behavior).

Ví dụ

Dangling, Void , Null and Wild Pointers

Dangling Pointer

Pointer trỏ tới ô nhớ đã được giải phỏng gọi là dangling pointer. Có 3 trường hợp một pointer trở thành dangling pointer.

TH1. Sau khi giải phóng ô nhớ động

TH2. Hàm trả về pointer trỏ tới biến local trong hàm

TH3. Ra ngoài phạm vi của biến được trỏ đến bởi pointer

Void Pointer

Void pointer là một loại con trỏ mà nó có thể trỏ tới bất kỳ loại data gì. Ví dụ nếu gán địa chỉ của biến int cho void pointer thì nó trỏ tới giá trị kiểu int, nếu gán địa chỉ của biến double thì nó trỏ tới giá trị double, v.v.

Hai điểm chú ý về void pointer

  1. Không thể trực tiếp truy cập được giá trị được trỏ bởi void pointer. Để truy cập giá trị đó void pointer phải được đổi thành kiểu con trỏ dữ liệu cụ thể (concrete data type like int *).
  2. Số học con trỏ (Pointer arithmetic) không thực hiện được với void pointer vì kiểu và kích thước của dữ liệu nó trỏ đến chung chung chưa xác định được.

Ví dụ

NULL Pointer

NULL Pointer là pointer chẳng trỏ tới ô nhớ nào cả (tức là nó chẳng chứa địa chỉ ô nhớ nào cả). Trong trường hợp chưa có địa chỉ nào để gán cho con trỏ tá có nên gán giá trị NULL cho nó. Từ C++11 từ khoá ‘nullptr’ được đưa vào để biểu diễn NULL pointer.

Hai điểm phân biệt lưu ý

  1. NULL vs Uninitialized pointer — uninitialized pointer chứa một giá trị chưa xác định (undefined value). null pointer chứa một giá trị đã xác định (defined value), nhưng giá trị này không phải là địa chỉ của bất kỳ ô nhớ nào.
  2. NULL vs Void Pointer — Null pointer là một giá trị (value), trong khi void pointer lại là một kiểu (type).

Ví dụ

Wild (Uninitialized) Pointer

Wild pointer là một pointer chưa được khởi tạo giá trị nào bởi lập trình viên (thậm chí chưa được gán NULL). Nó có thể được gán (bởi compiler/environment) trỏ tới một giá trị rác khác NULL (non-NULL garbage value) mà địa chỉ của nó không phải là một địa chỉ hợp lệ (Invalid address).

Ví dụ

Kết

Cảm ơn bạn đã kiên trì đọc đến đây. Chúng ta đã thao luận các khái niệm cơ bản về pointer như khai báo, sử dụng, các ứng dụng dùng con trỏ đến những khái niệm nâng cao hơn như con trỏ mảng, con trỏ hàm, con trỏ void cũng như các trạng thái giá trị mà con trỏ có thể mang trong vòng đời của nó như wild/uninitialized pointer, NULL pointer, hoặc dangling pointer.

Hy vọng bài blog này hữu ích làm rõ bản chất về Pointer trong C/C++. Có điều gì chưa chính xác hoặc có bất kỳ thắc mắc hay điều gì quan trọng xung quanh pointer chưa được đề cập các bạn cứ comment chúng ta cùng thảo luận nhé. Lần nữa xin cảm ơn.

--

--