Con trỏ hay còn được gọi là Pointer trong C++ là một phần kiến thức khá khó. Tuy nhiên cũng không kém phần thú vị. Nếu bạn nắm chắc cách sử dụng con trỏ trong C++ thì sẽ dễ dàng thực hiện các thao tác cao cấp cùng bộ nhớ. Việc này là vô cùng cần thiết nếu mục tiêu của bạn là trở thành một lập trình viên chuyên nghiệp. Vốn dĩ các thông tin xung quanh con trỏ C++ rất rộng lớn. Vì thế, trong bài viết này, Teky sẽ đề cập nhanh đến các khái niệm cơ bản cho những bạn chưa từng đọc qua về con trỏ C++ trước đây. Hãy cùng bắt đầu thôi.
Khái niệm của con trỏ trong C++
Tìm hiểu kiến trúc máy tính
Để hiểu được khái niệm của con trỏ trong C++, ta cần bắt đầu với cấu trúc của máy tính trước, cụ thể là bộ nhớ, hay còn gọi là RAM. RAM là tên viết tắt của Random Access Memory. RAM được sử dụng như một bộ nhớ tạm thời để xử lý các dữ liệu trong điều kiện được cung cấp điện. Nếu RAM bị ngắt điện, nó sẽ không thể hoạt động được nữa. RAM được cấu tạo nên từ rất nhiều ô nhớ. Mỗi ô có kích thước là 1 byte = 8 bit và sở hữu một địa chỉ duy nhất. Các ô nhớ được đánh số bắt đầu từ 0 trở đi.
Mỗi kiểu dữ liệu lại có kích thước khác nhau. Chính vì thế, không phải kiểu dữ liệu nào cũng nằm gọn trong một ô nhớ được. Ta biết rằng địa chỉ của biến dữ liệu chính là địa chỉ của ô nhớ nó nằm trong. Vậy nếu ví dụ như kiểu int chiếm tới 4 ô nhớ thì địa chỉ của nó là gì? Khi một biến chiếm nhiều ô nhớ, địa chỉ của nó là địa chỉ của ô nhớ đầu tiên trong chuỗi các ô nhớ liền kề nhau.
Khi trình biên dịch code hoạt động, nó sẽ dành riêng một vùng để ghi nhớ các biến. Địa chỉ của biến là địa chỉ của ô nhớ đầu tiên trong chuỗi liền kề. Khi được gọi tên, nó sẽ tự truy xuất đến địa chỉ thích hợp. Các biến khác nhau không nhất thiết phải liền kề nhau.
Cấp bộ nhớ trong C++
Khi ta tiến hành khai báo tên và vùng nhớ cố định cho một biến, nó được gọi là biến tĩnh hay biến được cấp phát tĩnh. Vì nó gắn liền với vùng nhớ cố định nên trong quá trình chương trình được thực thi không thể tác động lên nó được. Tác động ở đây bao gồm xóa đi và cả thay đổi kích thước. Tất cả những điều này chỉ có thể thực hiện khi chương trình kết thúc.
Vì thế, nó gây phiền phức trong khá nhiều trường hợp. Khi một biến trở nên vô dụng hoặc bành trướng quá mức mà ta không thể xóa và chỉnh sửa nó thì sẽ gây nên chiếm dụng bộ nhớ và lãng phí tài nguyên. Giải pháp trong trường hợp này chính là biến được hoặc biến được cấp phát động trong C++.
Biến động là một kiểu dữ liệu đã được định nghĩa. Nó có tên nhưng không được khai báo biến. Biến động sẽ được cấp phát một vùng nhớ trong RAM, có thể sử dụng khi có nguồn điện và ngược lại, không hoạt động khi không có nguồn điện. Biến động sẽ được điều khiển bằng con trỏ trong C++.
Biến con trỏ trong C++
Bản thân một con trỏ trong C++ là một biến, vậy nên nó chứa địa chỉ của ô nhớ đầu tiên trong vùng nhớ. Con trỏ C++ có thể chứa địa chỉ của cả biến tĩnh lẫn biến động. Tuy nhiên, như đã nói ở trên, biến động không có tên, vì thế con trỏ C++ sẽ chịu trách nhiệm quản lý biến động đó. Khi đó ta nói, con trỏ này trỏ đến biến kia hoặc con trỏ này tham chiếu đến vùng nhớ kia. Mỗi con trỏ chứa một địa chỉ khác nhau nên chúng cũng có kích thước khác nhau.
Khi làm việc với con trỏ C++, ta không thể tự ý thay đổi địa chỉ của nó, đây là việc hệ điều hành chịu trách nhiệm. Ngoài ra, không phải vùng nhớ nào con trỏ C++ cũng tham chiếu được. Nó chỉ có thể trỏ đến loại dữ liệu thích hợp mà thôi. Bản chất của con trỏ C++ là quản lý địa chỉ, vì thế ta không thể trỏ nó đến biểu thức hoặc hằng, những đối tượng này vốn không có địa chỉ.
Cách sử dụng con trỏ trong C++
Khai báo con trỏ C++
Để khai báo con trỏ C++, ta thực hiện một công thức giống như các biến bình thường:
<kiểu dữ liệu> * <tên biến>
Trong đó, kiểu dữ liệu có thể là nguyên thủy như int, char, double hoặc các struct, class như vector<string>, queue<pair<int, int>>. Tất cả đều được. Nhưng nếu một con trỏ trỏ vào nhiều biến khác nhau trong những thời điểm khác nhau thì các biến này phải có cùng một kiểu dữ liệu. Phải có kiểu dữ liệu chung giữa các biến thì mới có thể khai báo được. Dấu * trong công thức có nghĩa rằng ta đang báo hiệu hoạt động khai báo con trỏ cho trình biên dịch.
Một số ví dụ thường gặp về kiểu khai báo cho các loại dữ liệu khác nhau:
- Nếu biến có kiểu dữ liệu int thì cú pháp khai báo sẽ là int *p hoặc int* p.
- Nếu biến có kiểu dữ liệu string hoặc queue<char> thì cú pháp khai báo sẽ là string *s hoặc queue<char> *q.
Vậy có trường hợp nào con trỏ trỏ vào con trỏ mà không trỏ vào biến không? Đáp án là có. Con trỏ cũng là một biến, nó được hiểu như một kiểu dữ liệu. Vì thế một con trỏ bất kỳ có thể trỏ vào con trỏ khác nó. Tuy nhiên cách thức thực hiện khá phức tạp và hầu như chỉ được dùng trong các cuộc thi lập trình chuyên nghiệp. Vì thế Teky sẽ không đề cập sâu hơn ở đây.
Gán giá trị cho con trỏ
Sau khi khai báo xong xuôi thì ta cần gán giá trị cho con trỏ trong C++. Bạn cũng cần lưu ý rằng, nếu dùng con trỏ mà không khởi tạo thì giá trị của nó sẽ không có ý nghĩa, hay còn được gọi là giá trị rác. Rất nhiều rủi ro có thể xảy ra nếu chương trình dính giá trị rác, nhất là khi giá trị rác bị trùng với một địa chỉ biến mà bạn đang sử dụng.
Để khởi tạo và khai báo giá trị cho con trỏ, ta tham khảo ví dụ sau:
int *p, value;
value = 5;
p = &value; // khởi tạo giá trị cho con trỏ p là địa chỉ của value
Hoặc bạn cũng có thể khai báo và khởi tạo đồng thời:
int value = 5;
int *p = &value; // khai báo con trỏ p và khởi tạo giá trị cho con trỏ là địa chỉ của value
Còn nếu lỡ khai báo con trỏ rồi nhưng nó vẫn chưa có giá trị thích hợp, ta tạm thời khởi tạo giá trị NULL cho nó theo công thức: int *p_int = NULL;
Truy cập vào biến được con trỏ trỏ tới
Con trỏ được dùng để đặt tên khác cho một biến bất kỳ nào đó. Khi bạn muốn truy cập vào biến p được con trỏ trỏ tới, hãy quan tâm tới *p. *p sẽ có kiểu xuất hiện giống với p. Vì thế cách truy cập p chính là truy cập *p. Điều này được thể hiện rõ trong ví dụ sau:
int *p_int = …; // something which does not matter
string *p_string = …; // something which does not matter
*p_int = 5; (*p_int)++; cout << *p_int << endl;
if (!(*p_string).empty()) for (int i = 0; i < (*p_string).size(); i++) printf(“%c”, (*p_string)[i]);
Tham khảo thêm: Google Firebase là gì? Tại sao bạn nên sử dụng phần mềm này?
Kết luận
Với những kiến thức căn bản vừa được Teky mang đến, hẳn bạn đọc đã có những khái niệm trực quan hơn về con trỏ trong C++. Đây chưa bao giờ là một lĩnh vực dễ khám phá cả. Tuy nhiên nó lại ẩn chứa nhiều điều rất thú vị. Vì thế sẽ không lãng phí thời gian khi bạn quyết định tìm hiểu sâu hơn về con trỏ trong C++. Để có thể thành thảo sử dụng con trỏ C++, lời khuyên của Teky là bạn nên thực hành nhiều lần và ứng dụng thường xuyên vào công việc của mình nếu được. Chúc bạn có thể nhanh chóng làm chủ lĩnh vực này!
The post Con trỏ trong C++ là gì? Các khái niệm cơ bản xung quanh con trỏ appeared first on TEKY - Học viện sáng tạo công nghệ.
source https://teky.edu.vn/blog/con-tro-trong-c-2/